зеркало из https://github.com/github/putty.git
New facility for removing pending toplevel callbacks.
This is used when you're about to destroy an object that is (potentially) the context parameter for some still-pending toplevel callback. It causes callbacks.c to go through its pending list and delete any callback records referring to that context parameter, so that when you destroy the object those callbacks aren't still waiting to cause stale-pointer dereferences.
This commit is contained in:
Родитель
99bdaa7752
Коммит
afa9734b7d
65
callback.c
65
callback.c
|
@ -14,7 +14,7 @@ struct callback {
|
||||||
void *ctx;
|
void *ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct callback *cbhead = NULL, *cbtail = NULL;
|
struct callback *cbcurr = NULL, *cbhead = NULL, *cbtail = NULL;
|
||||||
|
|
||||||
toplevel_callback_notify_fn_t notify_frontend = NULL;
|
toplevel_callback_notify_fn_t notify_frontend = NULL;
|
||||||
void *frontend = NULL;
|
void *frontend = NULL;
|
||||||
|
@ -26,6 +26,30 @@ void request_callback_notifications(toplevel_callback_notify_fn_t fn,
|
||||||
frontend = fr;
|
frontend = fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void delete_callbacks_for_context(void *ctx)
|
||||||
|
{
|
||||||
|
struct callback *newhead, *newtail;
|
||||||
|
|
||||||
|
newhead = newtail = NULL;
|
||||||
|
while (cbhead) {
|
||||||
|
struct callback *cb = cbhead;
|
||||||
|
cbhead = cbhead->next;
|
||||||
|
if (cb->ctx == ctx) {
|
||||||
|
sfree(cb);
|
||||||
|
} else {
|
||||||
|
if (!newhead)
|
||||||
|
newhead = cb;
|
||||||
|
else
|
||||||
|
newtail->next = cb;
|
||||||
|
|
||||||
|
newtail = cb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cbhead = newhead;
|
||||||
|
cbtail = newtail;
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
@ -34,10 +58,18 @@ void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx)
|
||||||
cb->fn = fn;
|
cb->fn = fn;
|
||||||
cb->ctx = ctx;
|
cb->ctx = ctx;
|
||||||
|
|
||||||
/* If the front end has requested notification of pending
|
/*
|
||||||
|
* If the front end has requested notification of pending
|
||||||
* callbacks, and we didn't already have one queued, let it know
|
* callbacks, and we didn't already have one queued, let it know
|
||||||
* we do have one now. */
|
* we do have one now.
|
||||||
if (notify_frontend && !cbhead)
|
*
|
||||||
|
* 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)
|
||||||
notify_frontend(frontend);
|
notify_frontend(frontend);
|
||||||
|
|
||||||
if (cbtail)
|
if (cbtail)
|
||||||
|
@ -51,24 +83,27 @@ void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx)
|
||||||
void run_toplevel_callbacks(void)
|
void run_toplevel_callbacks(void)
|
||||||
{
|
{
|
||||||
if (cbhead) {
|
if (cbhead) {
|
||||||
struct callback *cb = cbhead;
|
|
||||||
/*
|
/*
|
||||||
* Careful ordering here. We call the function _before_
|
* Transfer the head callback into cbcurr to indicate that
|
||||||
* advancing cbhead (though, of course, we must free cb
|
* it's being executed. Then operations which transform the
|
||||||
* _after_ advancing it). This means that if the very last
|
* queue, like delete_callbacks_for_context, can proceed as if
|
||||||
* callback schedules another callback, cbhead does not become
|
* it's not there.
|
||||||
* NULL at any point, and so the frontend notification
|
|
||||||
* function won't be needlessly pestered.
|
|
||||||
*/
|
*/
|
||||||
cb->fn(cb->ctx);
|
cbcurr = cbhead;
|
||||||
cbhead = cb->next;
|
cbhead = cbhead->next;
|
||||||
sfree(cb);
|
|
||||||
if (!cbhead)
|
if (!cbhead)
|
||||||
cbtail = NULL;
|
cbtail = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now run the callback, and then clear it out of cbcurr.
|
||||||
|
*/
|
||||||
|
cbcurr->fn(cbcurr->ctx);
|
||||||
|
sfree(cbcurr);
|
||||||
|
cbcurr = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int toplevel_callback_pending(void)
|
int toplevel_callback_pending(void)
|
||||||
{
|
{
|
||||||
return cbhead != NULL;
|
return cbcurr != NULL || cbhead != NULL;
|
||||||
}
|
}
|
||||||
|
|
1
putty.h
1
putty.h
|
@ -1534,6 +1534,7 @@ 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);
|
int toplevel_callback_pending(void);
|
||||||
|
void delete_callbacks_for_context(void *ctx);
|
||||||
|
|
||||||
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,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче