Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F4445033
timer.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
27 KB
Referenced Files
None
Subscribers
None
timer.c
View Options
#include
<compat.h>
// IWYU pragma: keep
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<time.h>
#include
<uv.h>
#include
"errors.h"
#include
"runtime.h"
#include
"internal.h"
#include
"silver/engine.h"
#include
"gc/roots.h"
#include
"gc/modules.h"
#include
"modules/abort.h"
#include
"modules/timer.h"
#include
"modules/symbol.h"
typedef
struct
timer_entry
{
uv_timer_t
handle
;
ant_value_t
callback
;
ant_value_t
*
args
;
int
nargs
;
int
timer_id
;
int
active
;
int
closed
;
int
is_interval
;
struct
timer_entry
*
next
;
struct
timer_entry
*
prev
;
}
timer_entry_t
;
typedef
struct
microtask_entry
{
ant_value_t
callback
;
ant_value_t
promise
;
struct
microtask_entry
*
next
;
uint8_t
argc
;
ant_value_t
argv
[];
}
microtask_entry_t
;
typedef
struct
immediate_entry
{
ant_value_t
callback
;
int
immediate_id
;
int
active
;
struct
immediate_entry
*
next
;
}
immediate_entry_t
;
static
struct
{
ant_t
*
js
;
timer_entry_t
*
timers
;
microtask_entry_t
*
next_ticks
;
microtask_entry_t
*
next_ticks_tail
;
microtask_entry_t
*
next_ticks_processing
;
microtask_entry_t
*
microtasks
;
microtask_entry_t
*
microtasks_tail
;
microtask_entry_t
*
microtasks_processing
;
immediate_entry_t
*
immediates
;
immediate_entry_t
*
immediates_tail
;
int
next_timer_id
;
int
next_immediate_id
;
int
active_timer_count
;
}
timer_state
=
{
.
js
=
NULL
,
.
timers
=
NULL
,
.
next_ticks
=
NULL
,
.
next_ticks_tail
=
NULL
,
.
next_ticks_processing
=
NULL
,
.
microtasks
=
NULL
,
.
microtasks_tail
=
NULL
,
.
microtasks_processing
=
NULL
,
.
immediates
=
NULL
,
.
immediates_tail
=
NULL
,
.
next_timer_id
=
1
,
.
next_immediate_id
=
1
,
.
active_timer_count
=
0
,
};
static
ant_value_t
g_timeout_proto
=
0
;
static
ant_value_t
g_interval_proto
=
0
;
static
void
add_timer_entry
(
timer_entry_t
*
entry
)
{
entry
->
next
=
timer_state
.
timers
;
entry
->
prev
=
NULL
;
if
(
timer_state
.
timers
)
timer_state
.
timers
->
prev
=
entry
;
timer_state
.
timers
=
entry
;
}
static
void
remove_timer_entry
(
timer_entry_t
*
entry
)
{
if
(
entry
->
prev
)
entry
->
prev
->
next
=
entry
->
next
;
else
timer_state
.
timers
=
entry
->
next
;
if
(
entry
->
next
)
entry
->
next
->
prev
=
entry
->
prev
;
}
static
int
timer_entry_is_registered
(
timer_entry_t
*
entry
)
{
for
(
timer_entry_t
*
it
=
timer_state
.
timers
;
it
!=
NULL
;
it
=
it
->
next
)
if
(
it
==
entry
)
return
1
;
return
0
;
}
static
timer_entry_t
*
find_timer_entry_by_id
(
int
timer_id
)
{
for
(
timer_entry_t
*
entry
=
timer_state
.
timers
;
entry
!=
NULL
;
entry
=
entry
->
next
)
if
(
entry
->
timer_id
==
timer_id
)
return
entry
;
return
NULL
;
}
static
int
timer_copy_args
(
timer_entry_t
*
entry
,
ant_value_t
*
args
,
int
nargs
)
{
entry
->
nargs
=
nargs
>
2
?
nargs
-
2
:
0
;
if
(
entry
->
nargs
>
0
)
{
entry
->
args
=
ant_calloc
(
sizeof
(
ant_value_t
)
*
entry
->
nargs
);
if
(
!
entry
->
args
)
return
-1
;
memcpy
(
entry
->
args
,
args
+
2
,
sizeof
(
ant_value_t
)
*
entry
->
nargs
);
}
else
entry
->
args
=
NULL
;
return
0
;
}
static
ant_value_t
timer_to_primitive
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
js_get_slot
(
js_getthis
(
js
),
SLOT_DATA
);
}
static
ant_value_t
timer_inspect
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
ant_value_t
id_val
=
js_get_slot
(
this_obj
,
SLOT_DATA
);
int
timer_id
=
vtype
(
id_val
)
==
T_NUM
?
(
int
)
js_getnum
(
id_val
)
:
0
;
ant_value_t
tag_val
=
js_get_sym
(
js
,
this_obj
,
get_toStringTag_sym
());
const
char
*
tag
=
vtype
(
tag_val
)
==
T_STR
?
js_getstr
(
js
,
tag_val
,
NULL
)
:
"Timeout"
;
js_inspect_builder_t
builder
;
if
(
!
js_inspect_builder_init_dynamic
(
&
builder
,
js
,
128
))
{
return
js_mkerr
(
js
,
"out of memory"
);
}
bool
ok
=
js_inspect_header
(
&
builder
,
"%s (%d)"
,
tag
,
timer_id
);
if
(
ok
)
ok
=
js_inspect_object_body
(
&
builder
,
this_obj
);
if
(
ok
)
ok
=
js_inspect_close
(
&
builder
);
if
(
!
ok
)
{
js_inspect_builder_dispose
(
&
builder
);
return
js_mkerr
(
js
,
"out of memory"
);
}
return
js_inspect_builder_result
(
&
builder
);
}
static
ant_value_t
js_timer_ref
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
timer_entry_t
*
entry
=
find_timer_entry_by_id
((
int
)
js_getnum
(
js_get_slot
(
this_obj
,
SLOT_DATA
)));
if
(
entry
&&
!
entry
->
closed
&&
!
uv_is_closing
((
uv_handle_t
*
)
&
entry
->
handle
))
uv_ref
((
uv_handle_t
*
)
&
entry
->
handle
);
return
this_obj
;
}
static
ant_value_t
js_timer_unref
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_obj
=
js_getthis
(
js
);
timer_entry_t
*
entry
=
find_timer_entry_by_id
((
int
)
js_getnum
(
js_get_slot
(
this_obj
,
SLOT_DATA
)));
if
(
entry
&&
!
entry
->
closed
&&
!
uv_is_closing
((
uv_handle_t
*
)
&
entry
->
handle
))
uv_unref
((
uv_handle_t
*
)
&
entry
->
handle
);
return
this_obj
;
}
static
ant_value_t
js_timer_has_ref
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
timer_entry_t
*
entry
=
find_timer_entry_by_id
((
int
)
js_getnum
(
js_get_slot
(
js_getthis
(
js
),
SLOT_DATA
)));
if
(
!
entry
||
entry
->
closed
||
uv_is_closing
((
uv_handle_t
*
)
&
entry
->
handle
))
return
js_false
;
return
js_bool
(
uv_has_ref
((
const
uv_handle_t
*
)
&
entry
->
handle
)
!=
0
);
}
static
ant_value_t
timer_make_object
(
ant_t
*
js
,
int
id
,
double
delay_ms
,
int
is_interval
,
ant_value_t
callback
)
{
ant_value_t
obj
=
js_mkobj
(
js
);
ant_value_t
proto
=
is_interval
?
g_interval_proto
:
g_timeout_proto
;
if
(
is_object_type
(
proto
))
js_set_proto_init
(
obj
,
proto
);
js_set
(
js
,
obj
,
"delay"
,
js_mknum
(
delay_ms
));
js_set
(
js
,
obj
,
"repeat"
,
is_interval
?
js_mknum
(
delay_ms
)
:
js_mknull
());
js_set
(
js
,
obj
,
"callback"
,
callback
);
js_set_descriptor
(
js
,
obj
,
"callback"
,
8
,
JS_DESC_W
|
JS_DESC_C
);
js_set_slot
(
obj
,
SLOT_DATA
,
js_mknum
((
double
)
id
));
js_set_sym
(
js
,
obj
,
get_toPrimitive_sym
(),
js_mkfun
(
timer_to_primitive
));
return
obj
;
}
static
int
timer_id_from_arg
(
ant_t
*
js
,
ant_value_t
arg
)
{
if
(
vtype
(
arg
)
==
T_NUM
)
return
(
int
)
js_getnum
(
arg
);
return
(
int
)
js_getnum
(
js_get_slot
(
arg
,
SLOT_DATA
));
}
static
void
timer_close_cb
(
uv_handle_t
*
h
)
{
timer_entry_t
*
entry
=
(
timer_entry_t
*
)
h
->
data
;
if
(
!
entry
)
return
;
if
(
entry
->
closed
)
return
;
if
(
timer_entry_is_registered
(
entry
))
remove_timer_entry
(
entry
);
entry
->
closed
=
1
;
entry
->
active
=
0
;
entry
->
callback
=
0
;
entry
->
next
=
NULL
;
entry
->
prev
=
NULL
;
if
(
entry
->
args
)
{
free
(
entry
->
args
);
entry
->
args
=
NULL
;
}
entry
->
nargs
=
0
;
}
static
void
timer_callback
(
uv_timer_t
*
handle
)
{
timer_entry_t
*
entry
=
(
timer_entry_t
*
)
handle
->
data
;
if
(
!
entry
||
entry
->
closed
||
!
timer_entry_is_registered
(
entry
)
||
!
entry
->
active
)
return
;
ant_t
*
js
=
timer_state
.
js
;
if
(
!
entry
->
is_interval
)
{
entry
->
active
=
0
;
timer_state
.
active_timer_count
--
;
}
sv_vm_call
(
js
->
vm
,
js
,
entry
->
callback
,
js_mkundef
(),
entry
->
args
,
entry
->
nargs
,
NULL
,
false
);
process_microtasks
(
js
);
if
(
!
entry
->
is_interval
)
if
(
!
uv_is_closing
((
uv_handle_t
*
)
&
entry
->
handle
))
uv_close
(
(
uv_handle_t
*
)
&
entry
->
handle
,
timer_close_cb
);
}
// setTimeout(callback, delay, ...args)
static
ant_value_t
js_set_timeout
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
{
return
js_mkerr
(
js
,
"setTimeout requires at least 1 argument (callback)"
);
}
ant_value_t
callback
=
args
[
0
];
double
delay_ms
=
nargs
>
1
?
js_getnum
(
args
[
1
])
:
0
;
uint64_t
ms
=
delay_ms
>=
1
?
(
uint64_t
)
delay_ms
:
0
;
timer_entry_t
*
entry
=
ant_calloc
(
sizeof
(
timer_entry_t
));
if
(
entry
==
NULL
)
return
js_mkerr
(
js
,
"failed to allocate timer"
);
if
(
timer_copy_args
(
entry
,
args
,
nargs
)
<
0
)
{
free
(
entry
);
return
js_mkerr
(
js
,
"failed to allocate timer args"
);
}
uv_timer_init
(
uv_default_loop
(),
&
entry
->
handle
);
entry
->
handle
.
data
=
entry
;
entry
->
callback
=
callback
;
entry
->
timer_id
=
timer_state
.
next_timer_id
++
;
entry
->
active
=
1
;
entry
->
closed
=
0
;
entry
->
is_interval
=
0
;
add_timer_entry
(
entry
);
timer_state
.
active_timer_count
++
;
uv_timer_start
(
&
entry
->
handle
,
timer_callback
,
ms
,
0
);
return
timer_make_object
(
js
,
entry
->
timer_id
,
delay_ms
,
0
,
callback
);
}
// setInterval(callback, delay, ...args)
static
ant_value_t
js_set_interval
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
{
return
js_mkerr
(
js
,
"setInterval requires at least 1 argument (callback)"
);
}
ant_value_t
callback
=
args
[
0
];
double
delay_ms
=
nargs
>
1
?
js_getnum
(
args
[
1
])
:
0
;
uint64_t
ms
=
delay_ms
>=
1
?
(
uint64_t
)
delay_ms
:
1
;
timer_entry_t
*
entry
=
ant_calloc
(
sizeof
(
timer_entry_t
));
if
(
entry
==
NULL
)
return
js_mkerr
(
js
,
"failed to allocate timer"
);
if
(
timer_copy_args
(
entry
,
args
,
nargs
)
<
0
)
{
free
(
entry
);
return
js_mkerr
(
js
,
"failed to allocate timer args"
);
}
uv_timer_init
(
uv_default_loop
(),
&
entry
->
handle
);
entry
->
handle
.
data
=
entry
;
entry
->
callback
=
callback
;
entry
->
timer_id
=
timer_state
.
next_timer_id
++
;
entry
->
active
=
1
;
entry
->
closed
=
0
;
entry
->
is_interval
=
1
;
add_timer_entry
(
entry
);
timer_state
.
active_timer_count
++
;
uv_timer_start
(
&
entry
->
handle
,
timer_callback
,
ms
,
ms
);
return
timer_make_object
(
js
,
entry
->
timer_id
,
delay_ms
,
1
,
callback
);
}
// clearTimeout(timerId | timerObject)
static
ant_value_t
js_clear_timeout
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkundef
();
int
timer_id
=
timer_id_from_arg
(
js
,
args
[
0
]);
for
(
timer_entry_t
*
entry
=
timer_state
.
timers
;
entry
!=
NULL
;
entry
=
entry
->
next
)
{
if
(
entry
->
timer_id
==
timer_id
&&
entry
->
active
)
{
entry
->
active
=
0
;
timer_state
.
active_timer_count
--
;
if
(
!
uv_is_closing
((
uv_handle_t
*
)
&
entry
->
handle
))
uv_close
((
uv_handle_t
*
)
&
entry
->
handle
,
timer_close_cb
);
break
;
}}
return
js_mkundef
();
}
// setImmediate(callback)
static
ant_value_t
js_set_immediate
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
{
return
js_mkerr
(
js
,
"setImmediate requires 1 argument (callback)"
);
}
ant_value_t
callback
=
args
[
0
];
immediate_entry_t
*
entry
=
ant_calloc
(
sizeof
(
immediate_entry_t
));
if
(
entry
==
NULL
)
{
return
js_mkerr
(
js
,
"failed to allocate immediate"
);
}
entry
->
callback
=
callback
;
entry
->
immediate_id
=
timer_state
.
next_immediate_id
++
;
entry
->
active
=
1
;
entry
->
next
=
NULL
;
if
(
timer_state
.
immediates_tail
==
NULL
)
{
timer_state
.
immediates
=
entry
;
timer_state
.
immediates_tail
=
entry
;
}
else
{
timer_state
.
immediates_tail
->
next
=
entry
;
timer_state
.
immediates_tail
=
entry
;
}
ant_value_t
obj
=
js_mkobj
(
js
);
js_set
(
js
,
obj
,
"id"
,
js_mknum
((
double
)
entry
->
immediate_id
));
js_set
(
js
,
obj
,
"callback"
,
callback
);
return
obj
;
}
// clearImmediate(immediateId | immediateObject)
static
ant_value_t
js_clear_immediate
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkundef
();
int
immediate_id
=
timer_id_from_arg
(
js
,
args
[
0
]);
for
(
immediate_entry_t
*
entry
=
timer_state
.
immediates
;
entry
!=
NULL
;
entry
=
entry
->
next
)
{
if
(
entry
->
immediate_id
==
immediate_id
)
{
entry
->
active
=
0
;
break
;
}
}
return
js_mkundef
();
}
// queueMicrotask(callback)
static
ant_value_t
js_queue_microtask
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
{
return
js_mkerr
(
js
,
"queueMicrotask requires 1 argument (callback)"
);
}
queue_microtask
(
js
,
args
[
0
]);
return
js_mkundef
();
}
static
ant_value_t
timers_promises_get_state
(
ant_t
*
js
)
{
return
js_get_slot
(
js
->
current_func
,
SLOT_DATA
);
}
static
ant_value_t
timers_promises_abort_reason
(
ant_t
*
js
,
ant_value_t
signal
)
{
ant_value_t
reason
=
abort_signal_get_reason
(
signal
);
if
(
vtype
(
reason
)
!=
T_UNDEF
&&
vtype
(
reason
)
!=
T_NULL
)
return
reason
;
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"The operation was aborted"
);
}
static
void
timers_promises_remove_abort_listener
(
ant_t
*
js
,
ant_value_t
state
)
{
ant_value_t
signal
=
0
;
ant_value_t
listener
=
0
;
if
(
!
is_object_type
(
state
))
return
;
signal
=
js_get
(
js
,
state
,
"signal"
);
listener
=
js_get
(
js
,
state
,
"abortListener"
);
if
(
abort_signal_is_signal
(
signal
)
&&
is_callable
(
listener
))
abort_signal_remove_listener
(
js
,
signal
,
listener
);
js_set
(
js
,
state
,
"abortListener"
,
js_mkundef
());
}
static
void
timers_promises_settle
(
ant_t
*
js
,
ant_value_t
state
,
bool
reject
,
ant_value_t
value
)
{
ant_value_t
settled
=
0
;
ant_value_t
promise
=
0
;
if
(
!
is_object_type
(
state
))
return
;
settled
=
js_get
(
js
,
state
,
"settled"
);
if
(
js_truthy
(
js
,
settled
))
return
;
js_set
(
js
,
state
,
"settled"
,
js_true
);
timers_promises_remove_abort_listener
(
js
,
state
);
js_set
(
js
,
state
,
"handle"
,
js_mkundef
());
promise
=
js_get
(
js
,
state
,
"promise"
);
if
(
reject
)
js_reject_promise
(
js
,
promise
,
value
);
else
js_resolve_promise
(
js
,
promise
,
value
);
}
static
ant_value_t
timers_promises_resolve
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
timers_promises_get_state
(
js
);
ant_value_t
value
=
js_mkundef
();
if
(
!
is_object_type
(
state
))
return
js_mkundef
();
value
=
js_get
(
js
,
state
,
"value"
);
timers_promises_settle
(
js
,
state
,
false
,
value
);
return
js_mkundef
();
}
static
ant_value_t
timers_promises_on_abort
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
timers_promises_get_state
(
js
);
ant_value_t
signal
=
0
;
ant_value_t
handle
=
0
;
ant_value_t
is_immediate
=
0
;
ant_value_t
reason
=
js_mkundef
();
ant_value_t
clear_args
[
1
];
if
(
!
is_object_type
(
state
))
return
js_mkundef
();
signal
=
js_get
(
js
,
state
,
"signal"
);
handle
=
js_get
(
js
,
state
,
"handle"
);
is_immediate
=
js_get
(
js
,
state
,
"isImmediate"
);
if
(
vtype
(
handle
)
!=
T_UNDEF
&&
vtype
(
handle
)
!=
T_NULL
)
{
clear_args
[
0
]
=
handle
;
if
(
js_truthy
(
js
,
is_immediate
))
js_clear_immediate
(
js
,
clear_args
,
1
);
else
js_clear_timeout
(
js
,
clear_args
,
1
);
}
if
(
abort_signal_is_signal
(
signal
))
reason
=
timers_promises_abort_reason
(
js
,
signal
);
else
reason
=
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"The operation was aborted"
);
timers_promises_settle
(
js
,
state
,
true
,
reason
);
return
js_mkundef
();
}
static
bool
timers_promises_parse_options
(
ant_t
*
js
,
ant_value_t
value
,
ant_value_t
*
signal_out
,
ant_value_t
*
error_out
)
{
ant_value_t
signal
=
js_mkundef
();
if
(
signal_out
)
*
signal_out
=
js_mkundef
();
if
(
error_out
)
*
error_out
=
js_mkundef
();
if
(
vtype
(
value
)
==
T_UNDEF
||
vtype
(
value
)
==
T_NULL
)
return
true
;
if
(
vtype
(
value
)
!=
T_OBJ
)
{
if
(
error_out
)
*
error_out
=
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Timer options must be an object"
);
return
false
;
}
signal
=
js_get
(
js
,
value
,
"signal"
);
if
(
vtype
(
signal
)
!=
T_UNDEF
&&
vtype
(
signal
)
!=
T_NULL
&&
!
abort_signal_is_signal
(
signal
))
{
if
(
error_out
)
*
error_out
=
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"options.signal must be an AbortSignal"
);
return
false
;
}
if
(
signal_out
)
*
signal_out
=
signal
;
return
true
;
}
static
ant_value_t
timers_promises_schedule
(
ant_t
*
js
,
double
delay_ms
,
ant_value_t
value
,
ant_value_t
signal
,
bool
is_immediate
)
{
ant_value_t
promise
=
js_mkpromise
(
js
);
ant_value_t
state
=
js_mkobj
(
js
);
ant_value_t
callback
=
0
;
ant_value_t
handle
=
0
;
ant_value_t
args
[
2
];
if
(
abort_signal_is_signal
(
signal
)
&&
abort_signal_is_aborted
(
signal
))
{
js_reject_promise
(
js
,
promise
,
timers_promises_abort_reason
(
js
,
signal
));
return
promise
;
}
js_set
(
js
,
state
,
"promise"
,
promise
);
js_set
(
js
,
state
,
"value"
,
value
);
js_set
(
js
,
state
,
"signal"
,
signal
);
js_set
(
js
,
state
,
"abortListener"
,
js_mkundef
());
js_set
(
js
,
state
,
"handle"
,
js_mkundef
());
js_set
(
js
,
state
,
"settled"
,
js_false
);
js_set
(
js
,
state
,
"isImmediate"
,
js_bool
(
is_immediate
));
callback
=
js_heavy_mkfun
(
js
,
timers_promises_resolve
,
state
);
if
(
is_immediate
)
handle
=
js_set_immediate
(
js
,
&
callback
,
1
);
else
{
args
[
0
]
=
callback
;
args
[
1
]
=
js_mknum
(
delay_ms
);
handle
=
js_set_timeout
(
js
,
args
,
2
);
}
if
(
is_err
(
handle
))
{
js_reject_promise
(
js
,
promise
,
handle
);
return
promise
;
}
js_set
(
js
,
state
,
"handle"
,
handle
);
if
(
abort_signal_is_signal
(
signal
))
{
ant_value_t
listener
=
js_heavy_mkfun
(
js
,
timers_promises_on_abort
,
state
);
js_set
(
js
,
state
,
"abortListener"
,
listener
);
abort_signal_add_listener
(
js
,
signal
,
listener
);
}
return
promise
;
}
static
ant_value_t
js_timers_promises_setTimeout
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
double
delay_ms
=
nargs
>
0
?
js_getnum
(
args
[
0
])
:
0
;
ant_value_t
value
=
nargs
>
1
?
args
[
1
]
:
js_mkundef
();
ant_value_t
options
=
nargs
>
2
?
args
[
2
]
:
js_mkundef
();
ant_value_t
signal
=
js_mkundef
();
ant_value_t
error
=
js_mkundef
();
if
(
!
timers_promises_parse_options
(
js
,
options
,
&
signal
,
&
error
))
return
error
;
return
timers_promises_schedule
(
js
,
delay_ms
,
value
,
signal
,
false
);
}
static
ant_value_t
js_timers_promises_setImmediate
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
value
=
nargs
>
0
?
args
[
0
]
:
js_mkundef
();
ant_value_t
options
=
nargs
>
1
?
args
[
1
]
:
js_mkundef
();
ant_value_t
signal
=
js_mkundef
();
ant_value_t
error
=
js_mkundef
();
if
(
!
timers_promises_parse_options
(
js
,
options
,
&
signal
,
&
error
))
return
error
;
return
timers_promises_schedule
(
js
,
0
,
value
,
signal
,
true
);
}
static
ant_value_t
js_timers_promises_setInterval
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"node:timers/promises setInterval() is not implemented yet"
);
}
static
ant_value_t
js_timers_promises_scheduler_wait
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
js_timers_promises_setTimeout
(
js
,
args
,
nargs
);
}
static
ant_value_t
js_timers_promises_scheduler_yield
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
js_timers_promises_setImmediate
(
js
,
args
,
nargs
);
}
static
void
queue_microtask_entry
(
microtask_entry_t
**
head
,
microtask_entry_t
**
tail
,
microtask_entry_t
*
entry
)
{
if
(
*
tail
==
NULL
)
goto
empty
;
(
*
tail
)
->
next
=
entry
;
*
tail
=
entry
;
return
;
empty
:
*
head
=
entry
;
*
tail
=
entry
;
}
void
queue_microtask
(
ant_t
*
js
,
ant_value_t
callback
)
{
microtask_entry_t
*
entry
=
ant_calloc
(
sizeof
(
microtask_entry_t
));
if
(
entry
==
NULL
)
return
;
entry
->
callback
=
callback
;
entry
->
promise
=
js_mkundef
();
entry
->
next
=
NULL
;
entry
->
argc
=
0
;
queue_microtask_entry
(
&
timer_state
.
microtasks
,
&
timer_state
.
microtasks_tail
,
entry
);
}
void
queue_microtask_with_args
(
ant_t
*
js
,
ant_value_t
callback
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<=
0
)
{
queue_microtask
(
js
,
callback
);
return
;
}
microtask_entry_t
*
entry
=
ant_calloc
(
sizeof
(
microtask_entry_t
)
+
(
size_t
)
nargs
*
sizeof
(
ant_value_t
));
if
(
entry
==
NULL
)
return
;
entry
->
callback
=
callback
;
entry
->
promise
=
js_mkundef
();
entry
->
next
=
NULL
;
entry
->
argc
=
(
uint8_t
)
nargs
;
for
(
int
i
=
0
;
i
<
nargs
;
i
++
)
entry
->
argv
[
i
]
=
args
[
i
];
queue_microtask_entry
(
&
timer_state
.
microtasks
,
&
timer_state
.
microtasks_tail
,
entry
);
}
void
queue_next_tick
(
ant_t
*
js
,
ant_value_t
callback
)
{
microtask_entry_t
*
entry
=
ant_calloc
(
sizeof
(
microtask_entry_t
));
if
(
entry
==
NULL
)
return
;
entry
->
callback
=
callback
;
entry
->
promise
=
js_mkundef
();
entry
->
next
=
NULL
;
entry
->
argc
=
0
;
queue_microtask_entry
(
&
timer_state
.
next_ticks
,
&
timer_state
.
next_ticks_tail
,
entry
);
}
void
queue_next_tick_with_args
(
ant_t
*
js
,
ant_value_t
callback
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<=
0
)
{
queue_next_tick
(
js
,
callback
);
return
;
}
microtask_entry_t
*
entry
=
ant_calloc
(
sizeof
(
microtask_entry_t
)
+
(
size_t
)
nargs
*
sizeof
(
ant_value_t
));
if
(
entry
==
NULL
)
return
;
entry
->
callback
=
callback
;
entry
->
promise
=
js_mkundef
();
entry
->
next
=
NULL
;
entry
->
argc
=
(
uint8_t
)
nargs
;
for
(
int
i
=
0
;
i
<
nargs
;
i
++
)
entry
->
argv
[
i
]
=
args
[
i
];
queue_microtask_entry
(
&
timer_state
.
next_ticks
,
&
timer_state
.
next_ticks_tail
,
entry
);
}
void
queue_promise_trigger
(
ant_t
*
js
,
ant_value_t
promise
)
{
if
(
!
js_mark_promise_trigger_queued
(
js
,
promise
))
return
;
microtask_entry_t
*
entry
=
ant_calloc
(
sizeof
(
microtask_entry_t
));
if
(
entry
==
NULL
)
{
js_mark_promise_trigger_dequeued
(
js
,
promise
);
return
;
}
entry
->
callback
=
js_mkundef
();
entry
->
promise
=
promise
;
entry
->
next
=
NULL
;
queue_microtask_entry
(
&
timer_state
.
microtasks
,
&
timer_state
.
microtasks_tail
,
entry
);
}
static
inline
void
process_microtask_entry
(
ant_t
*
js
,
microtask_entry_t
*
entry
)
{
if
(
!
entry
)
return
;
if
(
vtype
(
entry
->
promise
)
==
T_PROMISE
)
{
GC_ROOT_SAVE
(
root_mark
,
js
);
ant_value_t
promise
=
entry
->
promise
;
GC_ROOT_PIN
(
js
,
promise
);
js_mark_promise_trigger_dequeued
(
js
,
promise
);
js_process_promise_handlers
(
js
,
promise
);
GC_ROOT_RESTORE
(
js
,
root_mark
);
return
;
}
GC_ROOT_SAVE
(
root_mark
,
js
);
ant_value_t
callback
=
entry
->
callback
;
GC_ROOT_PIN
(
js
,
callback
);
for
(
uint8_t
i
=
0
;
i
<
entry
->
argc
;
i
++
)
GC_ROOT_PIN
(
js
,
entry
->
argv
[
i
]);
sv_vm_call
(
js
->
vm
,
js
,
callback
,
js_mkundef
(),
entry
->
argv
,
entry
->
argc
,
NULL
,
false
);
GC_ROOT_RESTORE
(
js
,
root_mark
);
}
static
inline
microtask_entry_t
*
take_microtask_batch
(
void
)
{
microtask_entry_t
*
batch
=
timer_state
.
microtasks
;
timer_state
.
microtasks
=
NULL
;
timer_state
.
microtasks_tail
=
NULL
;
timer_state
.
microtasks_processing
=
batch
;
return
batch
;
}
static
inline
microtask_entry_t
*
take_next_tick_batch
(
void
)
{
microtask_entry_t
*
batch
=
timer_state
.
next_ticks
;
timer_state
.
next_ticks
=
NULL
;
timer_state
.
next_ticks_tail
=
NULL
;
timer_state
.
next_ticks_processing
=
batch
;
return
batch
;
}
static
inline
void
process_microtask_batch
(
ant_t
*
js
,
microtask_entry_t
*
batch
)
{
while
(
batch
!=
NULL
)
{
microtask_entry_t
*
entry
=
batch
;
batch
=
entry
->
next
;
timer_state
.
microtasks_processing
=
batch
;
process_microtask_entry
(
js
,
entry
);
free
(
entry
);
}}
static
inline
void
process_next_tick_batch
(
ant_t
*
js
,
microtask_entry_t
*
batch
)
{
while
(
batch
!=
NULL
)
{
microtask_entry_t
*
entry
=
batch
;
batch
=
entry
->
next
;
timer_state
.
next_ticks_processing
=
batch
;
process_microtask_entry
(
js
,
entry
);
free
(
entry
);
}}
static
void
process_microtasks_internal
(
ant_t
*
js
,
bool
check_unhandled_rejections
)
{
microtask_entry_t
*
batch
=
NULL
;
if
(
!
js
||
js
->
microtasks_draining
)
return
;
js
->
microtasks_draining
=
true
;
while
(
timer_state
.
next_ticks
!=
NULL
||
timer_state
.
microtasks
!=
NULL
)
{
while
((
batch
=
timer_state
.
next_ticks
)
!=
NULL
)
{
batch
=
take_next_tick_batch
();
process_next_tick_batch
(
js
,
batch
);
}
while
((
batch
=
timer_state
.
microtasks
)
!=
NULL
)
{
batch
=
take_microtask_batch
();
process_microtask_batch
(
js
,
batch
);
}}
timer_state
.
next_ticks_processing
=
NULL
;
timer_state
.
microtasks_processing
=
NULL
;
if
(
check_unhandled_rejections
)
js_check_unhandled_rejections
(
js
);
js
->
microtasks_draining
=
false
;
reap_retired_coroutines
();
}
void
process_microtasks
(
ant_t
*
js
)
{
process_microtasks_internal
(
js
,
true
);
}
bool
js_maybe_drain_microtasks
(
ant_t
*
js
)
{
if
(
!
js
)
return
false
;
if
(
js
->
microtasks_draining
)
return
false
;
if
(
js
->
vm_exec_depth
!=
0
)
return
false
;
if
(
!
has_pending_microtasks
())
return
false
;
process_microtasks_internal
(
js
,
true
);
return
true
;
}
bool
js_maybe_drain_microtasks_after_async_settle
(
ant_t
*
js
)
{
if
(
!
js
)
return
false
;
if
(
js
->
microtasks_draining
)
return
false
;
if
(
!
has_pending_microtasks
())
return
false
;
process_microtasks_internal
(
js
,
false
);
return
true
;
}
void
process_immediates
(
ant_t
*
js
)
{
while
(
timer_state
.
immediates
!=
NULL
)
{
immediate_entry_t
*
entry
=
timer_state
.
immediates
;
timer_state
.
immediates
=
entry
->
next
;
if
(
timer_state
.
immediates
==
NULL
)
{
timer_state
.
immediates_tail
=
NULL
;
}
if
(
entry
->
active
)
{
ant_value_t
args
[
0
];
sv_vm_call
(
js
->
vm
,
js
,
entry
->
callback
,
js_mkundef
(),
args
,
0
,
NULL
,
false
);
process_microtasks
(
js
);
}
free
(
entry
);
}}
int
has_pending_immediates
(
void
)
{
for
(
immediate_entry_t
*
entry
=
timer_state
.
immediates
;
entry
!=
NULL
;
entry
=
entry
->
next
)
if
(
entry
->
active
)
return
1
;
return
0
;
}
int
has_pending_timers
(
void
)
{
return
timer_state
.
active_timer_count
>
0
;
}
int
has_pending_microtasks
(
void
)
{
return
(
timer_state
.
next_ticks
!=
NULL
||
timer_state
.
microtasks
!=
NULL
)
?
1
:
0
;
}
static
void
timers_define_common
(
ant_t
*
js
,
ant_value_t
obj
)
{
js_set
(
js
,
obj
,
"setTimeout"
,
js_mkfun_flags
(
js_set_timeout
,
CFUNC_HAS_PROTOTYPE
));
js_set
(
js
,
obj
,
"clearTimeout"
,
js_mkfun_flags
(
js_clear_timeout
,
CFUNC_HAS_PROTOTYPE
));
js_set
(
js
,
obj
,
"setInterval"
,
js_mkfun_flags
(
js_set_interval
,
CFUNC_HAS_PROTOTYPE
));
js_set
(
js
,
obj
,
"clearInterval"
,
js_mkfun_flags
(
js_clear_timeout
,
CFUNC_HAS_PROTOTYPE
));
js_set
(
js
,
obj
,
"setImmediate"
,
js_mkfun_flags
(
js_set_immediate
,
CFUNC_HAS_PROTOTYPE
));
js_set
(
js
,
obj
,
"clearImmediate"
,
js_mkfun_flags
(
js_clear_immediate
,
CFUNC_HAS_PROTOTYPE
));
js_set
(
js
,
obj
,
"queueMicrotask"
,
js_mkfun
(
js_queue_microtask
));
}
void
init_timer_module
()
{
ant_t
*
js
=
rt
->
js
;
timer_state
.
js
=
js
;
g_timeout_proto
=
js_mkobj
(
js
);
g_interval_proto
=
js_mkobj
(
js
);
gc_register_root
(
&
g_timeout_proto
);
gc_register_root
(
&
g_interval_proto
);
js_set_proto_init
(
g_timeout_proto
,
js
->
sym
.
object_proto
);
js_set
(
js
,
g_timeout_proto
,
"ref"
,
js_mkfun
(
js_timer_ref
));
js_set
(
js
,
g_timeout_proto
,
"unref"
,
js_mkfun
(
js_timer_unref
));
js_set
(
js
,
g_timeout_proto
,
"hasRef"
,
js_mkfun
(
js_timer_has_ref
));
js_set_sym
(
js
,
g_timeout_proto
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"Timeout"
,
7
));
js_set_sym
(
js
,
g_timeout_proto
,
get_inspect_sym
(),
js_mkfun
(
timer_inspect
));
js_set_proto_init
(
g_interval_proto
,
js
->
sym
.
object_proto
);
js_set
(
js
,
g_interval_proto
,
"ref"
,
js_mkfun
(
js_timer_ref
));
js_set
(
js
,
g_interval_proto
,
"unref"
,
js_mkfun
(
js_timer_unref
));
js_set
(
js
,
g_interval_proto
,
"hasRef"
,
js_mkfun
(
js_timer_has_ref
));
js_set_sym
(
js
,
g_interval_proto
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"Interval"
,
8
));
js_set_sym
(
js
,
g_interval_proto
,
get_inspect_sym
(),
js_mkfun
(
timer_inspect
));
timers_define_common
(
js
,
js_glob
(
js
));
}
ant_value_t
timers_library
(
ant_t
*
js
)
{
ant_value_t
lib
=
js_mkobj
(
js
);
timers_define_common
(
js
,
lib
);
js_set_sym
(
js
,
lib
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"timers"
,
6
));
return
lib
;
}
// TODO: mostly stubbed
ant_value_t
timers_promises_library
(
ant_t
*
js
)
{
ant_value_t
lib
=
js_mkobj
(
js
);
ant_value_t
scheduler
=
js_mkobj
(
js
);
js_set
(
js
,
lib
,
"scheduler"
,
scheduler
);
js_set
(
js
,
lib
,
"setTimeout"
,
js_mkfun
(
js_timers_promises_setTimeout
));
js_set
(
js
,
lib
,
"setImmediate"
,
js_mkfun
(
js_timers_promises_setImmediate
));
js_set
(
js
,
lib
,
"setInterval"
,
js_mkfun
(
js_timers_promises_setInterval
));
js_set
(
js
,
scheduler
,
"wait"
,
js_mkfun
(
js_timers_promises_scheduler_wait
));
js_set
(
js
,
scheduler
,
"yield"
,
js_mkfun
(
js_timers_promises_scheduler_yield
));
js_set_sym
(
js
,
lib
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"timers/promises"
,
16
));
return
lib
;
}
void
gc_mark_timers
(
ant_t
*
js
,
gc_mark_fn
mark
)
{
if
(
is_object_type
(
g_timeout_proto
))
mark
(
js
,
g_timeout_proto
);
if
(
is_object_type
(
g_interval_proto
))
mark
(
js
,
g_interval_proto
);
for
(
timer_entry_t
*
t
=
timer_state
.
timers
;
t
;
t
=
t
->
next
)
{
mark
(
js
,
t
->
callback
);
for
(
int
i
=
0
;
i
<
t
->
nargs
;
i
++
)
mark
(
js
,
t
->
args
[
i
]);
}
for
(
microtask_entry_t
*
m
=
timer_state
.
microtasks
;
m
;
m
=
m
->
next
)
{
mark
(
js
,
m
->
callback
);
mark
(
js
,
m
->
promise
);
for
(
uint8_t
i
=
0
;
i
<
m
->
argc
;
i
++
)
mark
(
js
,
m
->
argv
[
i
]);
}
for
(
microtask_entry_t
*
m
=
timer_state
.
microtasks_processing
;
m
;
m
=
m
->
next
)
{
mark
(
js
,
m
->
callback
);
mark
(
js
,
m
->
promise
);
for
(
uint8_t
i
=
0
;
i
<
m
->
argc
;
i
++
)
mark
(
js
,
m
->
argv
[
i
]);
}
for
(
microtask_entry_t
*
m
=
timer_state
.
next_ticks
;
m
;
m
=
m
->
next
)
{
mark
(
js
,
m
->
callback
);
mark
(
js
,
m
->
promise
);
for
(
uint8_t
i
=
0
;
i
<
m
->
argc
;
i
++
)
mark
(
js
,
m
->
argv
[
i
]);
}
for
(
microtask_entry_t
*
m
=
timer_state
.
next_ticks_processing
;
m
;
m
=
m
->
next
)
{
mark
(
js
,
m
->
callback
);
mark
(
js
,
m
->
promise
);
for
(
uint8_t
i
=
0
;
i
<
m
->
argc
;
i
++
)
mark
(
js
,
m
->
argv
[
i
]);
}
for
(
immediate_entry_t
*
i
=
timer_state
.
immediates
;
i
;
i
=
i
->
next
)
{
mark
(
js
,
i
->
callback
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, May 2, 10:34 AM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
541770
Default Alt Text
timer.c (27 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment