2013-08-17 16:06:08 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
};
|
|
|
|
|
2017-11-25 17:17:21 +00:00
|
|
|
struct callback *cbcurr = NULL, *cbhead = NULL, *cbtail = NULL;
|
2013-08-17 16:06:08 +00:00
|
|
|
|
|
|
|
toplevel_callback_notify_fn_t notify_frontend = NULL;
|
2018-09-12 08:10:51 +00:00
|
|
|
void *notify_ctx = NULL;
|
2013-08-17 16:06:08 +00:00
|
|
|
|
|
|
|
void request_callback_notifications(toplevel_callback_notify_fn_t fn,
|
2018-09-12 08:10:51 +00:00
|
|
|
void *ctx)
|
2013-08-17 16:06:08 +00:00
|
|
|
{
|
|
|
|
notify_frontend = fn;
|
2018-09-12 08:10:51 +00:00
|
|
|
notify_ctx = ctx;
|
2013-08-17 16:06:08 +00:00
|
|
|
}
|
|
|
|
|
2018-05-18 06:22:57 +00:00
|
|
|
static void run_idempotent_callback(void *ctx)
|
|
|
|
{
|
|
|
|
struct IdempotentCallback *ic = (struct IdempotentCallback *)ctx;
|
2018-10-29 19:50:29 +00:00
|
|
|
ic->queued = false;
|
2018-05-18 06:22:57 +00:00
|
|
|
ic->fn(ic->ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void queue_idempotent_callback(struct IdempotentCallback *ic)
|
|
|
|
{
|
|
|
|
if (ic->queued)
|
|
|
|
return;
|
2018-10-29 19:50:29 +00:00
|
|
|
ic->queued = true;
|
2018-05-18 06:22:57 +00:00
|
|
|
queue_toplevel_callback(run_idempotent_callback, ic);
|
|
|
|
}
|
|
|
|
|
2017-11-25 17:17:21 +00:00
|
|
|
void delete_callbacks_for_context(void *ctx)
|
|
|
|
{
|
|
|
|
struct callback *newhead, *newtail;
|
|
|
|
|
|
|
|
newhead = newtail = NULL;
|
|
|
|
while (cbhead) {
|
|
|
|
struct callback *cb = cbhead;
|
|
|
|
cbhead = cbhead->next;
|
2018-05-18 06:22:57 +00:00
|
|
|
if (cb->ctx == ctx ||
|
|
|
|
(cb->fn == run_idempotent_callback &&
|
|
|
|
((struct IdempotentCallback *)cb->ctx)->ctx == ctx)) {
|
2017-11-25 17:17:21 +00:00
|
|
|
sfree(cb);
|
|
|
|
} else {
|
|
|
|
if (!newhead)
|
|
|
|
newhead = cb;
|
|
|
|
else
|
|
|
|
newtail->next = cb;
|
|
|
|
|
|
|
|
newtail = cb;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cbhead = newhead;
|
|
|
|
cbtail = newtail;
|
2018-10-25 17:34:19 +00:00
|
|
|
if (newtail)
|
|
|
|
newtail->next = NULL;
|
2017-11-25 17:17:21 +00:00
|
|
|
}
|
|
|
|
|
2013-08-17 16:06:08 +00:00
|
|
|
void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx)
|
|
|
|
{
|
|
|
|
struct callback *cb;
|
|
|
|
|
|
|
|
cb = snew(struct callback);
|
|
|
|
cb->fn = fn;
|
|
|
|
cb->ctx = ctx;
|
|
|
|
|
2017-11-25 17:17:21 +00:00
|
|
|
/*
|
|
|
|
* If the front end has requested notification of pending
|
2013-08-17 16:06:08 +00:00
|
|
|
* callbacks, and we didn't already have one queued, let it know
|
2017-11-25 17:17:21 +00:00
|
|
|
* we do have one now.
|
|
|
|
*
|
|
|
|
* If cbcurr is non-NULL, i.e. we are actually in the middle of
|
|
|
|
* executing a callback right now, then we count that as the queue
|
|
|
|
* already having been non-empty. That saves the front end getting
|
|
|
|
* a constant stream of needless re-notifications if the last
|
|
|
|
* callback keeps re-scheduling itself.
|
|
|
|
*/
|
|
|
|
if (notify_frontend && !cbhead && !cbcurr)
|
2018-09-12 08:10:51 +00:00
|
|
|
notify_frontend(notify_ctx);
|
2013-08-17 16:06:08 +00:00
|
|
|
|
|
|
|
if (cbtail)
|
|
|
|
cbtail->next = cb;
|
|
|
|
else
|
|
|
|
cbhead = cb;
|
|
|
|
cbtail = cb;
|
|
|
|
cb->next = NULL;
|
|
|
|
}
|
|
|
|
|
Fix startup hang in Unix file transfer tools.
This seems to be a knock-on effect of my recent reworking of the SSH
code to be based around queues and callbacks. The loop iteration
function in uxsftp.c (ssh_sftp_do_select) would keep going round its
select loop until something had happened on one of its file
descriptors, and then return to the caller in the assumption that the
resulting data might have triggered whatever condition the caller was
waiting for - and if not, then the caller checks, finds nothing
interesting has happened, and resumes looping with no harm done.
But now, when something happens on an fd, it doesn't _synchronously_
trigger the follow-up condition PSFTP was waiting for (which, at
startup time, happens to be back->sendok() starting to return TRUE).
Instead, it schedules a callback, which will schedule a callback,
which ... ends up setting that flag. But by that time, the loop
function has already returned, the caller has found nothing
interesting and resumed looping, and _now_ the interesting thing
happens but it's too late because ssh_sftp_do_select will wait until
the next file descriptor activity before it next returns.
Solution: give run_toplevel_callbacks a return value which says
whether it's actually done something, and if so, return immediately in
case that was the droid the caller was looking for. As it were.
2018-05-24 15:54:16 +00:00
|
|
|
int run_toplevel_callbacks(void)
|
2013-08-17 16:06:08 +00:00
|
|
|
{
|
2018-10-29 19:50:29 +00:00
|
|
|
int done_something = false;
|
Fix startup hang in Unix file transfer tools.
This seems to be a knock-on effect of my recent reworking of the SSH
code to be based around queues and callbacks. The loop iteration
function in uxsftp.c (ssh_sftp_do_select) would keep going round its
select loop until something had happened on one of its file
descriptors, and then return to the caller in the assumption that the
resulting data might have triggered whatever condition the caller was
waiting for - and if not, then the caller checks, finds nothing
interesting has happened, and resumes looping with no harm done.
But now, when something happens on an fd, it doesn't _synchronously_
trigger the follow-up condition PSFTP was waiting for (which, at
startup time, happens to be back->sendok() starting to return TRUE).
Instead, it schedules a callback, which will schedule a callback,
which ... ends up setting that flag. But by that time, the loop
function has already returned, the caller has found nothing
interesting and resumed looping, and _now_ the interesting thing
happens but it's too late because ssh_sftp_do_select will wait until
the next file descriptor activity before it next returns.
Solution: give run_toplevel_callbacks a return value which says
whether it's actually done something, and if so, return immediately in
case that was the droid the caller was looking for. As it were.
2018-05-24 15:54:16 +00:00
|
|
|
|
2013-09-15 14:05:31 +00:00
|
|
|
if (cbhead) {
|
2013-08-17 16:06:08 +00:00
|
|
|
/*
|
2017-11-25 17:17:21 +00:00
|
|
|
* Transfer the head callback into cbcurr to indicate that
|
|
|
|
* it's being executed. Then operations which transform the
|
|
|
|
* queue, like delete_callbacks_for_context, can proceed as if
|
|
|
|
* it's not there.
|
2013-08-17 16:06:08 +00:00
|
|
|
*/
|
2017-11-25 17:17:21 +00:00
|
|
|
cbcurr = cbhead;
|
|
|
|
cbhead = cbhead->next;
|
2013-09-15 14:05:31 +00:00
|
|
|
if (!cbhead)
|
|
|
|
cbtail = NULL;
|
2017-11-25 17:17:21 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now run the callback, and then clear it out of cbcurr.
|
|
|
|
*/
|
|
|
|
cbcurr->fn(cbcurr->ctx);
|
|
|
|
sfree(cbcurr);
|
|
|
|
cbcurr = NULL;
|
Fix startup hang in Unix file transfer tools.
This seems to be a knock-on effect of my recent reworking of the SSH
code to be based around queues and callbacks. The loop iteration
function in uxsftp.c (ssh_sftp_do_select) would keep going round its
select loop until something had happened on one of its file
descriptors, and then return to the caller in the assumption that the
resulting data might have triggered whatever condition the caller was
waiting for - and if not, then the caller checks, finds nothing
interesting has happened, and resumes looping with no harm done.
But now, when something happens on an fd, it doesn't _synchronously_
trigger the follow-up condition PSFTP was waiting for (which, at
startup time, happens to be back->sendok() starting to return TRUE).
Instead, it schedules a callback, which will schedule a callback,
which ... ends up setting that flag. But by that time, the loop
function has already returned, the caller has found nothing
interesting and resumed looping, and _now_ the interesting thing
happens but it's too late because ssh_sftp_do_select will wait until
the next file descriptor activity before it next returns.
Solution: give run_toplevel_callbacks a return value which says
whether it's actually done something, and if so, return immediately in
case that was the droid the caller was looking for. As it were.
2018-05-24 15:54:16 +00:00
|
|
|
|
2018-10-29 19:50:29 +00:00
|
|
|
done_something = true;
|
2013-08-17 16:06:08 +00:00
|
|
|
}
|
Fix startup hang in Unix file transfer tools.
This seems to be a knock-on effect of my recent reworking of the SSH
code to be based around queues and callbacks. The loop iteration
function in uxsftp.c (ssh_sftp_do_select) would keep going round its
select loop until something had happened on one of its file
descriptors, and then return to the caller in the assumption that the
resulting data might have triggered whatever condition the caller was
waiting for - and if not, then the caller checks, finds nothing
interesting has happened, and resumes looping with no harm done.
But now, when something happens on an fd, it doesn't _synchronously_
trigger the follow-up condition PSFTP was waiting for (which, at
startup time, happens to be back->sendok() starting to return TRUE).
Instead, it schedules a callback, which will schedule a callback,
which ... ends up setting that flag. But by that time, the loop
function has already returned, the caller has found nothing
interesting and resumed looping, and _now_ the interesting thing
happens but it's too late because ssh_sftp_do_select will wait until
the next file descriptor activity before it next returns.
Solution: give run_toplevel_callbacks a return value which says
whether it's actually done something, and if so, return immediately in
case that was the droid the caller was looking for. As it were.
2018-05-24 15:54:16 +00:00
|
|
|
return done_something;
|
2013-09-15 14:05:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int toplevel_callback_pending(void)
|
|
|
|
{
|
2017-11-25 17:17:21 +00:00
|
|
|
return cbcurr != NULL || cbhead != NULL;
|
2013-08-17 16:06:08 +00:00
|
|
|
}
|