-
Notifications
You must be signed in to change notification settings - Fork 7
/
callback.c
133 lines (111 loc) · 3.17 KB
/
callback.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
* 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;
};
static struct callback *cbcurr = NULL, *cbhead = NULL, *cbtail = NULL;
static toplevel_callback_notify_fn_t notify_frontend = NULL;
static void *notify_ctx = NULL;
void request_callback_notifications(toplevel_callback_notify_fn_t fn,
void *ctx)
{
notify_frontend = fn;
notify_ctx = ctx;
}
static void run_idempotent_callback(void *ctx)
{
struct IdempotentCallback *ic = (struct IdempotentCallback *)ctx;
ic->queued = false;
ic->fn(ic->ctx);
}
void queue_idempotent_callback(struct IdempotentCallback *ic)
{
if (ic->queued)
return;
ic->queued = true;
queue_toplevel_callback(run_idempotent_callback, ic);
}
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 ||
(cb->fn == run_idempotent_callback &&
((struct IdempotentCallback *)cb->ctx)->ctx == ctx)) {
sfree(cb);
} else {
if (!newhead)
newhead = cb;
else
newtail->next = cb;
newtail = cb;
}
}
cbhead = newhead;
cbtail = newtail;
if (newtail)
newtail->next = NULL;
}
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 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(notify_ctx);
if (cbtail)
cbtail->next = cb;
else
cbhead = cb;
cbtail = cb;
cb->next = NULL;
}
bool run_toplevel_callbacks(void)
{
bool done_something = false;
if (cbhead) {
/*
* 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.
*/
cbcurr = cbhead;
cbhead = cbhead->next;
if (!cbhead)
cbtail = NULL;
/*
* Now run the callback, and then clear it out of cbcurr.
*/
cbcurr->fn(cbcurr->ctx);
sfree(cbcurr);
cbcurr = NULL;
done_something = true;
}
return done_something;
}
bool toplevel_callback_pending(void)
{
return cbcurr != NULL || cbhead != NULL;
}