1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 01:18:00 +00:00

Reorganise Windows HANDLE management.

Before commit 6e69223dc2, Pageant would stop working after a
certain number of PuTTYs were active at the same time. (At most about
60, but maybe fewer - see below.)

This was because of two separate bugs. The easy one, fixed in
6e69223dc2 itself, was that PuTTY left each named-pipe connection
to Pageant open for the rest of its lifetime. So the real problem was
that Pageant had too many active connections at once. (And since a
given PuTTY might make multiple connections during userauth - one to
list keys, and maybe another to actually make a signature - that was
why the number of _PuTTYs_ might vary.)

It was clearly a bug that PuTTY was leaving connections to Pageant
needlessly open. But it was _also_ a bug that Pageant couldn't handle
more than about 60 at once. In this commit, I fix that secondary bug.

The cause of the bug is that the WaitForMultipleObjects function
family in the Windows API have a limit on the number of HANDLE objects
they can select between. The limit is MAXIMUM_WAIT_OBJECTS, defined to
be 64. And handle-io.c was using a separate event object for each I/O
subthread to communicate back to the main thread, so as soon as all
those event objects (plus a handful of other HANDLEs) added up to more
than 64, we'd start passing an overlarge handle array to
WaitForMultipleObjects, and it would start not doing what we wanted.

To fix this, I've reorganised handle-io.c so that all its subthreads
share just _one_ event object to signal readiness back to the main
thread. There's now a linked list of 'struct handle' objects that are
ready to be processed, protected by a CRITICAL_SECTION. Each subthread
signals readiness by adding itself to the linked list, and setting the
event object to indicate that the list is now non-empty. When the main
thread receives the event, it iterates over the whole list processing
all the ready handles.

(Each 'struct handle' still has a separate event object for the main
thread to use to communicate _to_ the subthread. That's OK, because no
thread is ever waiting on all those events at once: each subthread
only waits on its own.)

The previous HT_FOREIGN system didn't really fit into this framework.
So I've moved it out into its own system. There's now a handle-wait.c
which deals with the relatively simple job of managing a list of
handles that need to be waited for, each with a callback function;
that's what communicates a list of HANDLEs to event loops, and
receives the notification when the event loop notices that one of them
has done something. And handle-io.c is now just one client of
handle-wait.c, providing a single HANDLE to the event loop, and
dealing internally with everything that needs to be done when that
handle fires.

The new top-level handle-wait.c system *still* can't deal with more
than MAXIMUM_WAIT_OBJECTS. At the moment, I'm reasonably convinced it
doesn't need to: the only kind of HANDLE that any of our tools could
previously have needed to wait on more than one of was the one in
handle-io.c that I've just removed. But I've left some assertions and
a TODO comment in there just in case we need to change that in future.
This commit is contained in:
Simon Tatham 2021-05-24 13:06:10 +01:00
parent 02aca17f36
commit 17c57e1078
9 changed files with 300 additions and 189 deletions

View File

@ -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_OBJECTS:handle-io>)
target_sources(network PRIVATE $<TARGET_OBJECTS:handle-io>)
add_library(guimisc STATIC
select-gui.c)

View File

@ -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;

View File

@ -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);

View File

@ -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;
}
}

144
windows/handle-wait.c Normal file
View File

@ -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);
}

View File

@ -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:

View File

@ -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)

View File

@ -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.
*/

View File

@ -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)