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

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]
This commit is contained in:
Simon Tatham 2013-08-17 16:06:08 +00:00
parent 883641845f
commit 75c79e318f
9 changed files with 130 additions and 6 deletions

4
Recipe
View File

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

68
callback.c Normal file
View File

@ -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 <stddef.h>
#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;
}

25
putty.h
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -738,6 +738,8 @@ int main(int argc, char **argv)
}
}
run_toplevel_callbacks();
if (n == WAIT_TIMEOUT) {
now = next;
} else {

View File

@ -585,6 +585,8 @@ int do_eventsel_loop(HANDLE other_event)
sfree(handles);
run_toplevel_callbacks();
if (n == WAIT_TIMEOUT) {
now = next;
} else {