@@ -81,12 +81,13 @@ design provides a form of *symmetric switching*.
81
81
82
82
We illustrate the proposed stack-switching mechanism using two
83
83
examples: generators and task scheduling. The generators example uses
84
- asymmetric stack-switching and the task scheduling example uses
85
- symmetric stack-switching.
84
+ asymmetric stack-switching. The task scheduling example has two
85
+ variants: the first variant uses asymmetric stack-switching and the
86
+ second variant uses symmetric stack-switching.
86
87
87
88
### Generators
88
89
89
- The first example illustrates a generator-consumer pattern. Execution
90
+ Our first example illustrates a generator-consumer pattern. Execution
90
91
switches back and forth between a generator and a consumer execution
91
92
stack. Whenever execution switches from the generator to the consumer
92
93
the generator also passes a value to the consumer.
@@ -131,7 +132,7 @@ The overall module implementing our example has the following shape.
131
132
;; corresponds to the generated values passed to consumer; no values passed
132
133
;; back from generator to consumer.
133
134
(tag $gen (param i32))
134
-
135
+
135
136
;; Simple generator yielding values from 100 down to 1
136
137
(func $generator ...)
137
138
(elem declare func $generator)
@@ -259,7 +260,7 @@ The full definition of this module can be found
259
260
260
261
### Task scheduling
261
262
262
- The second example demonstrates how to implement task scheduling with
263
+ Our second example demonstrates how to implement task scheduling with
263
264
the stack-switching instructions. Specifically, suppose we want to
264
265
schedule a number of tasks, represented by functions ` $task_0 ` to
265
266
` $task_n ` , to be executed concurrently. Scheduling is cooperative,
@@ -278,12 +279,10 @@ This approach is illustrated by the following skeleton code.
278
279
279
280
``` wat
280
281
(module $scheduler1
281
-
282
282
(type $ft (func))
283
283
;; Continuation type of all tasks
284
284
(type $ct (cont $ft))
285
285
286
-
287
286
;; Tag used to yield execution in one task and resume another one.
288
287
(tag $yield)
289
288
@@ -444,16 +443,16 @@ continuation references. The task that we switched to is now
444
443
responsible for enqueuing the previous continuation (i.e., the one
445
444
received as a payload) in the task list.
446
445
447
- As a minor complication, we need to encode the fact that the
448
- continuation switched to receives the current one as an argument in
449
- the type of the continuations handled by all scheduling logic. This
450
- means the type ` $ct ` must be recursive: a continuation of this type
446
+ As a minor complication, we must encode the fact that the continuation
447
+ switched to receives the current continuation as an argument in the
448
+ type of the continuations handled by all scheduling logic. This means
449
+ that the type ` $ct ` must be recursive: a continuation of this type
451
450
takes a value of type ` (ref null $ct) ` as a parameter. In order to
452
451
give the same type to continuations that have yielded execution (those
453
452
created by ` switch ` ) and those continuations that correspond to
454
453
beginning the execution of a ` $task_i ` function (those created by
455
454
` cont.new ` ), we add a ` (ref null $ct) ` parameter to all of the
456
- ` $task_i ` functions. Finally, observe that the event loop passes a
455
+ ` $task_i ` functions. Finally, observe that the event loop passes a
457
456
null continuation to any continuation it resumes, indicating to the
458
457
resumed continuation that there is no previous continuation to enqueue
459
458
in the task list.
@@ -686,34 +685,32 @@ suspended continuation will result in a trap.
686
685
687
686
## Further examples
688
687
689
- We now provide examples using tags with result values as well as the
690
- instructions ` cont.bind ` and ` resume.throw ` .
691
- To this end, we revisit the examples from [ Section
688
+ We now illustrate the use of tags with result values and the
689
+ instructions ` cont.bind ` and ` resume.throw ` , by adapting and extending
690
+ the examples of [ Section
692
691
3] ( #introduction-to-continuation-based-stack-switching ) .
693
692
694
-
695
-
696
693
### Extending the generator
697
694
698
- The ` $generator ` function introduced in [ Section 3] ( #generators )
699
- produced the values 100 down to 1. It uses the tag ` $gen ` , defined as
695
+ The ` $generator ` function in [ Section 3] ( #generators )
696
+ produces the values 100 down to 1. It uses the tag ` $gen ` , defined as
700
697
` (tag $gen (param i32)) ` , to send values to the ` $producer ` function.
701
698
702
- We now consider a situation where the producer wants to indicate to
703
- the generator to reset itself (i.e., start counting down from 100
704
- again). To this end, we allow the producer to pass a boolean flag to
705
- the generator when resuming a continuation. We use type ` i32 ` for the
706
- flag, leading to the following definition of ` $gen ` :
699
+ We now adapt the producer to indicate to the generator when to reset
700
+ (i.e., start counting down from 100 again). The producer is adapted to
701
+ pass a boolean flag to the generator when resuming a continuation.
702
+ Correspondingly, the ` $gen ` tag is adapted to include an ` i32 ` result
703
+ type for the flag :
707
704
708
705
``` wat
709
706
(tag $gen (param i32) (result i32))
710
707
```
711
708
712
709
In the generator, the instruction ` (suspend $gen) ` now has type `[ i32]
713
- -> [ i32] `: Its argument type represents the generated value (as in the
714
- original version of the example), the result type represents the flag
715
- obtained back from the producer. We change the generator to behave as
716
- follows, choosing between resetting or decrementing ` $i ` :
710
+ -> [ i32] `: the parameter type represents the generated value (as in
711
+ the original version of the example) and the result type represents
712
+ the flag obtained back from the producer. We adapt the generator to
713
+ behave as follows, choosing between resetting or decrementing ` $i ` :
717
714
718
715
``` wat
719
716
(func $generator
@@ -734,17 +731,14 @@ follows, choosing between resetting or decrementing `$i`:
734
731
)
735
732
```
736
733
737
-
738
- In the producer, we then add some logic to pick the value of the flag,
739
- and pass it to the generator continuation on ` resume ` . However, this
740
- poses a challenge: The continuation created with `(cont.new $ct0
741
- (ref.func $generator)))` has the same type as before: A continuation
742
- type with no parameter or return types. In contrast, the type of the
743
- continuation received in a handler block for tag ` $gen ` is different
744
- from that type, due to the result type added to ` $gen ` : The result
745
- type of the tag becomes an additional parameter of the continuation
746
- received when handling that tag. This means that the producer now has
747
- to deal with two different continuation types:
734
+ In the producer, we add logic to select the value of the flag, and
735
+ pass it to the generator continuation on ` resume ` . However, this poses
736
+ a challenge: the continuation created with `(cont.new $ct0 (ref.func
737
+ $generator)))` has the same type as before: a continuation type with
738
+ no parameter or return types. In contrast, the type of the
739
+ continuation received in a handler block for tag ` $gen ` expects an
740
+ ` i32 ` due to the result type we added to ` $gen ` . This means that the
741
+ producer must now manipulate two different continuation types:
748
742
749
743
``` wat
750
744
(type $ft0 (func))
@@ -759,12 +753,12 @@ to deal with two different continuation types:
759
753
(type $ct1 (cont $ft1))
760
754
```
761
755
762
- To avoid making the producer function unnecessarily complicated, we
763
- want to make sure that there is only a single local variable that
764
- contains the next continuation to resume. Its type will be ` (ref $ct0) ` .
765
- We can then use ` cont.bind ` to turn the continuations received in the
766
- handler block from type ` (ref $ct1) ` into ` (ref $ct0) ` by binding the
767
- value of the flag to be passed.
756
+ In order to avoid making the producer function unnecessarily
757
+ complicated, we ensure that there is a single local variable that
758
+ contains the next continuation to resume; its type will be `(ref
759
+ $ct0) ` . We can then use ` cont.bind` to turn the continuations received
760
+ in the handler block from type ` (ref $ct1) ` into ` (ref $ct0) ` by
761
+ binding the value of the flag to be passed.
768
762
769
763
The overall function is then defined as follows:
770
764
@@ -807,39 +801,34 @@ The overall function is then defined as follows:
807
801
)
808
802
```
809
803
810
-
811
- Here, we set the flag for resetting the generator exactly
812
- once, after it returned 42 values.
804
+ Here, we set the flag for resetting the generator exactly once (after
805
+ it has returned 42 values).
813
806
814
807
The full version of the extended generator example can be found
815
808
[ here] ( examples/generator-extended.wast ) .
816
809
817
- ### Canceling tasks
818
-
819
- We now revisit the task scheduling example originally introduced
820
- in [ Section 3] ( #task-scheduling ) .
821
- To yield execution, tasks either suspend to the scheduler running in
822
- the parent (first variant of example) or call a scheduling function
823
- that uses ` switch ` (second variant).
824
-
810
+ ### Canceling tasks
825
811
826
- We may want to adapt the example such that there is a limit on the
827
- number of tasks that can exist at the same time. Once that limit is
828
- reached, some task must be canceled before being able to schedule
829
- another one .
812
+ The task scheduling examples from [ Section 3 ] ( #task-scheduling ) yield
813
+ either by suspending to the scheduler running in the parent
814
+ (asymmetric variant) or by calling a scheduling function that uses
815
+ ` switch ` (symmetric variant) .
830
816
831
- We can implement this with a small addition to the previous example.
832
- Instead of adding tasks to be scheduled directly to a queue, we
833
- call the following function ` $schedule_task ` with any continuation that
834
- should be scheduled.
817
+ Suppose we wish to adapt our schedulers to impose a limit on the
818
+ number of tasks that can exist at the same time. A simple way to
819
+ enforce the limit is to cancel the task at the head of the queue
820
+ whenever an attempt is made to add a continuation to a full queue. We
821
+ can implement this behaviour with a small modification to our previous
822
+ schedulers. Instead of adding tasks to be scheduled directly to a
823
+ queue, we call the following function.
835
824
836
825
``` wat
837
826
(func $schedule_task (param $c (ref null $ct))
838
827
;; If the task queue is too long, cancel a task in the queue
839
828
(if (i32.ge_s (call $task_queue-count) (global.get $concurrent_task_limit))
840
829
(then
841
830
(block $exc_handler
842
- (try_table (catch $abort $exc_handler)
831
+ (try_table (catch $abort $exc_handler)
843
832
(resume_throw $ct $abort (call $task_dequeue))
844
833
)
845
834
)
@@ -849,26 +838,24 @@ should be scheduled.
849
838
)
850
839
```
851
840
852
- The function checks if the current number of elements in the queue has
853
- already reached the limit. If so, the function takes an existing
854
- continuation from the queue and calls ` resume_throw ` on it.
841
+ The ` $schedule_task ` function checks if the current number of elements
842
+ in the queue has already reached the limit. If so, the function takes
843
+ an existing continuation from the queue and calls ` resume_throw ` on
844
+ it.
855
845
856
- Note that
857
- the ` resume_throw ` instruction is annotated with a newly defined tag, ` $abort ` .
858
- This tag denotes an exception that will be raised at the
846
+ The ` resume_throw ` instruction is annotated with a newly defined tag,
847
+ ` $abort ` . This tag denotes an exception that will be raised at the
859
848
suspension point of the continuation. We then wrap the ` resume_throw `
860
849
instruction in a ` try_table ` , which installs an exception handler for
861
- ` $abort ` . This exception handler simply does nothing.
862
- Altogether this means that the exception raised at the suspension
863
- point cannot escape outside of the ` $schedule_task ` function, which
864
- then proceeds to enqueue the continuation given as a function
865
- argument.
866
-
867
- Integrating the ` $schedule_task ` function above into the second
868
- variant of the task scheduling example (i.e., the variant using
869
- ` switch ` ) can be found [ here] ( examples/scheduler2-throw.wast ) .
870
- The changes to the first variant are analogous.
871
-
850
+ ` $abort ` . This exception handler simply swallows the exception, which
851
+ means that the exception raised at the suspension point cannot escape
852
+ the ` $schedule_task ` function. The old continuation is deallocated and
853
+ the function proceeds to enqueue the new continuation.
854
+
855
+ The full version of the symmetric scheduling example using the
856
+ ` $schedule_task ` function can be found
857
+ [ here] ( examples/scheduler2-throw.wast ) . The changes to the asymmetric
858
+ scheduling example are analogous.
872
859
873
860
## Design considerations
874
861
0 commit comments