Skip to content

Commit 0d115b4

Browse files
authored
[interpreter] Threading (#179)
* New script constructs * Implement script threads * Implement Wasm threads * JS threading * JS either * Explicit sharing * Simplify script hack * Remove Implicit * Comments
1 parent 3b7a4e9 commit 0d115b4

File tree

19 files changed

+560
-265
lines changed

19 files changed

+560
-265
lines changed

interpreter/README.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -374,9 +374,14 @@ binscript: <cmd>*
374374
cmd:
375375
<module> ;; define, validate, and initialize module
376376
( register <string> <name>? ) ;; register module for imports
377+
( thread <name>? <shared>* cmd* ) ;; spawn thread
378+
( wait <name> ) ;; join thread
377379
<action> ;; perform action and print results
378380
<assertion> ;; assert result of an action
379381
382+
shared:
383+
( shared ( module <name> ) )
384+
380385
module:
381386
( module <name>? binary <string>* ) ;; module in binary format (may be malformed)
382387
@@ -394,7 +399,8 @@ assertion:
394399
( assert_trap <module> <failure> ) ;; assert module traps on instantiation
395400
396401
result:
397-
( <val_type>.const <numpat> )
402+
( <val_type>.const <numpat> ) ;; numeric result
403+
( either result+ ) ;; non-deterministic result
398404
399405
numpat:
400406
<value> ;; literal result

interpreter/exec/eval.ml

+57-30
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,34 @@ and admin_instr' =
5757
| Label of int32 * instr list * code
5858
| Frame of int32 * frame * code
5959

60-
type config =
60+
type thread =
6161
{
6262
frame : frame;
6363
code : code;
6464
budget : int; (* to model stack overflow *)
6565
}
6666

67+
type config = thread list
68+
type thread_id = int
69+
type status = Running | Result of value list | Trap of exn
70+
6771
let frame inst locals = {inst; locals}
68-
let config inst vs es = {frame = frame inst []; code = vs, es; budget = 300}
72+
let thread inst vs es = {frame = frame inst []; code = vs, es; budget = 300}
73+
let empty_thread = thread empty_module_inst [] []
74+
let empty_config = []
75+
let spawn (c : config) = List.length c, c @ [empty_thread]
76+
77+
let status (c : config) (n : thread_id) : status =
78+
let t = List.nth c n in
79+
match t.code with
80+
| vs, [] -> Result (List.rev vs)
81+
| [], {it = Trapping msg; at} :: _ -> Trap (Trap.Error (at, msg))
82+
| _ -> Running
83+
84+
let clear (c : config) (n : thread_id) : config =
85+
let ts1, t, ts2 = Lib.List.extract n c in
86+
ts1 @ [{t with code = [], []}] @ ts2
87+
6988

7089
let plain e = Plain e.it @@ e.at
7190

@@ -122,11 +141,12 @@ let check_align addr ty sz at =
122141
* v : value
123142
* es : instr list
124143
* vs : value stack
144+
* t : thread
125145
* c : config
126146
*)
127147

128-
let rec step (c : config) : config =
129-
let {frame; code = vs, es; _} = c in
148+
let rec step_thread (t : thread) : thread =
149+
let {frame; code = vs, es; _} = t in
130150
let e = List.hd es in
131151
let vs', es' =
132152
match e.it, vs with
@@ -355,7 +375,7 @@ let rec step (c : config) : config =
355375
)
356376

357377
| Trapping msg, vs ->
358-
assert false
378+
[], [Trapping msg @@ e.at]
359379

360380
| Returning vs', vs ->
361381
Crash.error e.at "undefined frame"
@@ -379,8 +399,8 @@ let rec step (c : config) : config =
379399
vs, [Breaking (Int32.sub k 1l, vs0) @@ at]
380400

381401
| Label (n, es0, code'), vs ->
382-
let c' = step {c with code = code'} in
383-
vs, [Label (n, es0, c'.code) @@ e.at]
402+
let t' = step_thread {t with code = code'} in
403+
vs, [Label (n, es0, t'.code) @@ e.at]
384404

385405
| Frame (n, frame', (vs', [])), vs ->
386406
vs' @ vs, []
@@ -392,10 +412,10 @@ let rec step (c : config) : config =
392412
take n vs0 e.at @ vs, []
393413

394414
| Frame (n, frame', code'), vs ->
395-
let c' = step {frame = frame'; code = code'; budget = c.budget - 1} in
396-
vs, [Frame (n, c'.frame, c'.code) @@ e.at]
415+
let t' = step_thread {frame = frame'; code = code'; budget = t.budget - 1} in
416+
vs, [Frame (n, t'.frame, t'.code) @@ e.at]
397417

398-
| Invoke func, vs when c.budget = 0 ->
418+
| Invoke func, vs when t.budget = 0 ->
399419
Exhaustion.error e.at "call stack exhausted"
400420

401421
| Invoke func, vs ->
@@ -413,37 +433,44 @@ let rec step (c : config) : config =
413433
try List.rev (f (List.rev args)) @ vs', []
414434
with Crash (_, msg) -> Crash.error e.at msg
415435
)
416-
in {c with code = vs', es' @ List.tl es}
417-
436+
in {t with code = vs', es' @ List.tl es}
418437

419-
let rec eval (c : config) : value stack =
420-
match c.code with
421-
| vs, [] ->
422-
vs
423438

424-
| vs, {it = Trapping msg; at} :: _ ->
425-
Trap.error at msg
439+
let rec step (c : config) (n : thread_id) : config =
440+
let ts1, t, ts2 = Lib.List.extract n c in
441+
if snd t.code = [] then
442+
step c n
443+
else
444+
let t' = try step_thread t with Stack_overflow ->
445+
Exhaustion.error (List.hd (snd t.code)).at "call stack exhausted"
446+
in ts1 @ [t'] @ ts2
426447

427-
| vs, es ->
428-
eval (step c)
448+
let rec eval (c : config ref) (n : thread_id) : value list =
449+
match status !c n with
450+
| Result vs -> vs
451+
| Trap e -> raise e
452+
| Running ->
453+
let c' = step !c n in
454+
c := c'; eval c n
429455

430456

431457
(* Functions & Constants *)
432458

433-
let invoke (func : func_inst) (vs : value list) : value list =
459+
let invoke c n (func : func_inst) (vs : value list) : config =
434460
let at = match func with Func.AstFunc (_,_, f) -> f.at | _ -> no_region in
435461
let FuncType (ins, out) = Func.type_of func in
436462
if List.map Values.type_of vs <> ins then
437463
Crash.error at "wrong number or types of arguments";
438-
let c = config empty_module_inst (List.rev vs) [Invoke func @@ at] in
439-
try List.rev (eval c) with Stack_overflow ->
440-
Exhaustion.error at "call stack exhausted"
464+
let ts1, t, ts2 = Lib.List.extract n c in
465+
let vs', es' = t.code in
466+
let code = List.rev vs @ vs', (Invoke func @@ at) :: es' in
467+
ts1 @ [{t with code}] @ ts2
441468

442469
let eval_const (inst : module_inst) (const : const) : value =
443-
let c = config inst [] (List.map plain const.it) in
444-
match eval c with
470+
let t = thread inst [] (List.map plain const.it) in
471+
match eval (ref [t]) 0 with
445472
| [v] -> v
446-
| vs -> Crash.error const.at "wrong number of results on stack"
473+
| _ -> Crash.error const.at "wrong number of results on stack"
447474

448475
let i32 (v : value) at =
449476
match v with
@@ -518,7 +545,7 @@ let add_import (m : module_) (ext : extern) (im : import) (inst : module_inst)
518545
| ExternMemory mem -> {inst with memories = mem :: inst.memories}
519546
| ExternGlobal glob -> {inst with globals = glob :: inst.globals}
520547

521-
let init (m : module_) (exts : extern list) : module_inst =
548+
let init c n (m : module_) (exts : extern list) : module_inst * config =
522549
let
523550
{ imports; tables; memories; globals; funcs; types;
524551
exports; elems; data; start
@@ -545,5 +572,5 @@ let init (m : module_) (exts : extern list) : module_inst =
545572
let init_datas = List.map (init_memory inst) data in
546573
List.iter (fun f -> f ()) init_elems;
547574
List.iter (fun f -> f ()) init_datas;
548-
Lib.Option.app (fun x -> ignore (invoke (func inst x) [])) start;
549-
inst
575+
let c' = Lib.Option.fold c (fun x -> invoke c n (func inst x) []) start in
576+
inst, c'

interpreter/exec/eval.mli

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
open Values
22
open Instance
33

4+
type config
5+
type thread_id
6+
type status = Running | Result of value list | Trap of exn
7+
48
exception Link of Source.region * string
59
exception Trap of Source.region * string
610
exception Crash of Source.region * string
711
exception Exhaustion of Source.region * string
812

9-
val init : Ast.module_ -> extern list -> module_inst (* raises Link, Trap *)
10-
val invoke : func_inst -> value list -> value list (* raises Trap *)
13+
val empty_config : config
14+
15+
val spawn : config -> thread_id * config
16+
val clear : config -> thread_id -> config
17+
val status : config -> thread_id -> status
18+
val step : config -> thread_id -> config
19+
val invoke : config -> thread_id -> func_inst -> value list -> config (* raises Trap *)
20+
val init : config -> thread_id -> Ast.module_ -> extern list -> module_inst * config (* raises Link, Trap *)

interpreter/main/main.ml

+5-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ let argspec = Arg.align
3131
"-h", Arg.Clear Flags.harness, " exclude harness for JS conversion";
3232
"-d", Arg.Set Flags.dry, " dry, do not run program";
3333
"-t", Arg.Set Flags.trace, " trace execution";
34+
"-r", Arg.Int Random.init, " set non-determinism random seed";
35+
"-rr", Arg.Unit Random.self_init, " randomize non-determinism random seed";
3436
"-v", Arg.Unit banner, " show version"
3537
]
3638

@@ -40,12 +42,13 @@ let () =
4042
configure ();
4143
Arg.parse argspec
4244
(fun file -> add_arg ("(input " ^ quote file ^ ")")) usage;
43-
List.iter (fun arg -> if not (Run.run_string arg) then exit 1) !args;
45+
let context = Run.context () in
46+
List.iter (fun arg -> if not (Run.run_string context arg) then exit 1) !args;
4447
if !args = [] then Flags.interactive := true;
4548
if !Flags.interactive then begin
4649
Flags.print_sig := true;
4750
banner ();
48-
Run.run_stdin ()
51+
Run.run_stdin context
4952
end
5053
with exn ->
5154
flush_all ();

interpreter/runtime/instance.ml

+7
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,10 @@ let extern_type_of = function
3939

4040
let export inst name =
4141
try Some (List.assoc name inst.exports) with Not_found -> None
42+
43+
let shared_export (_, ex) = shared_extern_type (extern_type_of ex)
44+
let shared_module inst =
45+
if List.for_all (fun ex -> shared_export ex = Shared) inst.exports then
46+
Shared
47+
else
48+
Unshared

0 commit comments

Comments
 (0)