@@ -81,12 +81,13 @@ design provides a form of *symmetric switching*.
8181
8282We illustrate the proposed stack-switching mechanism using two
8383examples: 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.
8687
8788### Generators
8889
89- The first example illustrates a generator-consumer pattern. Execution
90+ Our first example illustrates a generator-consumer pattern. Execution
9091switches back and forth between a generator and a consumer execution
9192stack. Whenever execution switches from the generator to the consumer
9293the generator also passes a value to the consumer.
@@ -131,7 +132,7 @@ The overall module implementing our example has the following shape.
131132 ;; corresponds to the generated values passed to consumer; no values passed
132133 ;; back from generator to consumer.
133134 (tag $gen (param i32))
134-
135+
135136 ;; Simple generator yielding values from 100 down to 1
136137 (func $generator ...)
137138 (elem declare func $generator)
@@ -259,7 +260,7 @@ The full definition of this module can be found
259260
260261### Task scheduling
261262
262- The second example demonstrates how to implement task scheduling with
263+ Our second example demonstrates how to implement task scheduling with
263264the stack-switching instructions. Specifically, suppose we want to
264265schedule a number of tasks, represented by functions ` $task_0 ` to
265266` $task_n ` , to be executed concurrently. Scheduling is cooperative,
@@ -278,12 +279,10 @@ This approach is illustrated by the following skeleton code.
278279
279280``` wat
280281(module $scheduler1
281-
282282 (type $ft (func))
283283 ;; Continuation type of all tasks
284284 (type $ct (cont $ft))
285285
286-
287286 ;; Tag used to yield execution in one task and resume another one.
288287 (tag $yield)
289288
@@ -444,16 +443,16 @@ continuation references. The task that we switched to is now
444443responsible for enqueuing the previous continuation (i.e., the one
445444received as a payload) in the task list.
446445
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
451450takes a value of type ` (ref null $ct) ` as a parameter. In order to
452451give the same type to continuations that have yielded execution (those
453452created by ` switch ` ) and those continuations that correspond to
454453beginning the execution of a ` $task_i ` function (those created by
455454` 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
457456null continuation to any continuation it resumes, indicating to the
458457resumed continuation that there is no previous continuation to enqueue
459458in the task list.
@@ -686,34 +685,32 @@ suspended continuation will result in a trap.
686685
687686## Further examples
688687
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
6926913] ( #introduction-to-continuation-based-stack-switching ) .
693692
694-
695-
696693### Extending the generator
697694
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
700697` (tag $gen (param i32)) ` , to send values to the ` $producer ` function.
701698
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 :
707704
708705``` wat
709706(tag $gen (param i32) (result i32))
710707```
711708
712709In 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 ` :
717714
718715``` wat
719716 (func $generator
@@ -734,17 +731,14 @@ follows, choosing between resetting or decrementing `$i`:
734731 )
735732```
736733
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:
748742
749743``` wat
750744(type $ft0 (func))
@@ -759,12 +753,12 @@ to deal with two different continuation types:
759753(type $ct1 (cont $ft1))
760754```
761755
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.
768762
769763The overall function is then defined as follows:
770764
@@ -807,39 +801,34 @@ The overall function is then defined as follows:
807801)
808802```
809803
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).
813806
814807The full version of the extended generator example can be found
815808[ here] ( examples/generator-extended.wast ) .
816809
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
825811
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) .
830816
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.
835824
836825``` wat
837826(func $schedule_task (param $c (ref null $ct))
838827 ;; If the task queue is too long, cancel a task in the queue
839828 (if (i32.ge_s (call $task_queue-count) (global.get $concurrent_task_limit))
840829 (then
841830 (block $exc_handler
842- (try_table (catch $abort $exc_handler)
831+ (try_table (catch $abort $exc_handler)
843832 (resume_throw $ct $abort (call $task_dequeue))
844833 )
845834 )
@@ -849,26 +838,24 @@ should be scheduled.
849838)
850839```
851840
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.
855845
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
859848suspension point of the continuation. We then wrap the ` resume_throw `
860849instruction 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.
872859
873860## Design considerations
874861
0 commit comments