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

Only run one toplevel callback per event loop iteration.

This change attempts to reinstate as a universal property something
which was sporadically true of the ad-hockery that came before
toplevel callbacks: that if there's a _very long_ queue of things to
be done through the callback mechanism, the doing of them will be
interleaved with re-checks of other event sources, which might (e.g.)
cause a flag to be set which makes the next callback decide not to do
anything after all.

[originally from svn r10040]
This commit is contained in:
Simon Tatham 2013-09-15 14:05:31 +00:00
parent 043a762b5f
commit 5c4ce2fadf
8 changed files with 99 additions and 47 deletions

View File

@ -26,10 +26,28 @@ void request_callback_notifications(toplevel_callback_notify_fn_t fn,
frontend = fr; frontend = fr;
} }
void stoat_callback(void *ctx)
{
static int stoat = 0;
if (++stoat % 1000 == 0)
debug(("stoat %d\n", stoat));
queue_toplevel_callback(stoat_callback, NULL);
}
void queue_stoat(void)
{
static int stoat = 0;
if (!stoat) {
stoat = 1;
queue_toplevel_callback(stoat_callback, NULL);
}
}
void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx) void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx)
{ {
struct callback *cb; struct callback *cb;
queue_stoat();
cb = snew(struct callback); cb = snew(struct callback);
cb->fn = fn; cb->fn = fn;
cb->ctx = ctx; cb->ctx = ctx;
@ -50,19 +68,27 @@ void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx)
void run_toplevel_callbacks(void) void run_toplevel_callbacks(void)
{ {
while (cbhead) { queue_stoat();
if (cbhead) {
struct callback *cb = cbhead; struct callback *cb = cbhead;
/* /*
* Careful ordering here. We call the function _before_ * Careful ordering here. We call the function _before_
* advancing cbhead (though, of course, we must free cb * advancing cbhead (though, of course, we must free cb
* _after_ advancing it). This means that if the very last * _after_ advancing it). This means that if the very last
* callback schedules another callback, cbhead does not become * callback schedules another callback, cbhead does not become
* NULL at any point in this while loop, and so the frontend * NULL at any point, and so the frontend notification
* notification function won't be needlessly pestered. * function won't be needlessly pestered.
*/ */
cb->fn(cb->ctx); cb->fn(cb->ctx);
cbhead = cb->next; cbhead = cb->next;
sfree(cb); sfree(cb);
if (!cbhead)
cbtail = NULL;
} }
cbtail = NULL; }
int toplevel_callback_pending(void)
{
queue_stoat();
return cbhead != NULL;
} }

View File

@ -1403,11 +1403,16 @@ void timer_change_notify(unsigned long next);
* top-level event loop. However, if a front end doesn't have control * 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 * 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 * instead request notifications when a callback is available, so that
* it knows to ask its delegate event loop to do the same thing. * it knows to ask its delegate event loop to do the same thing. Also,
* if a front end needs to know whether a callback is pending without
* actually running it (e.g. so as to put a zero timeout on a select()
* call) then it can call toplevel_callback_pending(), which will
* return true if at least one callback is in the queue.
*/ */
typedef void (*toplevel_callback_fn_t)(void *ctx); typedef void (*toplevel_callback_fn_t)(void *ctx);
void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx); void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx);
void run_toplevel_callbacks(void); void run_toplevel_callbacks(void);
int toplevel_callback_pending(void);
typedef void (*toplevel_callback_notify_fn_t)(void *frontend); typedef void (*toplevel_callback_notify_fn_t)(void *frontend);
void request_callback_notifications(toplevel_callback_notify_fn_t notify, void request_callback_notifications(toplevel_callback_notify_fn_t notify,

View File

@ -1422,7 +1422,8 @@ static gint idle_toplevel_callback_func(gpointer data)
run_toplevel_callbacks(); run_toplevel_callbacks();
} }
gtk_idle_remove(inst->toplevel_callback_idle_id); if (!toplevel_callback_pending())
gtk_idle_remove(inst->toplevel_callback_idle_id);
return TRUE; return TRUE;
} }

View File

@ -979,6 +979,7 @@ int main(int argc, char **argv)
int maxfd; int maxfd;
int rwx; int rwx;
int ret; int ret;
unsigned long next;
FD_ZERO(&rset); FD_ZERO(&rset);
FD_ZERO(&wset); FD_ZERO(&wset);
@ -1032,12 +1033,17 @@ int main(int argc, char **argv)
FD_SET_MAX(fd, maxfd, xset); FD_SET_MAX(fd, maxfd, xset);
} }
do { if (toplevel_callback_pending()) {
unsigned long next, then; struct timeval tv;
long ticks; tv.tv_sec = 0;
struct timeval tv, *ptv; tv.tv_usec = 0;
ret = select(maxfd, &rset, &wset, &xset, &tv);
} else if (run_timers(now, &next)) {
do {
unsigned long then;
long ticks;
struct timeval tv;
if (run_timers(now, &next)) {
then = now; then = now;
now = GETTICKCOUNT(); now = GETTICKCOUNT();
if (now - then > next - then) if (now - then > next - then)
@ -1046,16 +1052,15 @@ int main(int argc, char **argv)
ticks = next - now; ticks = next - now;
tv.tv_sec = ticks / 1000; tv.tv_sec = ticks / 1000;
tv.tv_usec = ticks % 1000 * 1000; tv.tv_usec = ticks % 1000 * 1000;
ptv = &tv; ret = select(maxfd, &rset, &wset, &xset, &tv);
} else { if (ret == 0)
ptv = NULL; now = next;
} else
ret = select(maxfd, &rset, &wset, &xset, ptv); now = GETTICKCOUNT();
if (ret == 0) } while (ret < 0 && errno == EINTR);
now = next; } else {
else ret = select(maxfd, &rset, &wset, &xset, NULL);
now = GETTICKCOUNT(); }
} while (ret < 0 && errno == EINTR);
if (ret < 0) { if (ret < 0) {
perror("select"); perror("select");

View File

@ -444,6 +444,7 @@ static int ssh_sftp_do_select(int include_stdin, int no_fds_ok)
int i, fdcount, fdsize, *fdlist; int i, fdcount, fdsize, *fdlist;
int fd, fdstate, rwx, ret, maxfd; int fd, fdstate, rwx, ret, maxfd;
unsigned long now = GETTICKCOUNT(); unsigned long now = GETTICKCOUNT();
unsigned long next;
fdlist = NULL; fdlist = NULL;
fdcount = fdsize = 0; fdcount = fdsize = 0;
@ -488,12 +489,19 @@ static int ssh_sftp_do_select(int include_stdin, int no_fds_ok)
if (include_stdin) if (include_stdin)
FD_SET_MAX(0, maxfd, rset); FD_SET_MAX(0, maxfd, rset);
do { if (toplevel_callback_pending()) {
unsigned long next, then; struct timeval tv;
long ticks; tv.tv_sec = 0;
struct timeval tv, *ptv; tv.tv_usec = 0;
ret = select(maxfd, &rset, &wset, &xset, &tv);
if (ret == 0)
run_toplevel_callbacks();
} else if (run_timers(now, &next)) {
do {
unsigned long then;
long ticks;
struct timeval tv;
if (run_timers(now, &next)) {
then = now; then = now;
now = GETTICKCOUNT(); now = GETTICKCOUNT();
if (now - then > next - then) if (now - then > next - then)
@ -502,16 +510,15 @@ static int ssh_sftp_do_select(int include_stdin, int no_fds_ok)
ticks = next - now; ticks = next - now;
tv.tv_sec = ticks / 1000; tv.tv_sec = ticks / 1000;
tv.tv_usec = ticks % 1000 * 1000; tv.tv_usec = ticks % 1000 * 1000;
ptv = &tv; ret = select(maxfd, &rset, &wset, &xset, &tv);
} else { if (ret == 0)
ptv = NULL; now = next;
} else
ret = select(maxfd, &rset, &wset, &xset, ptv); now = GETTICKCOUNT();
if (ret == 0) } while (ret < 0 && errno == EINTR);
now = next; } else {
else ret = select(maxfd, &rset, &wset, &xset, NULL);
now = GETTICKCOUNT(); }
} while (ret < 0 && errno != EINTR);
} while (ret == 0); } while (ret == 0);
if (ret < 0) { if (ret < 0) {

View File

@ -847,11 +847,20 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
while (1) { while (1) {
HANDLE *handles; HANDLE *handles;
int nhandles, n; int nhandles, n;
DWORD timeout;
if (toplevel_callback_pending()) {
timeout = 0;
} else {
timeout = INFINITE;
/* The messages seem unreliable; especially if we're being tricky */
term_set_focus(term, GetForegroundWindow() == hwnd);
}
handles = handle_get_events(&nhandles); handles = handle_get_events(&nhandles);
n = MsgWaitForMultipleObjects(nhandles, handles, FALSE, INFINITE, n = MsgWaitForMultipleObjects(nhandles, handles, FALSE,
QS_ALLINPUT); timeout, QS_ALLINPUT);
if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) {
handle_got_event(handles[n - WAIT_OBJECT_0]); handle_got_event(handles[n - WAIT_OBJECT_0]);
@ -859,20 +868,15 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
} else } else
sfree(handles); sfree(handles);
run_toplevel_callbacks(); if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) if (msg.message == WM_QUIT)
goto finished; /* two-level break */ goto finished; /* two-level break */
if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg))) if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
DispatchMessage(&msg); DispatchMessage(&msg);
run_toplevel_callbacks();
} }
/* The messages seem unreliable; especially if we're being tricky */ run_toplevel_callbacks();
term_set_focus(term, GetForegroundWindow() == hwnd);
} }
finished: finished:

View File

@ -648,7 +648,9 @@ int main(int argc, char **argv)
sending = TRUE; sending = TRUE;
} }
if (run_timers(now, &next)) { if (toplevel_callback_pending()) {
ticks = 0;
} else if (run_timers(now, &next)) {
then = now; then = now;
now = GETTICKCOUNT(); now = GETTICKCOUNT();
if (now - then > next - then) if (now - then > next - then)

View File

@ -493,7 +493,9 @@ int do_eventsel_loop(HANDLE other_event)
int skcount; int skcount;
unsigned long now = GETTICKCOUNT(); unsigned long now = GETTICKCOUNT();
if (run_timers(now, &next)) { if (toplevel_callback_pending()) {
ticks = 0;
} else if (run_timers(now, &next)) {
then = now; then = now;
now = GETTICKCOUNT(); now = GETTICKCOUNT();
if (now - then > next - then) if (now - then > next - then)