/* * 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); 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); }