Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F4433622
async.h
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
21 KB
Referenced Files
None
Subscribers
None
async.h
View Options
#ifndef SV_ASYNC_H
#define SV_ASYNC_H
#include
"ant.h"
#include
"gc/roots.h"
#include
"sugar.h"
#include
"silver/engine.h"
#include
<minicoro.h>
typedef
struct
{
ant_t
*
js
;
coroutine_t
*
coro
;
}
sv_coro_header_t
;
typedef
struct
{
ant_t
*
js
;
coroutine_t
*
coro
;
sv_closure_t
*
closure
;
ant_value_t
callee_func
;
ant_value_t
super_val
;
ant_value_t
this_val
;
ant_value_t
*
args
;
int
argc
;
sv_vm_t
*
vm
;
}
sv_async_ctx_t
;
static
inline
bool
sv_async_func_supports_lazy_start
(
sv_func_t
*
func
)
{
if
(
!
func
||
!
func
->
has_await
||
func
->
is_generator
)
return
false
;
for
(
int
off
=
0
;
off
<
func
->
code_len
;)
{
sv_op_t
op
=
(
sv_op_t
)
func
->
code
[
off
];
if
(
op
==
OP_AWAIT_ITER_NEXT
||
op
==
OP_YIELD
||
op
==
OP_YIELD_STAR_NEXT
||
op
==
OP_YIELD_STAR_THROW
||
op
==
OP_YIELD_STAR_RETURN
)
return
false
;
int
size
=
sv_op_size
[
op
];
if
(
size
<=
0
)
return
false
;
off
+=
size
;
}
return
true
;
}
static
void
sv_mco_async_entry
(
mco_coro
*
mco
)
{
sv_async_ctx_t
*
ctx
=
(
sv_async_ctx_t
*
)
mco_get_user_data
(
mco
);
ant_t
*
js
=
ctx
->
js
;
MCO_CORO_STACK_ENTER
(
js
,
mco
);
ant_value_t
super_val
=
ctx
->
super_val
;
if
(
ctx
->
coro
)
{
super_val
=
ctx
->
coro
->
super_val
;
js
->
new_target
=
ctx
->
coro
->
new_target
;
}
sv_vm_t
*
vm
=
ctx
->
vm
;
ant_value_t
result
=
sv_execute_closure_entry
(
vm
,
ctx
->
closure
,
ctx
->
callee_func
,
super_val
,
ctx
->
this_val
,
ctx
->
args
,
ctx
->
argc
,
NULL
);
ant_value_t
promise
=
ctx
->
coro
->
async_promise
;
if
(
vm
&&
vm
->
suspended
)
return
;
if
(
is_err
(
result
))
{
ant_value_t
reject_value
=
js
->
thrown_exists
?
js
->
thrown_value
:
result
;
js
->
thrown_exists
=
false
;
js
->
thrown_value
=
js_mkundef
();
js_reject_promise
(
js
,
promise
,
reject_value
);
js_maybe_drain_microtasks_after_async_settle
(
js
);
}
else
{
js_resolve_promise
(
js
,
promise
,
result
);
js_maybe_drain_microtasks_after_async_settle
(
js
);
}
}
typedef
struct
{
ant_t
*
js
;
coroutine_t
*
coro
;
sv_func_t
*
func
;
ant_value_t
this_val
;
sv_vm_t
*
vm
;
}
sv_tla_ctx_t
;
typedef
enum
{
SV_AWAIT_READY
=
0
,
SV_AWAIT_ERROR
,
SV_AWAIT_SUSPENDED
,
}
sv_await_state_t
;
typedef
struct
{
sv_await_state_t
state
;
ant_value_t
value
;
bool
handoff
;
}
sv_await_result_t
;
static
inline
void
sv_async_link_activation
(
ant_t
*
js
,
coroutine_t
*
coro
)
{
if
(
!
js
||
!
coro
)
return
;
coro
->
active_parent
=
js
->
active_async_coro
;
coro
->
active_prev
=
NULL
;
if
(
js
->
active_async_coro
)
js
->
active_async_coro
->
active_prev
=
coro
;
js
->
active_async_coro
=
coro
;
coroutine_hold
(
coro
,
CORO_HOLD_ACTIVE
);
}
static
inline
void
sv_async_unlink_activation
(
ant_t
*
js
,
coroutine_t
*
coro
)
{
if
(
!
js
||
!
coro
)
return
;
if
(
coro
->
active_prev
)
coro
->
active_prev
->
active_parent
=
coro
->
active_parent
;
else
if
(
js
->
active_async_coro
==
coro
)
js
->
active_async_coro
=
coro
->
active_parent
;
if
(
coro
->
active_parent
)
coro
->
active_parent
->
active_prev
=
coro
->
active_prev
;
coro
->
active_parent
=
NULL
;
coro
->
active_prev
=
NULL
;
coroutine_unhold
(
coro
,
CORO_HOLD_ACTIVE
);
}
static
inline
bool
sv_async_coro_matches_vm
(
const
coroutine_t
*
coro
,
const
sv_vm_t
*
vm
)
{
if
(
!
coro
||
!
vm
)
return
false
;
if
(
coro
->
sv_vm
==
vm
)
return
true
;
return
coro
->
owner_vm
==
vm
;
}
static
inline
coroutine_t
*
sv_async_get_active_coro_for_vm
(
ant_t
*
js
,
sv_vm_t
*
vm
)
{
if
(
!
js
||
!
js
->
active_async_coro
)
return
NULL
;
if
(
!
vm
)
return
js
->
active_async_coro
;
for
(
coroutine_t
*
it
=
js
->
active_async_coro
;
it
;
it
=
it
->
active_parent
)
{
if
(
sv_async_coro_matches_vm
(
it
,
vm
))
return
it
;
}
return
NULL
;
}
static
inline
void
sv_async_init_activation
(
coroutine_t
*
coro
,
ant_t
*
js
,
sv_vm_t
*
owner_vm
,
ant_value_t
promise
,
ant_value_t
this_val
,
ant_value_t
super_val
,
ant_value_t
new_target
,
ant_value_t
async_func
,
int
nargs
)
{
if
(
!
coro
)
return
;
*
coro
=
(
coroutine_t
){
.
js
=
js
,
.
this_val
=
this_val
,
.
super_val
=
super_val
,
.
new_target
=
new_target
,
.
result
=
js_mkundef
(),
.
async_func
=
async_func
,
.
yield_value
=
js_mkundef
(),
.
args
=
NULL
,
.
awaited_promise
=
js_mkundef
(),
.
async_promise
=
promise
,
.
active_parent
=
NULL
,
.
prev
=
NULL
,
.
next
=
NULL
,
.
mco
=
NULL
,
.
owner_vm
=
owner_vm
,
.
sv_vm
=
NULL
,
.
resume_point
=
0
,
.
type
=
CORO_ASYNC_AWAIT
,
.
owner_entry_fp
=
owner_vm
?
owner_vm
->
fp
:
-1
,
.
owner_saved_fp
=
owner_vm
?
owner_vm
->
fp
-
1
:
-1
,
.
nargs
=
nargs
,
.
refcount
=
1
,
.
hold_bits
=
0
,
.
is_settled
=
false
,
.
is_error
=
false
,
.
is_done
=
false
,
.
materialized
=
false
,
.
mco_started
=
false
,
.
is_ready
=
false
,
.
did_suspend
=
false
,
.
await_registered
=
false
,
.
destroy_requested
=
false
,
};
}
static
inline
void
sv_async_move_open_upvalues
(
sv_vm_t
*
source_vm
,
sv_vm_t
*
async_vm
,
ant_value_t
*
source_base
,
ant_value_t
*
dest_base
,
size_t
stack_count
)
{
if
(
!
source_vm
||
!
async_vm
||
!
source_base
||
!
dest_base
||
stack_count
==
0
)
return
;
sv_upvalue_t
**
src_pp
=
&
source_vm
->
open_upvalues
;
sv_upvalue_t
**
dst_pp
=
&
async_vm
->
open_upvalues
;
while
(
*
src_pp
)
{
sv_upvalue_t
*
uv
=
*
src_pp
;
if
(
!
sv_slot_in_range
(
source_base
,
stack_count
,
uv
->
location
))
{
src_pp
=
&
uv
->
next
;
continue
;
}
ptrdiff_t
slot
=
uv
->
location
-
source_base
;
*
src_pp
=
uv
->
next
;
uv
->
location
=
&
dest_base
[
slot
];
uv
->
next
=
NULL
;
*
dst_pp
=
uv
;
dst_pp
=
&
uv
->
next
;
}
}
static
inline
sv_vm_t
*
sv_async_prepare_materialization
(
sv_vm_t
*
source_vm
,
ant_t
*
js
,
coroutine_t
*
coro
)
{
if
(
!
source_vm
||
!
js
||
!
coro
||
coro
->
sv_vm
)
return
coro
?
coro
->
sv_vm
:
NULL
;
if
(
source_vm
->
fp
<
0
)
return
NULL
;
int
entry_fp
=
source_vm
->
suspended_entry_fp
;
if
(
entry_fp
<
0
||
entry_fp
>
source_vm
->
fp
)
entry_fp
=
source_vm
->
fp
;
sv_frame_t
*
entry_frame
=
&
source_vm
->
frames
[
entry_fp
];
int
frame_count
=
source_vm
->
fp
-
entry_fp
+
1
;
int
stack_base
=
entry_frame
->
prev_sp
;
int
stack_count
=
source_vm
->
sp
-
stack_base
;
int
handler_base
=
entry_frame
->
handler_base
;
int
handler_count
=
source_vm
->
handler_depth
-
handler_base
;
sv_vm_t
*
async_vm
=
sv_vm_create
(
js
,
SV_VM_ASYNC
);
if
(
!
async_vm
)
return
NULL
;
if
(
stack_count
<
0
||
stack_count
>
async_vm
->
stack_size
)
{
sv_vm_destroy
(
async_vm
);
return
NULL
;
}
if
(
frame_count
<
1
||
frame_count
>
async_vm
->
max_frames
)
{
sv_vm_destroy
(
async_vm
);
return
NULL
;
}
if
(
handler_count
<
0
||
handler_count
>
SV_HANDLER_MAX
)
{
sv_vm_destroy
(
async_vm
);
return
NULL
;
}
if
(
stack_count
>
0
)
{
memcpy
(
async_vm
->
stack
,
&
source_vm
->
stack
[
stack_base
],
sizeof
(
ant_value_t
)
*
(
size_t
)
stack_count
);
}
async_vm
->
sp
=
stack_count
;
async_vm
->
fp
=
frame_count
-
1
;
for
(
int
i
=
0
;
i
<
frame_count
;
i
++
)
{
sv_frame_t
*
src
=
&
source_vm
->
frames
[
entry_fp
+
i
];
sv_frame_t
*
dst
=
&
async_vm
->
frames
[
i
];
*
dst
=
*
src
;
dst
->
prev_sp
=
src
->
prev_sp
-
stack_base
;
dst
->
handler_base
=
src
->
handler_base
-
handler_base
;
dst
->
handler_top
=
src
->
handler_top
-
handler_base
;
if
(
src
->
bp
)
dst
->
bp
=
async_vm
->
stack
+
(
src
->
bp
-
&
source_vm
->
stack
[
stack_base
]);
if
(
src
->
lp
)
dst
->
lp
=
async_vm
->
stack
+
(
src
->
lp
-
&
source_vm
->
stack
[
stack_base
]);
}
if
(
handler_count
>
0
)
{
memcpy
(
async_vm
->
handler_stack
,
&
source_vm
->
handler_stack
[
handler_base
],
sizeof
(
sv_handler_t
)
*
(
size_t
)
handler_count
);
}
async_vm
->
handler_depth
=
handler_count
;
async_vm
->
suspended
=
true
;
async_vm
->
suspended_entry_fp
=
0
;
async_vm
->
suspended_saved_fp
=
-1
;
async_vm
->
suspended_resume_pending
=
false
;
async_vm
->
suspended_resume_is_error
=
false
;
async_vm
->
suspended_resume_kind
=
SV_RESUME_NEXT
;
async_vm
->
suspended_resume_value
=
js_mkundef
();
return
async_vm
;
}
static
inline
bool
sv_async_materialize_activation
(
sv_vm_t
*
source_vm
,
sv_vm_t
*
async_vm
,
coroutine_t
*
coro
)
{
if
(
!
source_vm
||
!
async_vm
||
!
coro
||
source_vm
->
fp
<
0
)
return
false
;
int
entry_fp
=
source_vm
->
suspended_entry_fp
;
if
(
entry_fp
<
0
||
entry_fp
>
source_vm
->
fp
)
entry_fp
=
source_vm
->
fp
;
sv_frame_t
*
entry_frame
=
&
source_vm
->
frames
[
entry_fp
];
ant_value_t
*
source_base
=
&
source_vm
->
stack
[
entry_frame
->
prev_sp
];
size_t
stack_count
=
(
size_t
)(
source_vm
->
sp
-
entry_frame
->
prev_sp
);
sv_async_move_open_upvalues
(
source_vm
,
async_vm
,
source_base
,
async_vm
->
stack
,
stack_count
);
coro
->
owner_entry_fp
=
source_vm
->
suspended_entry_fp
;
coro
->
owner_saved_fp
=
source_vm
->
suspended_saved_fp
;
coro
->
sv_vm
=
async_vm
;
coro
->
materialized
=
true
;
return
true
;
}
static
void
sv_mco_tla_entry
(
mco_coro
*
mco
)
{
sv_tla_ctx_t
*
ctx
=
(
sv_tla_ctx_t
*
)
mco_get_user_data
(
mco
);
ant_t
*
js
=
ctx
->
js
;
sv_vm_t
*
vm
=
ctx
->
vm
;
MCO_CORO_STACK_ENTER
(
js
,
mco
);
ant_value_t
result
=
sv_execute_entry
(
vm
,
ctx
->
func
,
ctx
->
this_val
,
NULL
,
0
);
ant_value_t
promise
=
ctx
->
coro
->
async_promise
;
if
(
vm
&&
vm
->
suspended
)
return
;
if
(
is_err
(
result
))
{
ant_value_t
reject_value
=
js
->
thrown_exists
?
js
->
thrown_value
:
result
;
js
->
thrown_exists
=
false
;
js
->
thrown_value
=
js_mkundef
();
js_reject_promise
(
js
,
promise
,
reject_value
);
js_maybe_drain_microtasks_after_async_settle
(
js
);
}
else
{
js_resolve_promise
(
js
,
promise
,
result
);
js_maybe_drain_microtasks_after_async_settle
(
js
);
}
}
static
inline
ant_value_t
sv_start_tla
(
ant_t
*
js
,
sv_func_t
*
func
,
ant_value_t
this_val
)
{
if
(
++
coros_this_tick
>
CORO_PER_TICK_LIMIT
)
{
js
->
fatal_error
=
true
;
return
js_mkerr_typed
(
js
,
JS_ERR_RANGE
|
JS_ERR_NO_STACK
,
"Maximum async operations per tick exceeded"
);
}
ant_value_t
promise
=
js_mkpromise
(
js
);
if
(
func
&&
(
!
func
->
has_await
||
sv_async_func_supports_lazy_start
(
func
)))
{
GC_ROOT_SAVE
(
root_mark
,
js
);
GC_ROOT_PIN
(
js
,
this_val
);
GC_ROOT_PIN
(
js
,
promise
);
coroutine_t
*
coro
=
(
coroutine_t
*
)
CORO_MALLOC
(
sizeof
(
coroutine_t
));
if
(
!
coro
)
{
GC_ROOT_RESTORE
(
js
,
root_mark
);
return
js_mkerr
(
js
,
"out of memory for TLA coroutine"
);
}
sv_async_init_activation
(
coro
,
js
,
js
->
vm
,
promise
,
this_val
,
js_mkundef
(),
js_mkundef
(),
js_mkundef
(),
0
);
sv_async_link_activation
(
js
,
coro
);
ant_value_t
result
=
sv_execute_entry
(
js
->
vm
,
func
,
this_val
,
NULL
,
0
);
sv_async_unlink_activation
(
js
,
coro
);
if
(
coro
->
sv_vm
&&
coro
->
sv_vm
->
suspended
)
{
coroutine_release
(
coro
);
GC_ROOT_RESTORE
(
js
,
root_mark
);
return
promise
;
}
if
(
is_err
(
result
))
{
ant_value_t
reject_value
=
js
->
thrown_exists
?
js
->
thrown_value
:
result
;
js
->
thrown_exists
=
false
;
js
->
thrown_value
=
js_mkundef
();
js_reject_promise
(
js
,
promise
,
reject_value
);
}
else
{
js_resolve_promise
(
js
,
promise
,
result
);
}
coroutine_release
(
coro
);
GC_ROOT_RESTORE
(
js
,
root_mark
);
return
promise
;
}
sv_tla_ctx_t
*
ctx
=
(
sv_tla_ctx_t
*
)
CORO_MALLOC
(
sizeof
(
sv_tla_ctx_t
));
if
(
!
ctx
)
return
js_mkerr
(
js
,
"out of memory for TLA context"
);
sv_vm_t
*
async_vm
=
sv_vm_create
(
js
,
SV_VM_ASYNC
);
if
(
!
async_vm
)
{
CORO_FREE
(
ctx
);
return
js_mkerr
(
js
,
"out of memory for TLA VM"
);
}
ctx
->
js
=
js
;
ctx
->
func
=
func
;
ctx
->
this_val
=
this_val
;
ctx
->
coro
=
NULL
;
ctx
->
vm
=
async_vm
;
size_t
stack_size
=
0
;
const
char
*
env_stack
=
getenv
(
"ANT_CORO_STACK_SIZE"
);
if
(
env_stack
)
{
size_t
sz
=
(
size_t
)
atoi
(
env_stack
)
*
1024
;
if
(
sz
>=
32
*
1024
&&
sz
<=
8
*
1024
*
1024
)
stack_size
=
sz
;
}
mco_desc
desc
=
mco_desc_init
(
sv_mco_tla_entry
,
stack_size
);
desc
.
user_data
=
ctx
;
mco_coro
*
mco
=
NULL
;
mco_result
res
=
mco_create
(
&
mco
,
&
desc
);
if
(
res
!=
MCO_SUCCESS
)
{
sv_vm_destroy
(
async_vm
);
CORO_FREE
(
ctx
);
return
js_mkerr
(
js
,
"failed to create TLA coroutine"
);
}
coroutine_t
*
coro
=
(
coroutine_t
*
)
CORO_MALLOC
(
sizeof
(
coroutine_t
));
if
(
!
coro
)
{
mco_destroy
(
mco
);
sv_vm_destroy
(
async_vm
);
CORO_FREE
(
ctx
);
return
js_mkerr
(
js
,
"out of memory for TLA coroutine"
);
}
*
coro
=
(
coroutine_t
){
.
js
=
js
,
.
type
=
CORO_ASYNC_AWAIT
,
.
this_val
=
this_val
,
.
super_val
=
js_mkundef
(),
.
new_target
=
js_mkundef
(),
.
awaited_promise
=
js_mkundef
(),
.
result
=
js_mkundef
(),
.
async_func
=
js_mkundef
(),
.
args
=
NULL
,
.
nargs
=
0
,
.
active_parent
=
NULL
,
.
is_settled
=
false
,
.
is_error
=
false
,
.
is_done
=
false
,
.
resume_point
=
0
,
.
yield_value
=
js_mkundef
(),
.
async_promise
=
promise
,
.
next
=
NULL
,
.
mco
=
mco
,
.
owner_vm
=
async_vm
,
.
sv_vm
=
async_vm
,
.
mco_started
=
false
,
.
is_ready
=
true
,
.
did_suspend
=
false
,
.
refcount
=
1
,
.
hold_bits
=
0
,
.
await_registered
=
false
,
.
destroy_requested
=
false
,
};
ctx
->
coro
=
coro
;
enqueue_coroutine
(
coro
);
MCO_RESUME_SAVE
(
js
,
mco
,
res
);
if
(
res
!=
MCO_SUCCESS
&&
mco_status
(
mco
)
!=
MCO_DEAD
)
{
remove_coroutine
(
coro
);
coroutine_release
(
coro
);
return
js_mkerr
(
js
,
"failed to start TLA coroutine"
);
}
coro
->
mco_started
=
true
;
if
(
mco_status
(
mco
)
==
MCO_DEAD
)
{
remove_coroutine
(
coro
);
}
coroutine_release
(
coro
);
return
promise
;
}
static
inline
ant_value_t
sv_start_async_closure
(
sv_vm_t
*
caller_vm
,
ant_t
*
js
,
sv_closure_t
*
closure
,
ant_value_t
callee_func
,
ant_value_t
super_val
,
ant_value_t
this_val
,
ant_value_t
*
args
,
int
argc
)
{
if
(
++
coros_this_tick
>
CORO_PER_TICK_LIMIT
)
{
js
->
fatal_error
=
true
;
return
js_mkerr_typed
(
js
,
JS_ERR_RANGE
|
JS_ERR_NO_STACK
,
"Maximum async operations per tick exceeded"
);
}
if
(
caller_vm
&&
closure
&&
closure
->
func
&&
!
closure
->
func
->
has_await
)
{
GC_ROOT_SAVE
(
root_mark
,
js
);
GC_ROOT_PIN
(
js
,
callee_func
);
GC_ROOT_PIN
(
js
,
super_val
);
GC_ROOT_PIN
(
js
,
this_val
);
if
(
args
)
{
for
(
int
i
=
0
;
i
<
argc
;
i
++
)
GC_ROOT_PIN
(
js
,
args
[
i
]);
}
ant_value_t
promise
=
js_mkpromise
(
js
);
GC_ROOT_PIN
(
js
,
promise
);
ant_value_t
result
=
sv_execute_closure_entry
(
caller_vm
,
closure
,
callee_func
,
super_val
,
this_val
,
args
,
argc
,
NULL
);
if
(
is_err
(
result
))
{
ant_value_t
reject_value
=
js
->
thrown_exists
?
js
->
thrown_value
:
result
;
js
->
thrown_exists
=
false
;
js
->
thrown_value
=
js_mkundef
();
js_reject_promise
(
js
,
promise
,
reject_value
);
}
else
js_resolve_promise
(
js
,
promise
,
result
);
GC_ROOT_RESTORE
(
js
,
root_mark
);
return
promise
;
}
if
(
closure
&&
closure
->
func
&&
sv_async_func_supports_lazy_start
(
closure
->
func
))
{
GC_ROOT_SAVE
(
root_mark
,
js
);
GC_ROOT_PIN
(
js
,
callee_func
);
GC_ROOT_PIN
(
js
,
super_val
);
GC_ROOT_PIN
(
js
,
this_val
);
if
(
args
)
{
for
(
int
i
=
0
;
i
<
argc
;
i
++
)
GC_ROOT_PIN
(
js
,
args
[
i
]);
}
ant_value_t
promise
=
js_mkpromise
(
js
);
GC_ROOT_PIN
(
js
,
promise
);
coroutine_t
*
coro
=
(
coroutine_t
*
)
CORO_MALLOC
(
sizeof
(
coroutine_t
));
if
(
!
coro
)
{
GC_ROOT_RESTORE
(
js
,
root_mark
);
return
js_mkerr
(
js
,
"out of memory for async coroutine"
);
}
sv_async_init_activation
(
coro
,
js
,
caller_vm
,
promise
,
this_val
,
super_val
,
js
->
new_target
,
callee_func
,
argc
);
sv_async_link_activation
(
js
,
coro
);
ant_value_t
result
=
sv_execute_closure_entry
(
caller_vm
,
closure
,
callee_func
,
super_val
,
this_val
,
args
,
argc
,
NULL
);
sv_async_unlink_activation
(
js
,
coro
);
if
(
coro
->
sv_vm
&&
coro
->
sv_vm
->
suspended
)
{
coroutine_release
(
coro
);
GC_ROOT_RESTORE
(
js
,
root_mark
);
return
promise
;
}
if
(
is_err
(
result
))
{
ant_value_t
reject_value
=
js
->
thrown_exists
?
js
->
thrown_value
:
result
;
js
->
thrown_exists
=
false
;
js
->
thrown_value
=
js_mkundef
();
js_reject_promise
(
js
,
promise
,
reject_value
);
}
else
{
js_resolve_promise
(
js
,
promise
,
result
);
}
coroutine_release
(
coro
);
GC_ROOT_RESTORE
(
js
,
root_mark
);
return
promise
;
}
ant_value_t
promise
=
js_mkpromise
(
js
);
sv_async_ctx_t
*
ctx
=
(
sv_async_ctx_t
*
)
CORO_MALLOC
(
sizeof
(
sv_async_ctx_t
));
if
(
!
ctx
)
return
js_mkerr
(
js
,
"out of memory for async context"
);
sv_vm_t
*
async_vm
=
sv_vm_create
(
js
,
SV_VM_ASYNC
);
if
(
!
async_vm
)
{
CORO_FREE
(
ctx
);
return
js_mkerr
(
js
,
"out of memory for async VM"
);
}
ctx
->
js
=
js
;
ctx
->
closure
=
closure
;
ctx
->
callee_func
=
callee_func
;
ctx
->
super_val
=
super_val
;
ctx
->
this_val
=
this_val
;
ctx
->
args
=
NULL
;
ctx
->
argc
=
argc
;
ctx
->
coro
=
NULL
;
ctx
->
vm
=
async_vm
;
if
(
argc
>
0
&&
args
)
{
ctx
->
args
=
(
ant_value_t
*
)
CORO_MALLOC
(
sizeof
(
ant_value_t
)
*
(
size_t
)
argc
);
if
(
!
ctx
->
args
)
{
sv_vm_destroy
(
async_vm
);
CORO_FREE
(
ctx
);
return
js_mkerr
(
js
,
"out of memory for async args"
);
}
memcpy
(
ctx
->
args
,
args
,
sizeof
(
ant_value_t
)
*
(
size_t
)
argc
);
}
size_t
stack_size
=
0
;
const
char
*
env_stack
=
getenv
(
"ANT_CORO_STACK_SIZE"
);
if
(
env_stack
)
{
size_t
sz
=
(
size_t
)
atoi
(
env_stack
)
*
1024
;
if
(
sz
>=
32
*
1024
&&
sz
<=
8
*
1024
*
1024
)
stack_size
=
sz
;
}
mco_desc
desc
=
mco_desc_init
(
sv_mco_async_entry
,
stack_size
);
desc
.
user_data
=
ctx
;
mco_coro
*
mco
=
NULL
;
mco_result
res
=
mco_create
(
&
mco
,
&
desc
);
if
(
res
!=
MCO_SUCCESS
)
{
if
(
ctx
->
args
)
CORO_FREE
(
ctx
->
args
);
sv_vm_destroy
(
async_vm
);
CORO_FREE
(
ctx
);
return
js_mkerr
(
js
,
"failed to create async coroutine"
);
}
coroutine_t
*
coro
=
(
coroutine_t
*
)
CORO_MALLOC
(
sizeof
(
coroutine_t
));
if
(
!
coro
)
{
mco_destroy
(
mco
);
if
(
ctx
->
args
)
CORO_FREE
(
ctx
->
args
);
sv_vm_destroy
(
async_vm
);
CORO_FREE
(
ctx
);
return
js_mkerr
(
js
,
"out of memory for coroutine"
);
}
*
coro
=
(
coroutine_t
){
.
js
=
js
,
.
type
=
CORO_ASYNC_AWAIT
,
.
this_val
=
this_val
,
.
super_val
=
super_val
,
.
new_target
=
js
->
new_target
,
.
awaited_promise
=
js_mkundef
(),
.
result
=
js_mkundef
(),
.
async_func
=
callee_func
,
.
args
=
ctx
->
args
,
.
nargs
=
argc
,
.
active_parent
=
NULL
,
.
is_settled
=
false
,
.
is_error
=
false
,
.
is_done
=
false
,
.
resume_point
=
0
,
.
yield_value
=
js_mkundef
(),
.
async_promise
=
promise
,
.
next
=
NULL
,
.
mco
=
mco
,
.
owner_vm
=
async_vm
,
.
sv_vm
=
async_vm
,
.
mco_started
=
false
,
.
is_ready
=
true
,
.
did_suspend
=
false
,
.
refcount
=
1
,
.
hold_bits
=
0
,
.
await_registered
=
false
,
.
destroy_requested
=
false
,
};
ctx
->
coro
=
coro
;
enqueue_coroutine
(
coro
);
MCO_RESUME_SAVE
(
js
,
mco
,
res
);
mco_state
start_status
=
mco_status
(
mco
);
if
(
res
!=
MCO_SUCCESS
&&
start_status
!=
MCO_DEAD
)
{
remove_coroutine
(
coro
);
coroutine_release
(
coro
);
return
js_mkerr
(
js
,
"failed to start async coroutine"
);
}
coro
->
mco_started
=
true
;
if
(
start_status
==
MCO_DEAD
)
{
remove_coroutine
(
coro
);
}
coroutine_release
(
coro
);
return
promise
;
}
static
inline
sv_await_result_t
sv_await_value
(
sv_vm_t
*
vm
,
ant_t
*
js
,
ant_value_t
value
)
{
sv_await_result_t
out
=
{
.
state
=
SV_AWAIT_READY
,
.
value
=
js_mkundef
(),
.
handoff
=
false
,
};
value
=
js_promise_assimilate_awaitable
(
js
,
value
);
if
(
is_err
(
value
))
{
out
.
state
=
SV_AWAIT_ERROR
;
out
.
value
=
value
;
return
out
;
}
if
(
vtype
(
value
)
!=
T_PROMISE
)
{
out
.
value
=
value
;
return
out
;
}
mco_coro
*
current_mco
=
mco_running
();
if
(
!
current_mco
)
current_mco
=
NULL
;
coroutine_t
*
coro
=
NULL
;
if
(
current_mco
)
{
sv_coro_header_t
*
hdr
=
(
sv_coro_header_t
*
)
mco_get_user_data
(
current_mco
);
if
(
hdr
)
coro
=
hdr
->
coro
;
}
else
coro
=
sv_async_get_active_coro_for_vm
(
js
,
vm
);
if
(
!
coro
)
{
out
.
state
=
SV_AWAIT_ERROR
;
out
.
value
=
js_mkerr
(
js
,
"await can only be used inside async functions"
);
return
out
;
}
sv_vm_t
*
prepared_vm
=
NULL
;
bool
handoff
=
false
;
if
(
!
current_mco
&&
vm
&&
coro
->
owner_vm
==
vm
&&
!
coro
->
sv_vm
)
{
prepared_vm
=
sv_async_prepare_materialization
(
vm
,
js
,
coro
);
if
(
!
prepared_vm
)
{
out
.
state
=
SV_AWAIT_ERROR
;
out
.
value
=
js_mkerr
(
js
,
"out of memory for async VM"
);
return
out
;
}
handoff
=
true
;
}
coro
->
is_settled
=
false
;
coro
->
is_ready
=
false
;
js_await_result_t
await_result
=
js_promise_await_coroutine
(
js
,
value
,
coro
);
if
(
await_result
.
state
==
JS_AWAIT_ERROR
)
{
if
(
prepared_vm
)
{
sv_vm_destroy
(
prepared_vm
);
coro
->
sv_vm
=
NULL
;
coro
->
materialized
=
false
;
}
coro
->
is_settled
=
false
;
out
.
state
=
SV_AWAIT_ERROR
;
out
.
value
=
js_throw
(
js
,
await_result
.
value
);
return
out
;
}
coro
->
did_suspend
=
true
;
if
(
!
current_mco
)
{
if
(
prepared_vm
)
{
if
(
!
sv_async_materialize_activation
(
vm
,
prepared_vm
,
coro
))
{
coroutine_clear_await_registration
(
coro
);
sv_vm_destroy
(
prepared_vm
);
out
.
state
=
SV_AWAIT_ERROR
;
out
.
value
=
js_mkerr
(
js
,
"failed to materialize async activation"
);
return
out
;
}
}
out
.
state
=
SV_AWAIT_SUSPENDED
;
out
.
handoff
=
handoff
;
if
(
handoff
)
coro
->
sv_vm
->
suspended
=
true
;
else
if
(
coro
->
sv_vm
)
coro
->
sv_vm
->
suspended
=
true
;
return
out
;
}
mco_result
mco_res
=
mco_yield
(
current_mco
);
if
(
mco_res
!=
MCO_SUCCESS
)
{
out
.
state
=
SV_AWAIT_ERROR
;
out
.
value
=
js_mkerr
(
js
,
"failed to yield coroutine"
);
return
out
;
}
MCO_CORO_STACK_ENTER
(
js
,
current_mco
);
out
.
value
=
coro
->
result
;
bool
is_error
=
coro
->
is_error
;
coro
->
is_settled
=
false
;
coro
->
awaited_promise
=
js_mkundef
();
if
(
is_error
)
{
out
.
state
=
SV_AWAIT_ERROR
;
out
.
value
=
js_throw
(
js
,
out
.
value
);
return
out
;
}
out
.
state
=
SV_AWAIT_READY
;
return
out
;
}
#endif
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, May 2, 5:47 AM (1 d, 22 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
541376
Default Alt Text
async.h (21 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment