From 75c79e318fffc6db076861856c1320fcea849bf2 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 17 Aug 2013 16:06:08 +0000 Subject: [PATCH] Add a general way to request an immediate top-level callback. This is a little like schedule_timer, in that the callback you provide will be run from the top-level message loop of whatever application you're in; but unlike the timer mechanism, it will happen _immediately_. The aim is to provide a general way to avoid re-entrance of code, in cases where just _doing_ the thing you want done is liable to trigger a confusing recursive call to the function in which you came to the decision to do it; instead, you just request a top-level callback at the message loop's earliest convenience, and do it then. [originally from svn r10019] --- Recipe | 4 +-- callback.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++ putty.h | 25 +++++++++++++++++ unix/gtkwin.c | 22 +++++++++++++++ unix/uxplink.c | 2 ++ unix/uxsftp.c | 2 ++ windows/window.c | 9 +++--- windows/winplink.c | 2 ++ windows/winsftp.c | 2 ++ 9 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 callback.c diff --git a/Recipe b/Recipe index 3b65c83b..dee4da8f 100644 --- a/Recipe +++ b/Recipe @@ -288,7 +288,7 @@ GUITERM = TERMINAL window windlg winctrls sizetip winucs winprint + winutils wincfg sercfg winhelp winjump # Same thing on Unix. -UXTERM = TERMINAL uxcfg sercfg uxucs uxprint timing +UXTERM = TERMINAL uxcfg sercfg uxucs uxprint timing callback GTKTERM = UXTERM gtkwin gtkcfg gtkdlg gtkfont gtkcols xkeysym OSXTERM = UXTERM osxwin osxdlg osxctrls @@ -308,7 +308,7 @@ SFTP = sftp int64 logging # Miscellaneous objects appearing in all the network utilities (not # Pageant or PuTTYgen). -MISC = timing misc version settings tree234 proxy conf +MISC = timing callback misc version settings tree234 proxy conf WINMISC = MISC winstore winnet winhandl cmdline windefs winmisc winproxy + wintime UXMISC = MISC uxstore uxsel uxnet cmdline uxmisc uxproxy time diff --git a/callback.c b/callback.c new file mode 100644 index 00000000..e62fcd7d --- /dev/null +++ b/callback.c @@ -0,0 +1,68 @@ +/* + * Facility for queueing callback functions to be run from the + * top-level event loop after the current top-level activity finishes. + */ + +#include + +#include "putty.h" + +struct callback { + struct callback *next; + + toplevel_callback_fn_t fn; + void *ctx; +}; + +struct callback *cbhead = NULL, *cbtail = NULL; + +toplevel_callback_notify_fn_t notify_frontend = NULL; +void *frontend = NULL; + +void request_callback_notifications(toplevel_callback_notify_fn_t fn, + void *fr) +{ + notify_frontend = fn; + frontend = fr; +} + +void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx) +{ + struct callback *cb; + + cb = snew(struct callback); + cb->fn = fn; + cb->ctx = ctx; + + /* If the front end has requested notification of pending + * callbacks, and we didn't already have one queued, let it know + * we do have one now. */ + if (notify_frontend && !cbhead) + notify_frontend(frontend); + + if (cbtail) + cbtail->next = cb; + else + cbhead = cb; + cbtail = cb; + cb->next = NULL; +} + +void run_toplevel_callbacks(void) +{ + while (cbhead) { + struct callback *cb = cbhead; + /* + * Careful ordering here. We call the function _before_ + * advancing cbhead (though, of course, we must free cb + * _after_ advancing it). This means that if the very last + * callback schedules another callback, cbhead does not become + * NULL at any point in this while loop, and so the frontend + * notification function won't be needlessly pestered. + */ + cb->fn(cb->ctx); + cbhead = cb->next; + sfree(cb); + } + cbtail = NULL; +} diff --git a/putty.h b/putty.h index e5c641d3..d1a5daeb 100644 --- a/putty.h +++ b/putty.h @@ -1390,6 +1390,31 @@ void expire_timer_context(void *ctx); int run_timers(unsigned long now, unsigned long *next); void timer_change_notify(unsigned long next); +/* + * Exports from callback.c. + * + * This provides a method of queuing function calls to be run at the + * earliest convenience from the top-level event loop. Use it if + * you're deep in a nested chain of calls and want to trigger an + * action which will probably lead to your function being re-entered + * recursively if you just call the initiating function the normal + * way. + * + * Most front ends run the queued callbacks by simply calling + * run_toplevel_callbacks() after handling each event in their + * top-level event loop. However, if a front end doesn't have control + * over its own event loop (e.g. because it's using GTK) then it can + * instead request notifications when a callback is available, so that + * it knows to ask its delegate event loop to do the same thing. + */ +typedef void (*toplevel_callback_fn_t)(void *ctx); +void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx); +void run_toplevel_callbacks(void); + +typedef void (*toplevel_callback_notify_fn_t)(void *frontend); +void request_callback_notifications(toplevel_callback_notify_fn_t notify, + void *frontend); + /* * Define no-op macros for the jump list functions, on platforms that * don't support them. (This is a bit of a hack, and it'd be nicer to diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 667c72b3..2b660739 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -95,6 +95,7 @@ struct gui_data { int busy_status; guint term_paste_idle_id; guint term_exit_idle_id; + guint toplevel_callback_idle_id; int alt_keycode; int alt_digits; char *wintitle; @@ -1399,6 +1400,25 @@ void notify_remote_exit(void *frontend) inst->term_exit_idle_id = gtk_idle_add(idle_exit_func, inst); } +static gint idle_toplevel_callback_func(gpointer data) +{ + struct gui_data *inst = (struct gui_data *)data; + + run_toplevel_callbacks(); + + gtk_idle_remove(inst->toplevel_callback_idle_id); + + return TRUE; +} + +void notify_toplevel_callback(void *frontend) +{ + struct gui_data *inst = (struct gui_data *)frontend; + + inst->toplevel_callback_idle_id = + gtk_idle_add(idle_toplevel_callback_func, inst); +} + static gint timer_trigger(gpointer data) { unsigned long now = GPOINTER_TO_LONG(data); @@ -3858,6 +3878,8 @@ int pt_main(int argc, char **argv) inst->eventlogstuff = eventlogstuff_new(); + request_callback_notifications(notify_toplevel_callback, inst); + inst->term = term_init(inst->conf, &inst->ucsdata, inst); inst->logctx = log_init(inst, inst->conf); term_provide_logctx(inst->term, inst->logctx); diff --git a/unix/uxplink.c b/unix/uxplink.c index 898f27c3..36f3e2fc 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -1118,6 +1118,8 @@ int main(int argc, char **argv) net_pending_errors(); + run_toplevel_callbacks(); + if ((!connopen || !back->connected(backhandle)) && bufchain_size(&stdout_data) == 0 && bufchain_size(&stderr_data) == 0) diff --git a/unix/uxsftp.c b/unix/uxsftp.c index 57f28416..44e11764 100644 --- a/unix/uxsftp.c +++ b/unix/uxsftp.c @@ -536,6 +536,8 @@ static int ssh_sftp_do_select(int include_stdin, int no_fds_ok) sfree(fdlist); + run_toplevel_callbacks(); + return FD_ISSET(0, &rset) ? 1 : 0; } diff --git a/windows/window.c b/windows/window.c index 8d13ff91..15e77d72 100644 --- a/windows/window.c +++ b/windows/window.c @@ -869,6 +869,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) } else sfree(handles); + run_toplevel_callbacks(); + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) goto finished; /* two-level break */ @@ -877,12 +879,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) DispatchMessage(&msg); /* Send the paste buffer if there's anything to send */ term_paste(term); - /* If there's nothing new in the queue then we can do everything - * we've delayed, reading the socket, writing, and repainting - * the window. - */ + if (must_close_session) close_session(); + + run_toplevel_callbacks(); } /* The messages seem unreliable; especially if we're being tricky */ diff --git a/windows/winplink.c b/windows/winplink.c index 37453bb7..9d29c20b 100644 --- a/windows/winplink.c +++ b/windows/winplink.c @@ -738,6 +738,8 @@ int main(int argc, char **argv) } } + run_toplevel_callbacks(); + if (n == WAIT_TIMEOUT) { now = next; } else { diff --git a/windows/winsftp.c b/windows/winsftp.c index 33acaf62..23273507 100644 --- a/windows/winsftp.c +++ b/windows/winsftp.c @@ -585,6 +585,8 @@ int do_eventsel_loop(HANDLE other_event) sfree(handles); + run_toplevel_callbacks(); + if (n == WAIT_TIMEOUT) { now = next; } else { -- 2.45.2