| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
/* |
|
2
|
|
|
|
|
|
|
* Timer insertion is an O(n) operation; in a real world eventloop based on a |
|
3
|
|
|
|
|
|
|
* heap insertion would be O(log N). |
|
4
|
|
|
|
|
|
|
*/ |
|
5
|
|
|
|
|
|
|
#include |
|
6
|
|
|
|
|
|
|
#include |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
#include "duktape.h" |
|
9
|
|
|
|
|
|
|
#include "c_eventloop.h" |
|
10
|
|
|
|
|
|
|
#include "pl_util.h" |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
#if !defined(DUKTAPE_EVENTLOOP_DEBUG) |
|
13
|
|
|
|
|
|
|
#define DUKTAPE_EVENTLOOP_DEBUG 0 /* set to 1 to debug with printf */ |
|
14
|
|
|
|
|
|
|
#endif |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
#define TIMERS_SLOT_NAME "eventTimers" |
|
17
|
|
|
|
|
|
|
#define MIN_DELAY 1.0 |
|
18
|
|
|
|
|
|
|
#define MIN_WAIT 1.0 |
|
19
|
|
|
|
|
|
|
#define MAX_WAIT 60000.0 |
|
20
|
|
|
|
|
|
|
#define MAX_EXPIRIES 10 |
|
21
|
|
|
|
|
|
|
#define MAX_TIMERS 4096 /* this is quite excessive for embedded use, but good for testing */ |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
typedef struct { |
|
24
|
|
|
|
|
|
|
int64_t id; /* numeric ID (returned from e.g. setTimeout); zero if unused */ |
|
25
|
|
|
|
|
|
|
double target; /* next target time */ |
|
26
|
|
|
|
|
|
|
double delay; /* delay/interval */ |
|
27
|
|
|
|
|
|
|
int oneshot; /* oneshot=1 (setTimeout), repeated=0 (setInterval) */ |
|
28
|
|
|
|
|
|
|
int removed; /* timer has been requested for removal */ |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
/* The callback associated with the timer is held in the "global stash", |
|
31
|
|
|
|
|
|
|
* in .eventTimers[String(id)]. The references must be deleted |
|
32
|
|
|
|
|
|
|
* when a timer struct is deleted. |
|
33
|
|
|
|
|
|
|
*/ |
|
34
|
|
|
|
|
|
|
} ev_timer; |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
/* Active timers. Dense list, terminates to end of list or first unused timer. |
|
37
|
|
|
|
|
|
|
* The list is sorted by 'target', with lowest 'target' (earliest expiry) last |
|
38
|
|
|
|
|
|
|
* in the list. When a timer's callback is being called, the timer is moved |
|
39
|
|
|
|
|
|
|
* to 'timer_expiring' as it needs special handling should the user callback |
|
40
|
|
|
|
|
|
|
* delete that particular timer. |
|
41
|
|
|
|
|
|
|
*/ |
|
42
|
|
|
|
|
|
|
static ev_timer timer_list[MAX_TIMERS]; |
|
43
|
|
|
|
|
|
|
static ev_timer timer_expiring; |
|
44
|
|
|
|
|
|
|
static int timer_count; /* last timer at timer_count - 1 */ |
|
45
|
|
|
|
|
|
|
static int64_t timer_next_id = 1; |
|
46
|
|
|
|
|
|
|
|
|
47
|
2000359
|
|
|
|
|
|
static ev_timer *find_nearest_timer(void) { |
|
48
|
|
|
|
|
|
|
/* Last timer expires first (list is always kept sorted). */ |
|
49
|
2000359
|
100
|
|
|
|
|
if (timer_count <= 0) { |
|
50
|
2000347
|
|
|
|
|
|
return NULL; |
|
51
|
|
|
|
|
|
|
} |
|
52
|
12
|
|
|
|
|
|
return timer_list + timer_count - 1; |
|
53
|
|
|
|
|
|
|
} |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
/* Bubble last timer on timer list backwards until it has been moved to |
|
56
|
|
|
|
|
|
|
* its proper sorted position (based on 'target' time). |
|
57
|
|
|
|
|
|
|
*/ |
|
58
|
11
|
|
|
|
|
|
static void bubble_last_timer(void) { |
|
59
|
|
|
|
|
|
|
int i; |
|
60
|
11
|
|
|
|
|
|
int n = timer_count; |
|
61
|
|
|
|
|
|
|
ev_timer *t; |
|
62
|
|
|
|
|
|
|
ev_timer tmp; |
|
63
|
|
|
|
|
|
|
|
|
64
|
11
|
100
|
|
|
|
|
for (i = n - 1; i > 0; i--) { |
|
65
|
|
|
|
|
|
|
/* Timer to bubble is at index i, timer to compare to is |
|
66
|
|
|
|
|
|
|
* at i-1 (both guaranteed to exist). |
|
67
|
|
|
|
|
|
|
*/ |
|
68
|
4
|
|
|
|
|
|
t = timer_list + i; |
|
69
|
4
|
50
|
|
|
|
|
if (t->target <= (t-1)->target) { |
|
70
|
|
|
|
|
|
|
/* 't' expires earlier than (or same time as) 't-1', so we're done. */ |
|
71
|
4
|
|
|
|
|
|
break; |
|
72
|
|
|
|
|
|
|
} else { |
|
73
|
|
|
|
|
|
|
/* 't' expires later than 't-1', so swap them and repeat. */ |
|
74
|
0
|
|
|
|
|
|
memcpy((void *) &tmp, (void *) (t - 1), sizeof(ev_timer)); |
|
75
|
0
|
|
|
|
|
|
memcpy((void *) (t - 1), (void *) t, sizeof(ev_timer)); |
|
76
|
0
|
|
|
|
|
|
memcpy((void *) t, (void *) &tmp, sizeof(ev_timer)); |
|
77
|
|
|
|
|
|
|
} |
|
78
|
|
|
|
|
|
|
} |
|
79
|
11
|
|
|
|
|
|
} |
|
80
|
|
|
|
|
|
|
|
|
81
|
2000359
|
|
|
|
|
|
static void expire_timers(duk_context *ctx) { |
|
82
|
|
|
|
|
|
|
ev_timer *t; |
|
83
|
2000359
|
|
|
|
|
|
int sanity = MAX_EXPIRIES; |
|
84
|
|
|
|
|
|
|
double now; |
|
85
|
|
|
|
|
|
|
int rc; |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
/* Because a user callback can mutate the timer list (by adding or deleting |
|
88
|
|
|
|
|
|
|
* a timer), we expire one timer and then rescan from the end again. There |
|
89
|
|
|
|
|
|
|
* is a sanity limit on how many times we do this per expiry round. |
|
90
|
|
|
|
|
|
|
*/ |
|
91
|
|
|
|
|
|
|
|
|
92
|
2000359
|
|
|
|
|
|
duk_push_global_stash(ctx); |
|
93
|
2000359
|
|
|
|
|
|
duk_get_prop_string(ctx, -1, TIMERS_SLOT_NAME); |
|
94
|
|
|
|
|
|
|
/* [ ... stash eventTimers ] */ |
|
95
|
|
|
|
|
|
|
|
|
96
|
2000359
|
|
|
|
|
|
now = now_us() / 1000.0; |
|
97
|
2000370
|
50
|
|
|
|
|
while (sanity-- > 0) { |
|
98
|
|
|
|
|
|
|
/* |
|
99
|
|
|
|
|
|
|
* Expired timer(s) still exist? |
|
100
|
|
|
|
|
|
|
*/ |
|
101
|
2000370
|
100
|
|
|
|
|
if (timer_count <= 0) { |
|
102
|
2000347
|
|
|
|
|
|
break; |
|
103
|
|
|
|
|
|
|
} |
|
104
|
23
|
|
|
|
|
|
t = timer_list + timer_count - 1; |
|
105
|
23
|
100
|
|
|
|
|
if (t->target > now) { |
|
106
|
12
|
|
|
|
|
|
break; |
|
107
|
|
|
|
|
|
|
} |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
/* |
|
110
|
|
|
|
|
|
|
* Move the timer to 'expiring' for the duration of the callback. |
|
111
|
|
|
|
|
|
|
* Mark a one-shot timer deleted, compute a new target for an interval. |
|
112
|
|
|
|
|
|
|
*/ |
|
113
|
11
|
|
|
|
|
|
memcpy((void *) &timer_expiring, (void *) t, sizeof(ev_timer)); |
|
114
|
11
|
|
|
|
|
|
memset((void *) t, 0, sizeof(ev_timer)); |
|
115
|
11
|
|
|
|
|
|
timer_count--; |
|
116
|
11
|
|
|
|
|
|
t = &timer_expiring; |
|
117
|
|
|
|
|
|
|
|
|
118
|
11
|
50
|
|
|
|
|
if (t->oneshot) { |
|
119
|
11
|
|
|
|
|
|
t->removed = 1; |
|
120
|
|
|
|
|
|
|
} else { |
|
121
|
0
|
|
|
|
|
|
t->target = now + t->delay; /* XXX: or t->target + t->delay? */ |
|
122
|
|
|
|
|
|
|
} |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
/* |
|
125
|
|
|
|
|
|
|
* Call timer callback. The callback can operate on the timer list: |
|
126
|
|
|
|
|
|
|
* add new timers, remove timers. The callback can even remove the |
|
127
|
|
|
|
|
|
|
* expired timer whose callback we're calling. However, because the |
|
128
|
|
|
|
|
|
|
* timer being expired has been moved to 'timer_expiring', we don't |
|
129
|
|
|
|
|
|
|
* need to worry about the timer's offset changing on the timer list. |
|
130
|
|
|
|
|
|
|
*/ |
|
131
|
|
|
|
|
|
|
#if DUKTAPE_EVENTLOOP_DEBUG > 0 |
|
132
|
|
|
|
|
|
|
fprintf(stderr, "calling user callback for timer id %d\n", (int) t->id); |
|
133
|
|
|
|
|
|
|
fflush(stderr); |
|
134
|
|
|
|
|
|
|
#endif |
|
135
|
|
|
|
|
|
|
|
|
136
|
11
|
|
|
|
|
|
duk_push_number(ctx, (double) t->id); |
|
137
|
11
|
|
|
|
|
|
duk_get_prop(ctx, -2); /* -> [ ... stash eventTimers func ] */ |
|
138
|
11
|
|
|
|
|
|
rc = duk_pcall(ctx, 0 /*nargs*/); /* -> [ ... stash eventTimers retval ] */ |
|
139
|
11
|
|
|
|
|
|
check_duktape_call_for_errors(rc, ctx); |
|
140
|
11
|
|
|
|
|
|
duk_pop(ctx); /* [ ... stash eventTimers ] */ |
|
141
|
|
|
|
|
|
|
|
|
142
|
11
|
50
|
|
|
|
|
if (t->removed) { |
|
143
|
|
|
|
|
|
|
/* One-shot timer (always removed) or removed by user callback. */ |
|
144
|
|
|
|
|
|
|
#if DUKTAPE_EVENTLOOP_DEBUG > 0 |
|
145
|
|
|
|
|
|
|
fprintf(stderr, "deleting callback state for timer %d\n", (int) t->id); |
|
146
|
|
|
|
|
|
|
fflush(stderr); |
|
147
|
|
|
|
|
|
|
#endif |
|
148
|
11
|
|
|
|
|
|
duk_push_number(ctx, (double) t->id); |
|
149
|
11
|
|
|
|
|
|
duk_del_prop(ctx, -2); |
|
150
|
|
|
|
|
|
|
} else { |
|
151
|
|
|
|
|
|
|
/* Interval timer, not removed by user callback. Queue back to |
|
152
|
|
|
|
|
|
|
* timer list and bubble to its final sorted position. |
|
153
|
|
|
|
|
|
|
*/ |
|
154
|
|
|
|
|
|
|
#if DUKTAPE_EVENTLOOP_DEBUG > 0 |
|
155
|
|
|
|
|
|
|
fprintf(stderr, "queueing timer %d back into active list\n", (int) t->id); |
|
156
|
|
|
|
|
|
|
fflush(stderr); |
|
157
|
|
|
|
|
|
|
#endif |
|
158
|
0
|
0
|
|
|
|
|
if (timer_count >= MAX_TIMERS) { |
|
159
|
0
|
|
|
|
|
|
(void) duk_error(ctx, DUK_ERR_RANGE_ERROR, "out of timer slots"); |
|
160
|
|
|
|
|
|
|
} |
|
161
|
0
|
|
|
|
|
|
memcpy((void *) (timer_list + timer_count), (void *) t, sizeof(ev_timer)); |
|
162
|
0
|
|
|
|
|
|
timer_count++; |
|
163
|
0
|
|
|
|
|
|
bubble_last_timer(); |
|
164
|
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
} |
|
166
|
|
|
|
|
|
|
|
|
167
|
2000359
|
|
|
|
|
|
memset((void *) &timer_expiring, 0, sizeof(ev_timer)); |
|
168
|
|
|
|
|
|
|
|
|
169
|
2000359
|
|
|
|
|
|
duk_pop_2(ctx); /* -> [ ... ] */ |
|
170
|
2000359
|
|
|
|
|
|
} |
|
171
|
|
|
|
|
|
|
|
|
172
|
2000347
|
|
|
|
|
|
duk_ret_t eventloop_run(duk_context *ctx, void *udata) { |
|
173
|
|
|
|
|
|
|
ev_timer *t; |
|
174
|
|
|
|
|
|
|
double now; |
|
175
|
|
|
|
|
|
|
double diff; |
|
176
|
|
|
|
|
|
|
int timeout; |
|
177
|
|
|
|
|
|
|
int rc; |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
(void) udata; |
|
180
|
|
|
|
|
|
|
for (;;) { |
|
181
|
|
|
|
|
|
|
/* |
|
182
|
|
|
|
|
|
|
* Expire timers. |
|
183
|
|
|
|
|
|
|
*/ |
|
184
|
2000359
|
|
|
|
|
|
expire_timers(ctx); |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
/* |
|
187
|
|
|
|
|
|
|
* Determine poll() timeout (as close to poll() as possible as |
|
188
|
|
|
|
|
|
|
* the wait is relative). |
|
189
|
|
|
|
|
|
|
*/ |
|
190
|
2000359
|
|
|
|
|
|
now = now_us() / 1000.0; |
|
191
|
2000359
|
|
|
|
|
|
t = find_nearest_timer(); |
|
192
|
2000359
|
100
|
|
|
|
|
if (t) { |
|
193
|
12
|
|
|
|
|
|
diff = t->target - now; |
|
194
|
12
|
100
|
|
|
|
|
if (diff < MIN_WAIT) { |
|
195
|
8
|
|
|
|
|
|
diff = MIN_WAIT; |
|
196
|
4
|
50
|
|
|
|
|
} else if (diff > MAX_WAIT) { |
|
197
|
0
|
|
|
|
|
|
diff = MAX_WAIT; |
|
198
|
|
|
|
|
|
|
} |
|
199
|
12
|
|
|
|
|
|
timeout = (int) diff; /* clamping ensures that fits */ |
|
200
|
|
|
|
|
|
|
} else { |
|
201
|
|
|
|
|
|
|
#if DUKTAPE_EVENTLOOP_DEBUG > 0 |
|
202
|
|
|
|
|
|
|
fprintf(stderr, "no timers to poll, exiting\n"); |
|
203
|
|
|
|
|
|
|
fflush(stderr); |
|
204
|
|
|
|
|
|
|
#endif |
|
205
|
2000347
|
|
|
|
|
|
break; |
|
206
|
|
|
|
|
|
|
} |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
/* |
|
209
|
|
|
|
|
|
|
* Poll for timeout. |
|
210
|
|
|
|
|
|
|
*/ |
|
211
|
|
|
|
|
|
|
#if DUKTAPE_EVENTLOOP_DEBUG > 0 |
|
212
|
|
|
|
|
|
|
fprintf(stderr, "going to poll, timeout %d ms\n", timeout); |
|
213
|
|
|
|
|
|
|
fflush(stderr); |
|
214
|
|
|
|
|
|
|
#endif |
|
215
|
12
|
|
|
|
|
|
rc = poll(0, 0, timeout); |
|
216
|
|
|
|
|
|
|
#if DUKTAPE_EVENTLOOP_DEBUG > 0 |
|
217
|
|
|
|
|
|
|
fprintf(stderr, "poll rc: %d\n", rc); |
|
218
|
|
|
|
|
|
|
fflush(stderr); |
|
219
|
|
|
|
|
|
|
#endif |
|
220
|
|
|
|
|
|
|
if (rc < 0) { |
|
221
|
|
|
|
|
|
|
/* error */ |
|
222
|
|
|
|
|
|
|
} else if (rc == 0) { |
|
223
|
|
|
|
|
|
|
/* timeout */ |
|
224
|
|
|
|
|
|
|
} else { |
|
225
|
|
|
|
|
|
|
/* 'rc' fds active -- huh?*/ |
|
226
|
|
|
|
|
|
|
} |
|
227
|
12
|
|
|
|
|
|
} |
|
228
|
|
|
|
|
|
|
|
|
229
|
2000347
|
|
|
|
|
|
return 0; |
|
230
|
|
|
|
|
|
|
} |
|
231
|
|
|
|
|
|
|
|
|
232
|
11
|
|
|
|
|
|
static int create_timer(duk_context *ctx) { |
|
233
|
|
|
|
|
|
|
double delay; |
|
234
|
|
|
|
|
|
|
int oneshot; |
|
235
|
|
|
|
|
|
|
int idx; |
|
236
|
|
|
|
|
|
|
int64_t timer_id; |
|
237
|
|
|
|
|
|
|
double now; |
|
238
|
|
|
|
|
|
|
ev_timer *t; |
|
239
|
|
|
|
|
|
|
|
|
240
|
11
|
|
|
|
|
|
now = now_us() / 1000.0; |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
/* indexes: |
|
243
|
|
|
|
|
|
|
* 0 = function (callback) |
|
244
|
|
|
|
|
|
|
* 1 = delay |
|
245
|
|
|
|
|
|
|
* 2 = boolean: oneshot |
|
246
|
|
|
|
|
|
|
*/ |
|
247
|
11
|
|
|
|
|
|
delay = duk_require_number(ctx, 1); |
|
248
|
11
|
100
|
|
|
|
|
if (delay < MIN_DELAY) { |
|
249
|
7
|
|
|
|
|
|
delay = MIN_DELAY; |
|
250
|
|
|
|
|
|
|
} |
|
251
|
11
|
|
|
|
|
|
oneshot = duk_require_boolean(ctx, 2); |
|
252
|
|
|
|
|
|
|
|
|
253
|
11
|
50
|
|
|
|
|
if (timer_count >= MAX_TIMERS) { |
|
254
|
0
|
|
|
|
|
|
(void) duk_error(ctx, DUK_ERR_RANGE_ERROR, "out of timer slots"); |
|
255
|
|
|
|
|
|
|
} |
|
256
|
11
|
|
|
|
|
|
idx = timer_count++; |
|
257
|
11
|
|
|
|
|
|
timer_id = timer_next_id++; |
|
258
|
11
|
|
|
|
|
|
t = timer_list + idx; |
|
259
|
|
|
|
|
|
|
|
|
260
|
11
|
|
|
|
|
|
memset((void *) t, 0, sizeof(ev_timer)); |
|
261
|
11
|
|
|
|
|
|
t->id = timer_id; |
|
262
|
11
|
|
|
|
|
|
t->target = now + delay; |
|
263
|
11
|
|
|
|
|
|
t->delay = delay; |
|
264
|
11
|
|
|
|
|
|
t->oneshot = oneshot; |
|
265
|
11
|
|
|
|
|
|
t->removed = 0; |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
/* Timer is now at the last position; use swaps to "bubble" it to its |
|
268
|
|
|
|
|
|
|
* correct sorted position. |
|
269
|
|
|
|
|
|
|
*/ |
|
270
|
11
|
|
|
|
|
|
bubble_last_timer(); |
|
271
|
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
/* Finally, register the callback to the global stash 'eventTimers' object. */ |
|
273
|
11
|
|
|
|
|
|
duk_push_global_stash(ctx); |
|
274
|
11
|
|
|
|
|
|
duk_get_prop_string(ctx, -1, TIMERS_SLOT_NAME); /* -> [ func delay oneshot stash eventTimers ] */ |
|
275
|
11
|
|
|
|
|
|
duk_push_number(ctx, (double) timer_id); |
|
276
|
11
|
|
|
|
|
|
duk_dup(ctx, 0); |
|
277
|
11
|
|
|
|
|
|
duk_put_prop(ctx, -3); /* eventTimers[timer_id] = callback */ |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
/* Return timer id. */ |
|
280
|
11
|
|
|
|
|
|
duk_push_number(ctx, (double) timer_id); |
|
281
|
|
|
|
|
|
|
#if DUKTAPE_EVENTLOOP_DEBUG > 0 |
|
282
|
|
|
|
|
|
|
fprintf(stderr, "created timer id: %d\n", (int) timer_id); |
|
283
|
|
|
|
|
|
|
fflush(stderr); |
|
284
|
|
|
|
|
|
|
#endif |
|
285
|
11
|
|
|
|
|
|
return 1; |
|
286
|
|
|
|
|
|
|
} |
|
287
|
|
|
|
|
|
|
|
|
288
|
0
|
|
|
|
|
|
static int delete_timer(duk_context *ctx) { |
|
289
|
|
|
|
|
|
|
int i, n; |
|
290
|
|
|
|
|
|
|
int64_t timer_id; |
|
291
|
|
|
|
|
|
|
ev_timer *t; |
|
292
|
0
|
|
|
|
|
|
int found = 0; |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
/* indexes: |
|
295
|
|
|
|
|
|
|
* 0 = timer id |
|
296
|
|
|
|
|
|
|
*/ |
|
297
|
0
|
|
|
|
|
|
timer_id = (int64_t) duk_require_number(ctx, 0); |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
/* |
|
300
|
|
|
|
|
|
|
* Unlike insertion, deletion needs a full scan of the timer list |
|
301
|
|
|
|
|
|
|
* and an expensive remove. If no match is found, nothing is deleted. |
|
302
|
|
|
|
|
|
|
* Caller gets a boolean return code indicating match. |
|
303
|
|
|
|
|
|
|
* |
|
304
|
|
|
|
|
|
|
* When a timer is being expired and its user callback is running, |
|
305
|
|
|
|
|
|
|
* the timer has been moved to 'timer_expiring' and its deletion |
|
306
|
|
|
|
|
|
|
* needs special handling: just mark it to-be-deleted and let the |
|
307
|
|
|
|
|
|
|
* expiry code remove it. |
|
308
|
|
|
|
|
|
|
*/ |
|
309
|
|
|
|
|
|
|
|
|
310
|
0
|
|
|
|
|
|
t = &timer_expiring; |
|
311
|
0
|
0
|
|
|
|
|
if (t->id == timer_id) { |
|
312
|
0
|
|
|
|
|
|
t->removed = 1; |
|
313
|
0
|
|
|
|
|
|
duk_push_true(ctx); |
|
314
|
|
|
|
|
|
|
#if DUKTAPE_EVENTLOOP_DEBUG > 0 |
|
315
|
|
|
|
|
|
|
fprintf(stderr, "deleted expiring timer id: %d\n", (int) timer_id); |
|
316
|
|
|
|
|
|
|
fflush(stderr); |
|
317
|
|
|
|
|
|
|
#endif |
|
318
|
0
|
|
|
|
|
|
return 1; |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
|
|
321
|
0
|
|
|
|
|
|
n = timer_count; |
|
322
|
0
|
0
|
|
|
|
|
for (i = 0; i < n; i++) { |
|
323
|
0
|
|
|
|
|
|
t = timer_list + i; |
|
324
|
0
|
0
|
|
|
|
|
if (t->id == timer_id) { |
|
325
|
0
|
|
|
|
|
|
found = 1; |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
/* Shift elements downwards to keep the timer list dense |
|
328
|
|
|
|
|
|
|
* (no need if last element). |
|
329
|
|
|
|
|
|
|
*/ |
|
330
|
0
|
0
|
|
|
|
|
if (i < timer_count - 1) { |
|
331
|
0
|
|
|
|
|
|
memmove((void *) t, (void *) (t + 1), (timer_count - i - 1) * sizeof(ev_timer)); |
|
332
|
|
|
|
|
|
|
} |
|
333
|
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
/* Zero last element for clarity. */ |
|
335
|
0
|
|
|
|
|
|
memset((void *) (timer_list + n - 1), 0, sizeof(ev_timer)); |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
/* Update timer_count. */ |
|
338
|
0
|
|
|
|
|
|
timer_count--; |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
/* The C state is now up-to-date, but we still need to delete |
|
341
|
|
|
|
|
|
|
* the timer callback state from the global 'stash'. |
|
342
|
|
|
|
|
|
|
*/ |
|
343
|
0
|
|
|
|
|
|
duk_push_global_stash(ctx); |
|
344
|
0
|
|
|
|
|
|
duk_get_prop_string(ctx, -1, TIMERS_SLOT_NAME); /* -> [ timer_id stash eventTimers ] */ |
|
345
|
0
|
|
|
|
|
|
duk_push_number(ctx, (double) timer_id); |
|
346
|
0
|
|
|
|
|
|
duk_del_prop(ctx, -2); /* delete eventTimers[timer_id] */ |
|
347
|
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
#if DUKTAPE_EVENTLOOP_DEBUG > 0 |
|
349
|
|
|
|
|
|
|
fprintf(stderr, "deleted timer id: %d\n", (int) timer_id); |
|
350
|
|
|
|
|
|
|
fflush(stderr); |
|
351
|
|
|
|
|
|
|
#endif |
|
352
|
0
|
|
|
|
|
|
break; |
|
353
|
|
|
|
|
|
|
} |
|
354
|
|
|
|
|
|
|
} |
|
355
|
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
#if DUKTAPE_EVENTLOOP_DEBUG > 0 |
|
357
|
|
|
|
|
|
|
if (!found) { |
|
358
|
|
|
|
|
|
|
fprintf(stderr, "trying to delete timer id %d, but not found; ignoring\n", (int) timer_id); |
|
359
|
|
|
|
|
|
|
fflush(stderr); |
|
360
|
|
|
|
|
|
|
} |
|
361
|
|
|
|
|
|
|
#endif |
|
362
|
|
|
|
|
|
|
|
|
363
|
0
|
|
|
|
|
|
duk_push_boolean(ctx, found); |
|
364
|
0
|
|
|
|
|
|
return 1; |
|
365
|
|
|
|
|
|
|
} |
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
static duk_function_list_entry eventloop_funcs[] = { |
|
368
|
|
|
|
|
|
|
{ "createTimer", create_timer, 3 }, |
|
369
|
|
|
|
|
|
|
{ "deleteTimer", delete_timer, 1 }, |
|
370
|
|
|
|
|
|
|
{ NULL, NULL, 0 } |
|
371
|
|
|
|
|
|
|
}; |
|
372
|
|
|
|
|
|
|
|
|
373
|
263
|
|
|
|
|
|
void eventloop_register(duk_context *ctx) { |
|
374
|
263
|
|
|
|
|
|
memset((void *) timer_list, 0, MAX_TIMERS * sizeof(ev_timer)); |
|
375
|
263
|
|
|
|
|
|
memset((void *) &timer_expiring, 0, sizeof(ev_timer)); |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
/* Set global 'EventLoop'. */ |
|
378
|
263
|
|
|
|
|
|
duk_push_global_object(ctx); |
|
379
|
263
|
|
|
|
|
|
duk_push_object(ctx); |
|
380
|
263
|
|
|
|
|
|
duk_put_function_list(ctx, -1, eventloop_funcs); |
|
381
|
263
|
|
|
|
|
|
duk_put_prop_string(ctx, -2, "EventLoop"); |
|
382
|
263
|
|
|
|
|
|
duk_pop(ctx); |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
/* Initialize global stash 'eventTimers'. */ |
|
385
|
263
|
|
|
|
|
|
duk_push_global_stash(ctx); |
|
386
|
263
|
|
|
|
|
|
duk_push_object(ctx); |
|
387
|
263
|
|
|
|
|
|
duk_put_prop_string(ctx, -2, TIMERS_SLOT_NAME); |
|
388
|
263
|
|
|
|
|
|
duk_pop(ctx); |
|
389
|
263
|
|
|
|
|
|
} |