mirror of
				https://github.com/bellard/quickjs.git
				synced 2025-05-29 01:49:18 +08:00 
			
		
		
		
	fixed and simplified setTimeout() by using an integer timer handle (github issue #218)
This commit is contained in:
		
							parent
							
								
									84058766e9
								
							
						
					
					
						commit
						9e561d5c2e
					
				@ -89,7 +89,7 @@ typedef struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    struct list_head link;
 | 
					    struct list_head link;
 | 
				
			||||||
    BOOL has_object;
 | 
					    int timer_id;
 | 
				
			||||||
    int64_t timeout;
 | 
					    int64_t timeout;
 | 
				
			||||||
    JSValue func;
 | 
					    JSValue func;
 | 
				
			||||||
} JSOSTimer;
 | 
					} JSOSTimer;
 | 
				
			||||||
@ -125,6 +125,7 @@ typedef struct JSThreadState {
 | 
				
			|||||||
    struct list_head os_timers; /* list of JSOSTimer.link */
 | 
					    struct list_head os_timers; /* list of JSOSTimer.link */
 | 
				
			||||||
    struct list_head port_list; /* list of JSWorkerMessageHandler.link */
 | 
					    struct list_head port_list; /* list of JSWorkerMessageHandler.link */
 | 
				
			||||||
    int eval_script_recurse; /* only used in the main thread */
 | 
					    int eval_script_recurse; /* only used in the main thread */
 | 
				
			||||||
 | 
					    int next_timer_id; /* for setTimeout() */
 | 
				
			||||||
    /* not used in the main thread */
 | 
					    /* not used in the main thread */
 | 
				
			||||||
    JSWorkerMessagePipe *recv_pipe, *send_pipe;
 | 
					    JSWorkerMessagePipe *recv_pipe, *send_pipe;
 | 
				
			||||||
} JSThreadState;
 | 
					} JSThreadState;
 | 
				
			||||||
@ -2006,41 +2007,13 @@ static JSValue js_os_now(JSContext *ctx, JSValue this_val,
 | 
				
			|||||||
    return JS_NewFloat64(ctx, (double)get_time_ns() / 1e6);
 | 
					    return JS_NewFloat64(ctx, (double)get_time_ns() / 1e6);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void unlink_timer(JSRuntime *rt, JSOSTimer *th)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    if (th->link.prev) {
 | 
					 | 
				
			||||||
        list_del(&th->link);
 | 
					 | 
				
			||||||
        th->link.prev = th->link.next = NULL;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void free_timer(JSRuntime *rt, JSOSTimer *th)
 | 
					static void free_timer(JSRuntime *rt, JSOSTimer *th)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    list_del(&th->link);
 | 
				
			||||||
    JS_FreeValueRT(rt, th->func);
 | 
					    JS_FreeValueRT(rt, th->func);
 | 
				
			||||||
    js_free_rt(rt, th);
 | 
					    js_free_rt(rt, th);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static JSClassID js_os_timer_class_id;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void js_os_timer_finalizer(JSRuntime *rt, JSValue val)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id);
 | 
					 | 
				
			||||||
    if (th) {
 | 
					 | 
				
			||||||
        th->has_object = FALSE;
 | 
					 | 
				
			||||||
        if (!th->link.prev)
 | 
					 | 
				
			||||||
            free_timer(rt, th);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void js_os_timer_mark(JSRuntime *rt, JSValueConst val,
 | 
					 | 
				
			||||||
                             JS_MarkFunc *mark_func)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id);
 | 
					 | 
				
			||||||
    if (th) {
 | 
					 | 
				
			||||||
        JS_MarkValue(rt, th->func, mark_func);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val,
 | 
					static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val,
 | 
				
			||||||
                                int argc, JSValueConst *argv)
 | 
					                                int argc, JSValueConst *argv)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -2049,45 +2022,56 @@ static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val,
 | 
				
			|||||||
    int64_t delay;
 | 
					    int64_t delay;
 | 
				
			||||||
    JSValueConst func;
 | 
					    JSValueConst func;
 | 
				
			||||||
    JSOSTimer *th;
 | 
					    JSOSTimer *th;
 | 
				
			||||||
    JSValue obj;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    func = argv[0];
 | 
					    func = argv[0];
 | 
				
			||||||
    if (!JS_IsFunction(ctx, func))
 | 
					    if (!JS_IsFunction(ctx, func))
 | 
				
			||||||
        return JS_ThrowTypeError(ctx, "not a function");
 | 
					        return JS_ThrowTypeError(ctx, "not a function");
 | 
				
			||||||
    if (JS_ToInt64(ctx, &delay, argv[1]))
 | 
					    if (JS_ToInt64(ctx, &delay, argv[1]))
 | 
				
			||||||
        return JS_EXCEPTION;
 | 
					        return JS_EXCEPTION;
 | 
				
			||||||
    obj = JS_NewObjectClass(ctx, js_os_timer_class_id);
 | 
					 | 
				
			||||||
    if (JS_IsException(obj))
 | 
					 | 
				
			||||||
        return obj;
 | 
					 | 
				
			||||||
    th = js_mallocz(ctx, sizeof(*th));
 | 
					    th = js_mallocz(ctx, sizeof(*th));
 | 
				
			||||||
    if (!th) {
 | 
					    if (!th)
 | 
				
			||||||
        JS_FreeValue(ctx, obj);
 | 
					 | 
				
			||||||
        return JS_EXCEPTION;
 | 
					        return JS_EXCEPTION;
 | 
				
			||||||
    }
 | 
					    th->timer_id = ts->next_timer_id;
 | 
				
			||||||
    th->has_object = TRUE;
 | 
					    if (ts->next_timer_id == INT32_MAX)
 | 
				
			||||||
 | 
					        ts->next_timer_id = 1;
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        ts->next_timer_id++;
 | 
				
			||||||
    th->timeout = get_time_ms() + delay;
 | 
					    th->timeout = get_time_ms() + delay;
 | 
				
			||||||
    th->func = JS_DupValue(ctx, func);
 | 
					    th->func = JS_DupValue(ctx, func);
 | 
				
			||||||
    list_add_tail(&th->link, &ts->os_timers);
 | 
					    list_add_tail(&th->link, &ts->os_timers);
 | 
				
			||||||
    JS_SetOpaque(obj, th);
 | 
					    return JS_NewInt32(ctx, th->timer_id);
 | 
				
			||||||
    return obj;
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static JSOSTimer *find_timer_by_id(JSThreadState *ts, int timer_id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    struct list_head *el;
 | 
				
			||||||
 | 
					    if (timer_id <= 0)
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    list_for_each(el, &ts->os_timers) {
 | 
				
			||||||
 | 
					        JSOSTimer *th = list_entry(el, JSOSTimer, link);
 | 
				
			||||||
 | 
					        if (th->timer_id == timer_id)
 | 
				
			||||||
 | 
					            return th;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val,
 | 
					static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val,
 | 
				
			||||||
                                  int argc, JSValueConst *argv)
 | 
					                                  int argc, JSValueConst *argv)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    JSOSTimer *th = JS_GetOpaque2(ctx, argv[0], js_os_timer_class_id);
 | 
					    JSRuntime *rt = JS_GetRuntime(ctx);
 | 
				
			||||||
    if (!th)
 | 
					    JSThreadState *ts = JS_GetRuntimeOpaque(rt);
 | 
				
			||||||
 | 
					    JSOSTimer *th;
 | 
				
			||||||
 | 
					    int timer_id;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (JS_ToInt32(ctx, &timer_id, argv[0]))
 | 
				
			||||||
        return JS_EXCEPTION;
 | 
					        return JS_EXCEPTION;
 | 
				
			||||||
    unlink_timer(JS_GetRuntime(ctx), th);
 | 
					    th = find_timer_by_id(ts, timer_id);
 | 
				
			||||||
 | 
					    if (!th)
 | 
				
			||||||
 | 
					        return JS_UNDEFINED;
 | 
				
			||||||
 | 
					    free_timer(rt, th);
 | 
				
			||||||
    return JS_UNDEFINED;
 | 
					    return JS_UNDEFINED;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static JSClassDef js_os_timer_class = {
 | 
					 | 
				
			||||||
    "OSTimer",
 | 
					 | 
				
			||||||
    .finalizer = js_os_timer_finalizer,
 | 
					 | 
				
			||||||
    .gc_mark = js_os_timer_mark,
 | 
					 | 
				
			||||||
}; 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* return a promise */
 | 
					/* return a promise */
 | 
				
			||||||
static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val,
 | 
					static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val,
 | 
				
			||||||
                                int argc, JSValueConst *argv)
 | 
					                                int argc, JSValueConst *argv)
 | 
				
			||||||
@ -2111,7 +2095,7 @@ static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val,
 | 
				
			|||||||
        JS_FreeValue(ctx, resolving_funcs[1]);
 | 
					        JS_FreeValue(ctx, resolving_funcs[1]);
 | 
				
			||||||
        return JS_EXCEPTION;
 | 
					        return JS_EXCEPTION;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    th->has_object = FALSE;
 | 
					    th->timer_id = -1;
 | 
				
			||||||
    th->timeout = get_time_ms() + delay;
 | 
					    th->timeout = get_time_ms() + delay;
 | 
				
			||||||
    th->func = JS_DupValue(ctx, resolving_funcs[0]);
 | 
					    th->func = JS_DupValue(ctx, resolving_funcs[0]);
 | 
				
			||||||
    list_add_tail(&th->link, &ts->os_timers);
 | 
					    list_add_tail(&th->link, &ts->os_timers);
 | 
				
			||||||
@ -2161,8 +2145,6 @@ static int js_os_poll(JSContext *ctx)
 | 
				
			|||||||
                /* the timer expired */
 | 
					                /* the timer expired */
 | 
				
			||||||
                func = th->func;
 | 
					                func = th->func;
 | 
				
			||||||
                th->func = JS_UNDEFINED;
 | 
					                th->func = JS_UNDEFINED;
 | 
				
			||||||
                unlink_timer(rt, th);
 | 
					 | 
				
			||||||
                if (!th->has_object)
 | 
					 | 
				
			||||||
                free_timer(rt, th);
 | 
					                free_timer(rt, th);
 | 
				
			||||||
                call_handler(ctx, func);
 | 
					                call_handler(ctx, func);
 | 
				
			||||||
                JS_FreeValue(ctx, func);
 | 
					                JS_FreeValue(ctx, func);
 | 
				
			||||||
@ -2330,8 +2312,6 @@ static int js_os_poll(JSContext *ctx)
 | 
				
			|||||||
                /* the timer expired */
 | 
					                /* the timer expired */
 | 
				
			||||||
                func = th->func;
 | 
					                func = th->func;
 | 
				
			||||||
                th->func = JS_UNDEFINED;
 | 
					                th->func = JS_UNDEFINED;
 | 
				
			||||||
                unlink_timer(rt, th);
 | 
					 | 
				
			||||||
                if (!th->has_object)
 | 
					 | 
				
			||||||
                free_timer(rt, th);
 | 
					                free_timer(rt, th);
 | 
				
			||||||
                call_handler(ctx, func);
 | 
					                call_handler(ctx, func);
 | 
				
			||||||
                JS_FreeValue(ctx, func);
 | 
					                JS_FreeValue(ctx, func);
 | 
				
			||||||
@ -3735,10 +3715,6 @@ static int js_os_init(JSContext *ctx, JSModuleDef *m)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    os_poll_func = js_os_poll;
 | 
					    os_poll_func = js_os_poll;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    /* OSTimer class */
 | 
					 | 
				
			||||||
    JS_NewClassID(&js_os_timer_class_id);
 | 
					 | 
				
			||||||
    JS_NewClass(JS_GetRuntime(ctx), js_os_timer_class_id, &js_os_timer_class);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef USE_WORKER
 | 
					#ifdef USE_WORKER
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        JSRuntime *rt = JS_GetRuntime(ctx);
 | 
					        JSRuntime *rt = JS_GetRuntime(ctx);
 | 
				
			||||||
@ -3850,6 +3826,7 @@ void js_std_init_handlers(JSRuntime *rt)
 | 
				
			|||||||
    init_list_head(&ts->os_signal_handlers);
 | 
					    init_list_head(&ts->os_signal_handlers);
 | 
				
			||||||
    init_list_head(&ts->os_timers);
 | 
					    init_list_head(&ts->os_timers);
 | 
				
			||||||
    init_list_head(&ts->port_list);
 | 
					    init_list_head(&ts->port_list);
 | 
				
			||||||
 | 
					    ts->next_timer_id = 1;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    JS_SetRuntimeOpaque(rt, ts);
 | 
					    JS_SetRuntimeOpaque(rt, ts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -3883,8 +3860,6 @@ void js_std_free_handlers(JSRuntime *rt)
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
    list_for_each_safe(el, el1, &ts->os_timers) {
 | 
					    list_for_each_safe(el, el1, &ts->os_timers) {
 | 
				
			||||||
        JSOSTimer *th = list_entry(el, JSOSTimer, link);
 | 
					        JSOSTimer *th = list_entry(el, JSOSTimer, link);
 | 
				
			||||||
        unlink_timer(rt, th);
 | 
					 | 
				
			||||||
        if (!th->has_object)
 | 
					 | 
				
			||||||
        free_timer(rt, th);
 | 
					        free_timer(rt, th);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user