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
|
2000346
|
|
|
|
|
|
return NULL; |
51
|
|
|
|
|
|
|
} |
52
|
13
|
|
|
|
|
|
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
|
2000346
|
|
|
|
|
|
break; |
103
|
|
|
|
|
|
|
} |
104
|
24
|
|
|
|
|
|
t = timer_list + timer_count - 1; |
105
|
24
|
100
|
|
|
|
|
if (t->target > now) { |
106
|
13
|
|
|
|
|
|
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
|
2000346
|
|
|
|
|
|
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
|
13
|
|
|
|
|
|
diff = t->target - now; |
194
|
13
|
100
|
|
|
|
|
if (diff < MIN_WAIT) { |
195
|
9
|
|
|
|
|
|
diff = MIN_WAIT; |
196
|
4
|
50
|
|
|
|
|
} else if (diff > MAX_WAIT) { |
197
|
0
|
|
|
|
|
|
diff = MAX_WAIT; |
198
|
|
|
|
|
|
|
} |
199
|
13
|
|
|
|
|
|
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
|
2000346
|
|
|
|
|
|
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
|
13
|
|
|
|
|
|
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
|
13
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
2000346
|
|
|
|
|
|
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
|
262
|
|
|
|
|
|
void eventloop_register(duk_context *ctx) { |
374
|
262
|
|
|
|
|
|
memset((void *) timer_list, 0, MAX_TIMERS * sizeof(ev_timer)); |
375
|
262
|
|
|
|
|
|
memset((void *) &timer_expiring, 0, sizeof(ev_timer)); |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
/* Set global 'EventLoop'. */ |
378
|
262
|
|
|
|
|
|
duk_push_global_object(ctx); |
379
|
262
|
|
|
|
|
|
duk_push_object(ctx); |
380
|
262
|
|
|
|
|
|
duk_put_function_list(ctx, -1, eventloop_funcs); |
381
|
262
|
|
|
|
|
|
duk_put_prop_string(ctx, -2, "EventLoop"); |
382
|
262
|
|
|
|
|
|
duk_pop(ctx); |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
/* Initialize global stash 'eventTimers'. */ |
385
|
262
|
|
|
|
|
|
duk_push_global_stash(ctx); |
386
|
262
|
|
|
|
|
|
duk_push_object(ctx); |
387
|
262
|
|
|
|
|
|
duk_put_prop_string(ctx, -2, TIMERS_SLOT_NAME); |
388
|
262
|
|
|
|
|
|
duk_pop(ctx); |
389
|
262
|
|
|
|
|
|
} |