diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 1daf926e..dc97e675 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -32,7 +32,7 @@ if(NOT HAVE_STRTOUMAX) add_sources_from_current_dir(utils utils/strtoumax.c) endif() add_sources_from_current_dir(eventloop - cliloop.c handle-io.c) + cliloop.c handle-wait.c) add_sources_from_current_dir(console select-cli.c nohelp.c console.c) add_sources_from_current_dir(settings @@ -53,6 +53,14 @@ add_sources_from_current_dir(guiterminal dialog.c controls.c config.c printing.c jump-list.c sizetip.c) add_dependencies(guiterminal generated_licence_h) # dialog.c uses licence.h +# This object awkwardly needs to live in the network library as well +# as the eventloop library, in case it didn't get pulled in from the +# latter before handle-socket.c needed it. +add_library(handle-io OBJECT + handle-io.c) +target_sources(eventloop PRIVATE $) +target_sources(network PRIVATE $) + add_library(guimisc STATIC select-gui.c) diff --git a/windows/cliloop.c b/windows/cliloop.c index 26a4d3aa..eced54ca 100644 --- a/windows/cliloop.c +++ b/windows/cliloop.c @@ -8,8 +8,6 @@ void cli_main_loop(cliloop_pre_t pre, cliloop_post_t post, void *ctx) now = GETTICKCOUNT(); while (true) { - int nhandles; - HANDLE *handles; DWORD n; DWORD ticks; @@ -34,25 +32,25 @@ void cli_main_loop(cliloop_pre_t pre, cliloop_post_t post, void *ctx) * get WAIT_TIMEOUT */ } - handles = handle_get_events(&nhandles); + HandleWaitList *hwl = get_handle_wait_list(); size_t winselcli_index = -(size_t)1; - size_t extra_base = nhandles; + size_t extra_base = hwl->nhandles; if (winselcli_event != INVALID_HANDLE_VALUE) { + assert(extra_base < MAXIMUM_WAIT_OBJECTS); winselcli_index = extra_base++; - handles = sresize(handles, extra_base, HANDLE); - handles[winselcli_index] = winselcli_event; + hwl->handles[winselcli_index] = winselcli_event; } size_t total_handles = extra_base + n_extra_handles; - handles = sresize(handles, total_handles, HANDLE); + assert(total_handles < MAXIMUM_WAIT_OBJECTS); for (size_t i = 0; i < n_extra_handles; i++) - handles[extra_base + i] = extra_handles[i]; + hwl->handles[extra_base + i] = extra_handles[i]; - n = WaitForMultipleObjects(total_handles, handles, false, ticks); + n = WaitForMultipleObjects(total_handles, hwl->handles, false, ticks); size_t extra_handle_index = n_extra_handles; - if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { - handle_got_event(handles[n - WAIT_OBJECT_0]); + if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)hwl->nhandles) { + handle_wait_activate(hwl, n - WAIT_OBJECT_0); } else if (winselcli_event != INVALID_HANDLE_VALUE && n == WAIT_OBJECT_0 + winselcli_index) { WSANETWORKEVENTS things; @@ -122,7 +120,7 @@ void cli_main_loop(cliloop_pre_t pre, cliloop_post_t post, void *ctx) now = GETTICKCOUNT(); } - sfree(handles); + handle_wait_list_free(hwl); if (!post(ctx, extra_handle_index)) break; diff --git a/windows/conpty.c b/windows/conpty.c index 4f6f130f..843d6725 100644 --- a/windows/conpty.c +++ b/windows/conpty.c @@ -15,7 +15,8 @@ typedef struct ConPTY ConPTY; struct ConPTY { HPCON pseudoconsole; HANDLE outpipe, inpipe, hprocess; - struct handle *out, *in, *subprocess; + struct handle *out, *in; + HandleWait *subprocess; bool exited; DWORD exitstatus; Seat *seat; @@ -43,7 +44,7 @@ static void conpty_terminate(ConPTY *conpty) conpty->inpipe = INVALID_HANDLE_VALUE; } if (conpty->subprocess) { - handle_free(conpty->subprocess); + delete_handle_wait(conpty->subprocess); conpty->subprocess = NULL; conpty->hprocess = INVALID_HANDLE_VALUE; } @@ -65,7 +66,7 @@ static void conpty_process_wait_callback(void *vctx) * We can stop waiting for the process now. */ if (conpty->subprocess) { - handle_free(conpty->subprocess); + delete_handle_wait(conpty->subprocess); conpty->subprocess = NULL; conpty->hprocess = INVALID_HANDLE_VALUE; } @@ -238,7 +239,7 @@ static char *conpty_init(const BackendVtable *vt, Seat *seat, conpty->inpipe = out_r; conpty->in = handle_input_new(out_r, conpty_gotdata, conpty, 0); out_r = INVALID_HANDLE_VALUE; - conpty->subprocess = handle_add_foreign_event( + conpty->subprocess = add_handle_wait( pi.hProcess, conpty_process_wait_callback, conpty); conpty->hprocess = pi.hProcess; CloseHandle(pi.hThread); diff --git a/windows/handle-io.c b/windows/handle-io.c index 085f30a0..ecb48577 100644 --- a/windows/handle-io.c +++ b/windows/handle-io.c @@ -37,6 +37,12 @@ * Generic definitions. */ +typedef struct handle_list_node handle_list_node; +struct handle_list_node { + handle_list_node *next, *prev; +}; +static void add_to_ready_list(handle_list_node *node); + /* * Maximum amount of backlog we will allow to build up on an input * handle before we stop reading from it. @@ -56,7 +62,7 @@ struct handle_generic { * thread. */ HANDLE h; /* the handle itself */ - HANDLE ev_to_main; /* event used to signal main thread */ + handle_list_node ready_node; /* for linking on to the ready list */ HANDLE ev_from_main; /* event used to signal back to us */ bool moribund; /* are we going to kill this soon? */ bool done; /* request subthread to terminate */ @@ -65,7 +71,7 @@ struct handle_generic { void *privdata; /* for client to remember who they are */ }; -typedef enum { HT_INPUT, HT_OUTPUT, HT_FOREIGN } HandleType; +typedef enum { HT_INPUT, HT_OUTPUT } HandleType; /* ---------------------------------------------------------------------- * Input threads. @@ -79,7 +85,7 @@ struct handle_input { * Copy of the handle_generic structure. */ HANDLE h; /* the handle itself */ - HANDLE ev_to_main; /* event used to signal main thread */ + handle_list_node ready_node; /* for linking on to the ready list */ HANDLE ev_from_main; /* event used to signal back to us */ bool moribund; /* are we going to kill this soon? */ bool done; /* request subthread to terminate */ @@ -93,7 +99,7 @@ struct handle_input { int flags; /* - * Data set by the input thread before signalling ev_to_main, + * Data set by the input thread before marking the handle ready, * and read by the main thread after receiving that signal. */ char buffer[4096]; /* the data read from the handle */ @@ -176,7 +182,7 @@ static DWORD WINAPI handle_input_threadfunc(void *param) */ finished = (ctx->len == 0); - SetEvent(ctx->ev_to_main); + add_to_ready_list(&ctx->ready_node); if (finished) break; @@ -189,7 +195,7 @@ static DWORD WINAPI handle_input_threadfunc(void *param) * not touch ctx at all, because the main thread might * have freed it. */ - SetEvent(ctx->ev_to_main); + add_to_ready_list(&ctx->ready_node); break; } } @@ -240,7 +246,7 @@ struct handle_output { * Copy of the handle_generic structure. */ HANDLE h; /* the handle itself */ - HANDLE ev_to_main; /* event used to signal main thread */ + handle_list_node ready_node; /* for linking on to the ready list */ HANDLE ev_from_main; /* event used to signal back to us */ bool moribund; /* are we going to kill this soon? */ bool done; /* request subthread to terminate */ @@ -261,8 +267,8 @@ struct handle_output { DWORD len; /* how much data there is */ /* - * Data set by the input thread before signalling ev_to_main, - * and read by the main thread after receiving that signal. + * Data set by the input thread before marking this handle as + * ready, and read by the main thread after receiving that signal. */ DWORD lenwritten; /* how much data we actually wrote */ int writeerr; /* return value from WriteFile */ @@ -303,7 +309,7 @@ static DWORD WINAPI handle_output_threadfunc(void *param) * not touch ctx at all, because the main thread might * have freed it. */ - SetEvent(ctx->ev_to_main); + add_to_ready_list(&ctx->ready_node); break; } if (povl) { @@ -326,7 +332,7 @@ static DWORD WINAPI handle_output_threadfunc(void *param) ctx->writeerr = 0; } - SetEvent(ctx->ev_to_main); + add_to_ready_list(&ctx->ready_node); if (!writeret) { /* * The write operation has suffered an error. Telling that @@ -361,33 +367,6 @@ static void handle_try_output(struct handle_output *ctx) } } -/* ---------------------------------------------------------------------- - * 'Foreign events'. These are handle structures which just contain a - * single event object passed to us by another module such as - * winnps.c, so that they can make use of our handle_get_events / - * handle_got_event mechanism for communicating with application main - * loops. - */ -struct handle_foreign { - /* - * Copy of the handle_generic structure. - */ - HANDLE h; /* the handle itself */ - HANDLE ev_to_main; /* event used to signal main thread */ - HANDLE ev_from_main; /* event used to signal back to us */ - bool moribund; /* are we going to kill this soon? */ - bool done; /* request subthread to terminate */ - bool defunct; /* has the subthread already gone? */ - bool busy; /* operation currently in progress? */ - void *privdata; /* for client to remember who they are */ - - /* - * Our own data, just consisting of knowledge of who to call back. - */ - void (*callback)(void *); - void *ctx; -}; - /* ---------------------------------------------------------------------- * Unified code handling both input and output threads. */ @@ -398,36 +377,91 @@ struct handle { struct handle_generic g; struct handle_input i; struct handle_output o; - struct handle_foreign f; } u; }; -static tree234 *handles_by_evtomain; +/* + * Linked list storing the current list of handles ready to have + * something done to them by the main thread. + */ +static handle_list_node ready_head[1]; +static CRITICAL_SECTION ready_critsec[1]; -static int handle_cmp_evtomain(void *av, void *bv) +/* + * Event object used by all subthreads to signal that they've just put + * something on the ready list, i.e. that the ready list is non-empty. + */ +static HANDLE ready_event = INVALID_HANDLE_VALUE; + +static void add_to_ready_list(handle_list_node *node) { - struct handle *a = (struct handle *)av; - struct handle *b = (struct handle *)bv; - - if ((uintptr_t)a->u.g.ev_to_main < (uintptr_t)b->u.g.ev_to_main) - return -1; - else if ((uintptr_t)a->u.g.ev_to_main > (uintptr_t)b->u.g.ev_to_main) - return +1; - else - return 0; + /* + * Called from subthreads, when their handle has done something + * that they need the main thread to respond to. We append the + * given list node to the end of the ready list, and set + * ready_event to signal to the main thread that the ready list is + * now non-empty. + */ + EnterCriticalSection(ready_critsec); + node->next = ready_head; + node->prev = ready_head->prev; + node->next->prev = node->prev->next = node; + SetEvent(ready_event); + LeaveCriticalSection(ready_critsec); } -static int handle_find_evtomain(void *av, void *bv) +static void remove_from_ready_list(handle_list_node *node) { - HANDLE *a = (HANDLE *)av; - struct handle *b = (struct handle *)bv; + /* + * Called from the main thread, just before destroying a 'struct + * handle' completely: as a precaution, we make absolutely sure + * it's not linked on the ready list, just in case somehow it + * still was. + */ + EnterCriticalSection(ready_critsec); + node->next->prev = node->prev; + node->prev->next = node->next; + node->next = node->prev = node; + LeaveCriticalSection(ready_critsec); +} - if ((uintptr_t)*a < (uintptr_t)b->u.g.ev_to_main) - return -1; - else if ((uintptr_t)*a > (uintptr_t)b->u.g.ev_to_main) - return +1; - else - return 0; +static void handle_ready(struct handle *h); /* process one handle (below) */ + +static void handle_ready_callback(void *vctx) +{ + /* + * Called when the main thread detects ready_event, indicating + * that at least one handle is on the ready list. We empty the + * whole list and process the handles one by one. + * + * It's possible that other handles may be destroyed, and hence + * taken _off_ the ready list, during this processing. That + * shouldn't cause a deadlock, because according to the API docs, + * it's safe to call EnterCriticalSection twice in the same thread + * - the second call will return immediately because that thread + * already owns the critsec. (And then it takes two calls to + * LeaveCriticalSection to release it again, which is just what we + * want here.) + */ + EnterCriticalSection(ready_critsec); + while (ready_head->next != ready_head) { + handle_list_node *node = ready_head->next; + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = node->prev = node; + handle_ready(container_of(node, struct handle, u.g.ready_node)); + } + LeaveCriticalSection(ready_critsec); +} + +static inline void ensure_ready_event_setup(void) +{ + if (ready_event == INVALID_HANDLE_VALUE) { + ready_head->prev = ready_head->next = ready_head; + InitializeCriticalSection(ready_critsec); + ready_event = CreateEvent(NULL, false, false, NULL); + add_handle_wait(ready_event, handle_ready_callback, NULL); + } } struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata, @@ -438,7 +472,6 @@ struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata, h->type = HT_INPUT; h->u.i.h = handle; - h->u.i.ev_to_main = CreateEvent(NULL, false, false, NULL); h->u.i.ev_from_main = CreateEvent(NULL, false, false, NULL); h->u.i.gotdata = gotdata; h->u.i.defunct = false; @@ -447,10 +480,7 @@ struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata, h->u.i.privdata = privdata; h->u.i.flags = flags; - if (!handles_by_evtomain) - handles_by_evtomain = newtree234(handle_cmp_evtomain); - add234(handles_by_evtomain, h); - + ensure_ready_event_setup(); CreateThread(NULL, 0, handle_input_threadfunc, &h->u.i, 0, &in_threadid); h->u.i.busy = true; @@ -466,7 +496,6 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata, h->type = HT_OUTPUT; h->u.o.h = handle; - h->u.o.ev_to_main = CreateEvent(NULL, false, false, NULL); h->u.o.ev_from_main = CreateEvent(NULL, false, false, NULL); h->u.o.busy = false; h->u.o.defunct = false; @@ -478,40 +507,13 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata, h->u.o.sentdata = sentdata; h->u.o.flags = flags; - if (!handles_by_evtomain) - handles_by_evtomain = newtree234(handle_cmp_evtomain); - add234(handles_by_evtomain, h); - + ensure_ready_event_setup(); CreateThread(NULL, 0, handle_output_threadfunc, &h->u.o, 0, &out_threadid); return h; } -struct handle *handle_add_foreign_event(HANDLE event, - void (*callback)(void *), void *ctx) -{ - struct handle *h = snew(struct handle); - - h->type = HT_FOREIGN; - h->u.f.h = INVALID_HANDLE_VALUE; - h->u.f.ev_to_main = event; - h->u.f.ev_from_main = INVALID_HANDLE_VALUE; - h->u.f.defunct = true; /* we have no thread in the first place */ - h->u.f.moribund = false; - h->u.f.done = false; - h->u.f.privdata = NULL; - h->u.f.callback = callback; - h->u.f.ctx = ctx; - h->u.f.busy = true; - - if (!handles_by_evtomain) - handles_by_evtomain = newtree234(handle_cmp_evtomain); - add234(handles_by_evtomain, h); - - return h; -} - size_t handle_write(struct handle *h, const void *data, size_t len) { assert(h->type == HT_OUTPUT); @@ -537,46 +539,19 @@ void handle_write_eof(struct handle *h) } } -HANDLE *handle_get_events(int *nevents) -{ - HANDLE *ret; - struct handle *h; - int i; - size_t n, size; - - /* - * Go through our tree counting the handle objects currently - * engaged in useful activity. - */ - ret = NULL; - n = size = 0; - if (handles_by_evtomain) { - for (i = 0; (h = index234(handles_by_evtomain, i)) != NULL; i++) { - if (h->u.g.busy) { - sgrowarray(ret, size, n); - ret[n++] = h->u.g.ev_to_main; - } - } - } - - *nevents = n; - return ret; -} - static void handle_destroy(struct handle *h) { if (h->type == HT_OUTPUT) bufchain_clear(&h->u.o.queued_data); CloseHandle(h->u.g.ev_from_main); - CloseHandle(h->u.g.ev_to_main); - del234(handles_by_evtomain, h); + remove_from_ready_list(&h->u.g.ready_node); sfree(h); } void handle_free(struct handle *h) { assert(h && !h->u.g.moribund); - if (h->u.g.busy && h->type != HT_FOREIGN) { + if (h->u.g.busy) { /* * If the handle is currently busy, we cannot immediately free * it, because its subthread is in the middle of something. @@ -608,24 +583,8 @@ void handle_free(struct handle *h) } } -void handle_got_event(HANDLE event) +static void handle_ready(struct handle *h) { - struct handle *h; - - assert(handles_by_evtomain); - h = find234(handles_by_evtomain, &event, handle_find_evtomain); - if (!h) { - /* - * This isn't an error condition. If two or more event - * objects were signalled during the same select operation, - * and processing of the first caused the second handle to - * be closed, then it will sometimes happen that we receive - * an event notification here for a handle which is already - * deceased. In that situation we simply do nothing. - */ - return; - } - if (h->u.g.moribund) { /* * A moribund handle is one which we have either already @@ -689,11 +648,6 @@ void handle_got_event(HANDLE event) handle_try_output(&h->u.o); } break; - - case HT_FOREIGN: - /* Just call the callback. */ - h->u.f.callback(h->u.f.ctx); - break; } } diff --git a/windows/handle-wait.c b/windows/handle-wait.c new file mode 100644 index 00000000..47479aa0 --- /dev/null +++ b/windows/handle-wait.c @@ -0,0 +1,144 @@ +/* + * handle-wait.c: Manage a collection of HANDLEs to wait for (in a + * WaitFor{Single,Multiple}Objects sense), each with a callback to be + * called when it's activated. Tracks the list, and provides an API to + * event loops that let them get a list of things to wait for and a + * way to call back to here when one of them does something. + */ + +/* + * TODO: currently this system can't cope with more than + * MAXIMUM_WAIT_OBJECTS (= 64) handles at a time. It enforces that by + * assertion, so we'll at least find out if that assumption is ever + * violated. + * + * It should be OK for the moment. As of 2021-05-24, the only uses of + * this system are by the ConPTY backend (just once, to watch for its + * subprocess terminating); by Pageant (for the event that the + * WM_COPYDATA subthread uses to signal the main thread); and by + * named-pipe-server.c (once per named-pipe server, of which there is + * one in Pageant and one in connection-sharing upstreams). So the + * total number of handles has a pretty small upper bound. + * + * But sooner or later, I'm sure we'll find a reason why we really + * need to watch a squillion handles at once. When that happens, I + * can't see any alternative to setting up some kind of tree of + * subthreads in this module, each one condensing 64 of our handles + * into one, by doing its own WaitForMultipleObjects and setting an + * event object to indicate that one of them did something. It'll be + * horribly ugly. + */ + +#include "putty.h" + +struct HandleWait { + HANDLE handle; + handle_wait_callback_fn_t callback; + void *callback_ctx; + + int index; /* sort key for tree234 */ +}; + +struct HandleWaitListInner { + HandleWait *hws[MAXIMUM_WAIT_OBJECTS]; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + + struct HandleWaitList hwl; +}; + +static int handlewait_cmp(void *av, void *bv) +{ + HandleWait *a = (HandleWait *)av, *b = (HandleWait *)bv; + if (a->index < b->index) + return -1; + if (a->index > b->index) + return +1; + return 0; +} + +static tree234 *handlewaits_tree_real; + +static inline tree234 *ensure_handlewaits_tree_exists(void) +{ + if (!handlewaits_tree_real) + handlewaits_tree_real = newtree234(handlewait_cmp); + return handlewaits_tree_real; +} + +static int allocate_index(void) +{ + tree234 *t = ensure_handlewaits_tree_exists(); + search234_state st[1]; + + search234_start(st, t); + while (st->element) { + HandleWait *hw = (HandleWait *)st->element; + if (st->index < hw->index) { + /* There are unused index slots to the left of this element */ + search234_step(st, -1); + } else { + assert(st->index == hw->index); + search234_step(st, +1); + } + } + + return st->index; +} + +HandleWait *add_handle_wait(HANDLE h, handle_wait_callback_fn_t callback, + void *callback_ctx) +{ + HandleWait *hw = snew(HandleWait); + hw->handle = h; + hw->callback = callback; + hw->callback_ctx = callback_ctx; + + tree234 *t = ensure_handlewaits_tree_exists(); + hw->index = allocate_index(); + HandleWait *added = add234(t, hw); + assert(added == hw); + + return hw; +} + +void delete_handle_wait(HandleWait *hw) +{ + tree234 *t = ensure_handlewaits_tree_exists(); + HandleWait *deleted = del234(t, hw); + assert(deleted == hw); + sfree(hw); +} + +HandleWaitList *get_handle_wait_list(void) +{ + tree234 *t = ensure_handlewaits_tree_exists(); + struct HandleWaitListInner *hwli = snew(struct HandleWaitListInner); + size_t n = 0; + HandleWait *hw; + for (int i = 0; (hw = index234(t, i)) != NULL; i++) { + assert(n < MAXIMUM_WAIT_OBJECTS); + hwli->hws[n] = hw; + hwli->hwl.handles[n] = hw->handle; + n++; + } + hwli->hwl.nhandles = n; + return &hwli->hwl; +} + +void handle_wait_activate(HandleWaitList *hwl, int index) +{ + struct HandleWaitListInner *hwli = + container_of(hwl, struct HandleWaitListInner, hwl); + tree234 *t = ensure_handlewaits_tree_exists(); + assert(0 <= index); + assert(index < hwli->hwl.nhandles); + HandleWait *hw = hwli->hws[index]; + hw->callback(hw->callback_ctx); +} + +void handle_wait_list_free(HandleWaitList *hwl) +{ + struct HandleWaitListInner *hwli = + container_of(hwl, struct HandleWaitListInner, hwl); + sfree(hwli); +} diff --git a/windows/named-pipe-server.c b/windows/named-pipe-server.c index cdda500d..a272cdad 100644 --- a/windows/named-pipe-server.c +++ b/windows/named-pipe-server.c @@ -22,7 +22,7 @@ typedef struct NamedPipeServerSocket { /* The current named pipe object + attempt to connect to it */ HANDLE pipehandle; OVERLAPPED connect_ovl; - struct handle *callback_handle; /* winhandl.c's reference */ + HandleWait *callback_handle; /* handle-wait.c's reference */ /* PuTTY Socket machinery */ Plug *plug; @@ -45,7 +45,7 @@ static void sk_namedpipeserver_close(Socket *s) NamedPipeServerSocket *ps = container_of(s, NamedPipeServerSocket, sock); if (ps->callback_handle) - handle_free(ps->callback_handle); + delete_handle_wait(ps->callback_handle); CloseHandle(ps->pipehandle); CloseHandle(ps->connect_ovl.hEvent); sfree(ps->error); @@ -226,9 +226,8 @@ Socket *new_named_pipe_listener(const char *pipename, Plug *plug) memset(&ret->connect_ovl, 0, sizeof(ret->connect_ovl)); ret->connect_ovl.hEvent = CreateEvent(NULL, true, false, NULL); - ret->callback_handle = - handle_add_foreign_event(ret->connect_ovl.hEvent, - named_pipe_connect_callback, ret); + ret->callback_handle = add_handle_wait( + ret->connect_ovl.hEvent, named_pipe_connect_callback, ret); named_pipe_accept_loop(ret, false); cleanup: diff --git a/windows/pageant.c b/windows/pageant.c index e7023945..c82c18bc 100644 --- a/windows/pageant.c +++ b/windows/pageant.c @@ -1633,7 +1633,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) wmct.ev_reply_ready = CreateEvent(NULL, false, false, NULL); CreateThread(NULL, 0, wm_copydata_threadfunc, &inst, 0, &wm_copydata_threadid); - handle_add_foreign_event(wmct.ev_msg_ready, wm_copydata_got_msg, NULL); + add_handle_wait(wmct.ev_msg_ready, wm_copydata_got_msg, NULL); if (show_keylist_on_startup) create_keylist_window(); @@ -1642,19 +1642,16 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) * Main message loop. */ while (true) { - HANDLE *handles; - int nhandles, n; + int n; - handles = handle_get_events(&nhandles); + HandleWaitList *hwl = get_handle_wait_list(); - n = MsgWaitForMultipleObjects(nhandles, handles, false, + n = MsgWaitForMultipleObjects(hwl->nhandles, hwl->handles, false, INFINITE, QS_ALLINPUT); - if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { - handle_got_event(handles[n - WAIT_OBJECT_0]); - sfree(handles); - } else - sfree(handles); + if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)hwl->nhandles) + handle_wait_activate(hwl, n - WAIT_OBJECT_0); + handle_wait_list_free(hwl); while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) diff --git a/windows/platform.h b/windows/platform.h index a03d9d63..f549896d 100644 --- a/windows/platform.h +++ b/windows/platform.h @@ -621,14 +621,10 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata, void *privdata, int flags); size_t handle_write(struct handle *h, const void *data, size_t len); void handle_write_eof(struct handle *h); -HANDLE *handle_get_events(int *nevents); void handle_free(struct handle *h); -void handle_got_event(HANDLE event); void handle_unthrottle(struct handle *h, size_t backlog); size_t handle_backlog(struct handle *h); void *handle_get_privdata(struct handle *h); -struct handle *handle_add_foreign_event(HANDLE event, - void (*callback)(void *), void *ctx); /* Analogue of stdio_sink in marshal.h, for a Windows handle */ struct handle_sink { struct handle *h; @@ -636,6 +632,23 @@ struct handle_sink { }; void handle_sink_init(handle_sink *sink, struct handle *h); +/* + * Exports from handle-wait.c. + */ +typedef struct HandleWait HandleWait; +typedef void (*handle_wait_callback_fn_t)(void *); +HandleWait *add_handle_wait(HANDLE h, handle_wait_callback_fn_t callback, + void *callback_ctx); +void delete_handle_wait(HandleWait *hw); + +typedef struct HandleWaitList { + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + int nhandles; +} HandleWaitList; +HandleWaitList *get_handle_wait_list(void); +void handle_wait_activate(HandleWaitList *hwl, int index); +void handle_wait_list_free(HandleWaitList *hwl); + /* * Exports from winpgntc.c. */ diff --git a/windows/window.c b/windows/window.c index c644422c..36284927 100644 --- a/windows/window.c +++ b/windows/window.c @@ -721,8 +721,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) UpdateWindow(wgs.term_hwnd); while (1) { - HANDLE *handles; - int nhandles, n; + int n; DWORD timeout; if (toplevel_callback_pending() || @@ -751,16 +750,14 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) term_set_focus(term, GetForegroundWindow() == wgs.term_hwnd); } - handles = handle_get_events(&nhandles); + HandleWaitList *hwl = get_handle_wait_list(); - n = MsgWaitForMultipleObjects(nhandles, handles, false, + n = MsgWaitForMultipleObjects(hwl->nhandles, hwl->handles, false, timeout, QS_ALLINPUT); - if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { - handle_got_event(handles[n - WAIT_OBJECT_0]); - sfree(handles); - } else - sfree(handles); + if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)hwl->nhandles) + handle_wait_activate(hwl, n - WAIT_OBJECT_0); + handle_wait_list_free(hwl); while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT)