Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2916063
timer.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
10 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
"arena.h"
#include
"errors.h"
#include
"silver/engine.h"
#include
"runtime.h"
#include
"modules/timer.h"
typedef
struct
timer_entry
{
uv_timer_t
handle
;
jsval_t
callback
;
jsval_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
{
jsval_t
callback
;
uint32_t
promise_id
;
struct
microtask_entry
*
next
;
}
microtask_entry_t
;
typedef
struct
immediate_entry
{
jsval_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
*
microtasks
;
microtask_entry_t
*
microtasks_tail
;
immediate_entry_t
*
immediates
;
immediate_entry_t
*
immediates_tail
;
int
next_timer_id
;
int
next_immediate_id
;
int
active_timer_count
;
}
timer_state
=
{
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
1
,
1
,
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
int
timer_copy_args
(
timer_entry_t
*
entry
,
jsval_t
*
args
,
int
nargs
)
{
entry
->
nargs
=
nargs
>
2
?
nargs
-
2
:
0
;
if
(
entry
->
nargs
>
0
)
{
entry
->
args
=
ant_calloc
(
sizeof
(
jsval_t
)
*
entry
->
nargs
);
if
(
!
entry
->
args
)
return
-1
;
memcpy
(
entry
->
args
,
args
+
2
,
sizeof
(
jsval_t
)
*
entry
->
nargs
);
}
else
entry
->
args
=
NULL
;
return
0
;
}
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
jsval_t
js_set_timeout
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
if
(
nargs
<
2
)
{
return
js_mkerr
(
js
,
"setTimeout requires 2 arguments (callback, delay)"
);
}
jsval_t
callback
=
args
[
0
];
double
delay_ms
=
js_getnum
(
args
[
1
]);
uint64_t
ms
=
delay_ms
<
1
?
0
:
(
uint64_t
)
delay_ms
;
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
js_mknum
((
double
)
entry
->
timer_id
);
}
// setInterval(callback, delay, ...args)
static
jsval_t
js_set_interval
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
if
(
nargs
<
2
)
{
return
js_mkerr
(
js
,
"setInterval requires 2 arguments (callback, delay)"
);
}
jsval_t
callback
=
args
[
0
];
double
delay_ms
=
js_getnum
(
args
[
1
]);
uint64_t
ms
=
delay_ms
<
1
?
1
:
(
uint64_t
)
delay_ms
;
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
js_mknum
((
double
)
entry
->
timer_id
);
}
// clearTimeout(timerId)
static
jsval_t
js_clear_timeout
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkundef
();
int
timer_id
=
(
int
)
js_getnum
(
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
jsval_t
js_set_immediate
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
{
return
js_mkerr
(
js
,
"setImmediate requires 1 argument (callback)"
);
}
jsval_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
;
}
return
js_mknum
((
double
)
entry
->
immediate_id
);
}
// clearImmediate(immediateId)
static
jsval_t
js_clear_immediate
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkundef
();
int
immediate_id
=
(
int
)
js_getnum
(
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
jsval_t
js_queue_microtask
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
{
return
js_mkerr
(
js
,
"queueMicrotask requires 1 argument (callback)"
);
}
queue_microtask
(
js
,
args
[
0
]);
return
js_mkundef
();
}
void
queue_microtask
(
ant_t
*
js
,
jsval_t
callback
)
{
microtask_entry_t
*
entry
=
ant_calloc
(
sizeof
(
microtask_entry_t
));
if
(
entry
==
NULL
)
return
;
entry
->
callback
=
callback
;
entry
->
promise_id
=
0
;
entry
->
next
=
NULL
;
if
(
timer_state
.
microtasks_tail
==
NULL
)
{
timer_state
.
microtasks
=
entry
;
timer_state
.
microtasks_tail
=
entry
;
}
else
{
timer_state
.
microtasks_tail
->
next
=
entry
;
timer_state
.
microtasks_tail
=
entry
;
}
}
void
queue_promise_trigger
(
uint32_t
promise_id
)
{
microtask_entry_t
*
entry
=
ant_calloc
(
sizeof
(
microtask_entry_t
));
if
(
entry
==
NULL
)
return
;
entry
->
callback
=
0
;
entry
->
promise_id
=
promise_id
;
entry
->
next
=
NULL
;
if
(
timer_state
.
microtasks_tail
==
NULL
)
{
timer_state
.
microtasks
=
entry
;
timer_state
.
microtasks_tail
=
entry
;
}
else
{
timer_state
.
microtasks_tail
->
next
=
entry
;
timer_state
.
microtasks_tail
=
entry
;
}
}
void
process_microtasks
(
ant_t
*
js
)
{
while
(
timer_state
.
microtasks
!=
NULL
)
{
microtask_entry_t
*
entry
=
timer_state
.
microtasks
;
timer_state
.
microtasks
=
entry
->
next
;
if
(
timer_state
.
microtasks
==
NULL
)
{
timer_state
.
microtasks_tail
=
NULL
;
}
if
(
entry
->
promise_id
!=
0
)
{
js_process_promise_handlers
(
js
,
entry
->
promise_id
);
}
else
{
jsval_t
args
[
0
];
sv_vm_call
(
js
->
vm
,
js
,
entry
->
callback
,
js_mkundef
(),
args
,
0
,
NULL
,
false
);
}
free
(
entry
);
}
js_check_unhandled_rejections
(
js
);
}
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
)
{
jsval_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
.
microtasks
!=
NULL
?
1
:
0
;
}
void
init_timer_module
()
{
ant_t
*
js
=
rt
->
js
;
timer_state
.
js
=
js
;
jsval_t
global
=
js_glob
(
js
);
js_set
(
js
,
global
,
"setTimeout"
,
js_mkfun
(
js_set_timeout
));
js_set
(
js
,
global
,
"clearTimeout"
,
js_mkfun
(
js_clear_timeout
));
js_set
(
js
,
global
,
"setInterval"
,
js_mkfun
(
js_set_interval
));
js_set
(
js
,
global
,
"clearInterval"
,
js_mkfun
(
js_clear_timeout
));
js_set
(
js
,
global
,
"setImmediate"
,
js_mkfun
(
js_set_immediate
));
js_set
(
js
,
global
,
"clearImmediate"
,
js_mkfun
(
js_clear_immediate
));
js_set
(
js
,
global
,
"queueMicrotask"
,
js_mkfun
(
js_queue_microtask
));
}
void
timer_gc_update_roots
(
GC_OP_VAL_ARGS
)
{
for
(
timer_entry_t
*
t
=
timer_state
.
timers
;
t
;
t
=
t
->
next
)
{
op_val
(
ctx
,
&
t
->
callback
);
for
(
int
i
=
0
;
i
<
t
->
nargs
;
i
++
)
op_val
(
ctx
,
&
t
->
args
[
i
]);
}
for
(
microtask_entry_t
*
m
=
timer_state
.
microtasks
;
m
;
m
=
m
->
next
)
{
if
(
m
->
promise_id
==
0
)
op_val
(
ctx
,
&
m
->
callback
);
}
for
(
immediate_entry_t
*
i
=
timer_state
.
immediates
;
i
;
i
=
i
->
next
)
{
op_val
(
ctx
,
&
i
->
callback
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Thu, Mar 26, 4:40 PM (1 d, 1 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
511704
Default Alt Text
timer.c (10 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment