1
+ #include <time.h>
2
+ #include <sys/time.h>
1
3
4
+ #include "quickjs/quickjs-libc.h"
5
+ #include "quickjs/quickjs.h"
6
+ #include "cutils.h"
7
+ #include "list.h"
2
8
#include "runtime.h"
3
9
10
+ static uint64_t os_pending_signals ;
11
+ static int (* os_poll_func )(JSContext * ctx );
12
+
13
+ static JSClassID js_os_timer_class_id ;
14
+
15
+ typedef struct {
16
+ struct list_head link ;
17
+ int fd ;
18
+ JSValue rw_func [2 ];
19
+ } JSOSRWHandler ;
20
+
21
+ typedef struct {
22
+ struct list_head link ;
23
+ int sig_num ;
24
+ JSValue func ;
25
+ } JSOSSignalHandler ;
26
+
27
+ typedef struct {
28
+ struct list_head link ;
29
+ BOOL has_object ;
30
+ int64_t timeout ;
31
+ JSValue func ;
32
+ } JSOSTimer ;
33
+
34
+ static struct list_head os_rw_handlers = LIST_HEAD_INIT (os_rw_handlers );
35
+ static struct list_head os_signal_handlers = LIST_HEAD_INIT (os_signal_handlers );
36
+ static struct list_head os_timers = LIST_HEAD_INIT (os_timers );
37
+
38
+ static int64_t get_time_ms (void )
39
+ {
40
+ struct timespec ts ;
41
+ clock_gettime (CLOCK_MONOTONIC , & ts );
42
+ return (uint64_t )ts .tv_sec * 1000 + (ts .tv_nsec / 1000000 );
43
+ }
44
+
45
+ static void unlink_timer (JSRuntime * rt , JSOSTimer * th )
46
+ {
47
+ if (th -> link .prev ) {
48
+ list_del (& th -> link );
49
+ th -> link .prev = th -> link .next = NULL ;
50
+ }
51
+ }
52
+
53
+ static void free_timer (JSRuntime * rt , JSOSTimer * th )
54
+ {
55
+ JS_FreeValueRT (rt , th -> func );
56
+ js_free_rt (rt , th );
57
+ }
58
+
59
+ static void js_os_timer_finalizer (JSRuntime * rt , JSValue val )
60
+ {
61
+ JSOSTimer * th = JS_GetOpaque (val , js_os_timer_class_id );
62
+ if (th ) {
63
+ th -> has_object = FALSE;
64
+ if (!th -> link .prev )
65
+ free_timer (rt , th );
66
+ }
67
+ }
68
+
69
+ static void js_os_timer_mark (JSRuntime * rt , JSValueConst val ,
70
+ JS_MarkFunc * mark_func )
71
+ {
72
+ JSOSTimer * th = JS_GetOpaque (val , js_os_timer_class_id );
73
+ if (th ) {
74
+ JS_MarkValue (rt , th -> func , mark_func );
75
+ }
76
+ }
77
+
78
+ static JSValue js_os_setTimeout (JSContext * ctx , JSValueConst this_val ,
79
+ int argc , JSValueConst * argv )
80
+ {
81
+ int64_t delay ;
82
+ JSValueConst func ;
83
+ JSOSTimer * th ;
84
+ JSValue obj ;
85
+
86
+ func = argv [0 ];
87
+ if (!JS_IsFunction (ctx , func ))
88
+ return JS_ThrowTypeError (ctx , "not a function" );
89
+ if (JS_ToInt64 (ctx , & delay , argv [1 ]))
90
+ return JS_EXCEPTION ;
91
+ obj = JS_NewObjectClass (ctx , js_os_timer_class_id );
92
+ if (JS_IsException (obj ))
93
+ return obj ;
94
+ th = js_mallocz (ctx , sizeof (* th ));
95
+ if (!th ) {
96
+ JS_FreeValue (ctx , obj );
97
+ return JS_EXCEPTION ;
98
+ }
99
+ th -> has_object = TRUE;
100
+ th -> timeout = get_time_ms () + delay ;
101
+ th -> func = JS_DupValue (ctx , func );
102
+ list_add_tail (& th -> link , & os_timers );
103
+ JS_SetOpaque (obj , th );
104
+ return obj ;
105
+ }
106
+
107
+ static JSValue js_os_clearTimeout (JSContext * ctx , JSValueConst this_val ,
108
+ int argc , JSValueConst * argv )
109
+ {
110
+ JSOSTimer * th = JS_GetOpaque2 (ctx , argv [0 ], js_os_timer_class_id );
111
+ if (!th )
112
+ return JS_EXCEPTION ;
113
+ unlink_timer (JS_GetRuntime (ctx ), th );
114
+ return JS_UNDEFINED ;
115
+ }
116
+
117
+ static JSClassDef js_os_timer_class = {
118
+ "OSTimer" ,
119
+ .finalizer = js_os_timer_finalizer ,
120
+ .gc_mark = js_os_timer_mark ,
121
+ };
122
+
123
+ static void call_handler (JSContext * ctx , JSValueConst func )
124
+ {
125
+ JSValue ret , func1 ;
126
+ /* 'func' might be destroyed when calling itself (if it frees the
127
+ handler), so must take extra care */
128
+ func1 = JS_DupValue (ctx , func );
129
+ ret = JS_Call (ctx , func1 , JS_UNDEFINED , 0 , NULL );
130
+ JS_FreeValue (ctx , func1 );
131
+ if (JS_IsException (ret ))
132
+ js_std_dump_error (ctx );
133
+ JS_FreeValue (ctx , ret );
134
+ }
135
+
136
+ static int js_os_poll (JSContext * ctx )
137
+ {
138
+ int ret , fd_max , min_delay ;
139
+ int64_t cur_time , delay ;
140
+ fd_set rfds , wfds ;
141
+ JSOSRWHandler * rh ;
142
+ struct list_head * el ;
143
+ struct timeval tv , * tvp ;
144
+
145
+ if (unlikely (os_pending_signals != 0 )) {
146
+ JSOSSignalHandler * sh ;
147
+ uint64_t mask ;
148
+
149
+ list_for_each (el , & os_signal_handlers ) {
150
+ sh = list_entry (el , JSOSSignalHandler , link );
151
+ mask = (uint64_t )1 << sh -> sig_num ;
152
+ if (os_pending_signals & mask ) {
153
+ os_pending_signals &= ~mask ;
154
+ call_handler (ctx , sh -> func );
155
+ return 0 ;
156
+ }
157
+ }
158
+ }
159
+
160
+ if (list_empty (& os_rw_handlers ) && list_empty (& os_timers ))
161
+ return -1 ; /* no more events */
162
+
163
+ if (!list_empty (& os_timers )) {
164
+ cur_time = get_time_ms ();
165
+ min_delay = 10000 ;
166
+ list_for_each (el , & os_timers ) {
167
+ JSOSTimer * th = list_entry (el , JSOSTimer , link );
168
+ delay = th -> timeout - cur_time ;
169
+ if (delay <= 0 ) {
170
+ JSValue func ;
171
+ /* the timer expired */
172
+ func = th -> func ;
173
+ th -> func = JS_UNDEFINED ;
174
+ unlink_timer (JS_GetRuntime (ctx ), th );
175
+ if (!th -> has_object )
176
+ free_timer (JS_GetRuntime (ctx ), th );
177
+ call_handler (ctx , func );
178
+ JS_FreeValue (ctx , func );
179
+ return 0 ;
180
+ } else if (delay < min_delay ) {
181
+ min_delay = delay ;
182
+ }
183
+ }
184
+ tv .tv_sec = min_delay / 1000 ;
185
+ tv .tv_usec = (min_delay % 1000 ) * 1000 ;
186
+ tvp = & tv ;
187
+ } else {
188
+ tvp = NULL ;
189
+ }
190
+
191
+ FD_ZERO (& rfds );
192
+ FD_ZERO (& wfds );
193
+ fd_max = -1 ;
194
+ list_for_each (el , & os_rw_handlers ) {
195
+ rh = list_entry (el , JSOSRWHandler , link );
196
+ fd_max = max_int (fd_max , rh -> fd );
197
+ if (!JS_IsNull (rh -> rw_func [0 ]))
198
+ FD_SET (rh -> fd , & rfds );
199
+ if (!JS_IsNull (rh -> rw_func [1 ]))
200
+ FD_SET (rh -> fd , & wfds );
201
+ }
202
+
203
+ ret = select (fd_max + 1 , & rfds , & wfds , NULL , tvp );
204
+ if (ret > 0 ) {
205
+ list_for_each (el , & os_rw_handlers ) {
206
+ rh = list_entry (el , JSOSRWHandler , link );
207
+ if (!JS_IsNull (rh -> rw_func [0 ]) &&
208
+ FD_ISSET (rh -> fd , & rfds )) {
209
+ call_handler (ctx , rh -> rw_func [0 ]);
210
+ /* must stop because the list may have been modified */
211
+ break ;
212
+ }
213
+ if (!JS_IsNull (rh -> rw_func [1 ])) {
214
+ FD_SET (rh -> fd , & wfds );
215
+ call_handler (ctx , rh -> rw_func [1 ]);
216
+ /* must stop because the list may have been modified */
217
+ break ;
218
+ }
219
+ }
220
+ }
221
+ return 0 ;
222
+ }
223
+
224
+ static const JSCFunctionListEntry js_os_funcs [] = {
225
+ JS_CFUNC_DEF ("setTimeout" , 2 , js_os_setTimeout ),
226
+ JS_CFUNC_DEF ("clearTimeout" , 1 , js_os_clearTimeout ),
227
+ };
228
+
229
+ static int js_os_init (JSContext * ctx , JSModuleDef * m )
230
+ {
231
+ os_poll_func = js_os_poll ;
232
+
233
+ /* OSTimer class */
234
+ JS_NewClassID (& js_os_timer_class_id );
235
+ JS_NewClass (JS_GetRuntime (ctx ), js_os_timer_class_id , & js_os_timer_class );
236
+
237
+ return JS_SetModuleExportList (ctx , m , js_os_funcs ,
238
+ countof (js_os_funcs ));
239
+ }
240
+
241
+ JSModuleDef * js_init_module_my_os (JSContext * ctx , const char * module_name )
242
+ {
243
+ JSModuleDef * m ;
244
+ m = JS_NewCModule (ctx , module_name , js_os_init );
245
+ if (!m )
246
+ return NULL ;
247
+ JS_AddModuleExportList (ctx , m , js_os_funcs , countof (js_os_funcs ));
248
+ return m ;
249
+ }
250
+
4
251
void js_rt_loop (JSContext * ctx )
5
252
{
6
253
JSContext * ctx1 ;
@@ -16,5 +263,8 @@ void js_rt_loop(JSContext *ctx)
16
263
break ;
17
264
}
18
265
}
266
+
267
+ if (!os_poll_func || os_poll_func (ctx ))
268
+ break ;
19
269
}
20
- }
270
+ }
0 commit comments