Skip to content

Commit 0e7fece

Browse files
authored
Clean up yield inheritance (#115)
Clean up yield inheritance: 1. Key the scheduling state based on the {{Scheduler}} to prevent leaking it across (potentially cross-origin) windows. This changes the event loop's continuation state to be a small wrapper around a map. The continuation state is propagated in the same way, but the scheduler state is unique to the scheduler and not shared. In practice there will only be one entry in this map (a task or microtask can only have originated from one task), but the mechanism is generic enough to support other use cases, implementations can optimize this, and the key/value mapping hopefully makes the isolation clear. 2. Propagate the current scheduling state in "queue a microtask", unless coming from JavaScript, in which case the propagation is handled by the abstract job closure. Previously, the state would be inherited only if it wasn't reset by another microtask or after the postTask callback ran. This fixes the inconsistency, making directly scheduled microtasks match microtasks originating from JavaScript.
1 parent d7e060c commit 0e7fece

File tree

2 files changed

+92
-16
lines changed

2 files changed

+92
-16
lines changed

spec/patches.md

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,30 @@ determine task execution order across [=scheduler task queues=] of the same {{Ta
3232
all {{Scheduler}}s associated with the same [=event loop=]. A timestamp would also suffice as long
3333
as it is guaranteed to be strictly increasing and unique.
3434

35-
Add: An [=event loop=] has a <dfn for="event loop">current scheduling state</dfn> (a [=scheduling
36-
state=] or null), which is initialized to null.
35+
Add: An [=event loop=] has a <dfn for="event loop">current continuation state</dfn> (a
36+
[=continuation state=] or null), which is initially null.
37+
38+
Add the following algorithms:
39+
40+
<div algorithm>
41+
To <dfn>set the continuation state value</dfn> for |key| to |value| given an |eventLoop| (an
42+
[=event loop=]):
43+
44+
1. If |eventLoop|'s [=event loop/current continuation state=] is null, then set |eventLoop|'s
45+
[=event loop/current continuation state=] to a new [=continuation state=].
46+
1. Let |continuationState| be |eventLoop|'s [=event loop/current continuation state=].
47+
1. Assert: |continuationState|'s [=continuation state/state map=][|key|] does not [=map/exist=].
48+
1. Set |continuationState|'s [=continuation state/state map=][|key|] to |value|.
49+
</div>
50+
51+
<div algorithm>
52+
To <dfn>get the continuation state value</dfn> for |key| given an |eventLoop| (an [=event loop=]):
53+
54+
1. Let |continuationState| be |eventLoop|'s [=event loop/current continuation state=].
55+
1. If |continuationState| is not null and |continuationState|'s
56+
[=continuation state/state map=][|key|] [=map/exists=], then return |continuationState|'s
57+
[=continuation state/state map=][|key|], otherwise return null.
58+
</div>
3759

3860
### <a href="https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model">Event loop: processing model</a> ### {#sec-patches-html-event-loop-processing}
3961

@@ -80,33 +102,59 @@ Issue: The |taskQueue| in this step will either be a [=set=] of [=tasks=] or a [
80102
*roughly* compatible. Ideally, there would be a common task queue interface that supports a `pop()`
81103
method that would return a plain [=task=], but that would involve a fair amount of refactoring.
82104

105+
### <a href="https://html.spec.whatwg.org/#queuing-tasks">Event Loop: Queuing Tasks</a> ### {#sec-patches-html-queuing-tasks}
106+
107+
Change the <a href="https://html.spec.whatwg.org/#queue-a-microtask">To queue a microtask</a>
108+
algorithm to accept an optional boolean |ignoreContinuationState| (default false).
109+
110+
Change Step 5 to the following:
111+
112+
1. Let |continuationState| be null.
113+
1. If |ignoreContinuationState| is false and |eventLoop|'s
114+
[=event loop/current continuation state=] is not null, then set |continuationState| to the
115+
result of [=list/cloning=] |event loop|'s [=event loop/current continuation state=].
116+
1. Set <var ignore=''>microtask</var>'s <a attribute for="task">steps</a> to the following:
117+
1. If |ignoreContinuationState| is false, then set |eventLoop|'s
118+
[=event loop/current continuation state=] to |continuationState|.
119+
1. Run <var ignore=''>steps</var>.
120+
1. If |ignoreContinuationState| is false, then set |eventLoop|'s
121+
[=event loop/current continuation state=] to null.
122+
83123
### <a href="https://html.spec.whatwg.org/multipage/webappapis.html#hostmakejobcallback">HostMakeJobCallback(callable)</a> ### {#sec-patches-html-hostmakejobcallback}
84124

85125
Add the following before step 5:
86126

87127
1. Let |event loop| be <var ignore=''>incumbent settings<var>'s
88128
[=environment settings object/realm=]'s [=realm/agent=]'s [=agent/event loop=].
89-
1. Let |state| be |event loop|'s [=event loop/current scheduling state=].
129+
1. Let |state| be the result of [=list/cloning=] |event loop|'s
130+
[=event loop/current continuation state=] if [=event loop/current continuation state=] is not
131+
null, or otherwise null.
90132

91133
Modify step 5 to read:
92134

93135
1. Return the <span>JobCallback Record</span> { \[[Callback]]: <var ignore=''>callable</var>,
94136
\[[HostDefined]]: { \[[IncumbentSettings]]: <var ignore=''>incumbent settings</var>,
95137
\[[ActiveScriptContext]]: <var ignore=''>script execution context</var>,
96-
\[[SchedulingState]]: |state| } }.
138+
\[[ContinuationState]]: |state| } }.
97139

98140
### <a href="https://html.spec.whatwg.org/multipage/webappapis.html#hostcalljobcallback">HostCallJobCallback(callback, V, argumentsList)</a> ### {#sec-patches-html-hostcalljobcallback}
99141

100142
Add the following steps before step 5:
101143

102144
1. Let |event loop| be <var ignore=''>incumbent settings<var>'s
103145
[=environment settings object/realm=]'s [=realm/agent=]'s [=agent/event loop=].
104-
1. Set |event loop|'s [=event loop/current scheduling state=] to
105-
<var ignore=''>callback</var>.\[[HostDefined]].\[[SchedulingState]].
146+
1. Set |event loop|'s [=event loop/current continuation state=] to
147+
<var ignore=''>callback</var>.\[[HostDefined]].\[[ContinuationState]].
106148

107149
Add the following after step 7:
108150

109-
1. Set |event loop|'s [=event loop/current scheduling state=] to null.
151+
1. Set |event loop|'s [=event loop/current continuation state=] to null.
152+
153+
### <a href="https://html.spec.whatwg.org/multipage/webappapis.html#hostenqueuepromisejob">HostEnqueuePromiseJob(job, realm)</a> ### {#sec-patches-html-hostenqueuepromisejob}
154+
155+
Change step 2 to:
156+
157+
1. Queue a microtask to perform the following steps with |ignoreContinuationState| set to true:
110158

111159
## <a href="https://w3c.github.io/requestidlecallback/">`requestIdleCallback()`</a> ## {#sec-patches-requestidlecallback}
112160

@@ -118,9 +166,9 @@ Add the following step before step 3.3:
118166
1. Let |state| be a new [=scheduling state=].
119167
1. Set |state|'s [=scheduling state/priority source=] to the result of [=creating a fixed priority
120168
unabortable task signal=] given "{{TaskPriority/background}}" and |realm|.
121-
1. Let |event loop| be |realm|'s [=realm/agent=]'s [=agent/event loop=].
122-
1. Set |event loop|'s [=event loop/current scheduling state=] to |state|.
169+
1. Let |scheduler| be the {{Scheduler}} whose [=relevant realm=] is |realm|.
170+
1. [=Set the current scheduling state=] for |scheduler| to |state|.
123171

124172
Add the following after step 3.3:
125173

126-
1. Set |event loop|'s [=event loop/current scheduling state=] to null.
174+
1. Set |event loop|'s [=event loop/current continuation state=] to null.

spec/scheduling-tasks.md

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,12 +175,22 @@ A <dfn>scheduler task queue</dfn> is a [=struct=] with the following [=struct/it
175175
A <dfn>scheduling state</dfn> is a [=struct=] with the following [=struct/items=]:
176176

177177
: <dfn for="scheduling state">abort source</dfn>
178-
:: An {{AbortSignal}} object or, initially null.
178+
:: An {{AbortSignal}} object or null, initially null.
179179
: <dfn for="scheduling state">priority source</dfn>
180180
:: A {{TaskSignal}} object or null, initially null.
181181

182182
<br/>
183183

184+
A <dfn>continuation state</dfn> is a [=struct=] with the following [=struct/items=]:
185+
186+
: <dfn for="continuation state">state map</dfn>
187+
:: An initially empty [=map=].
188+
189+
Note: The [=continuation state/state map=] can be implemented as weak map if its keys are
190+
implemented as garbage collected objects.
191+
192+
<br/>
193+
184194
A <dfn>task handle</dfn> is a [=struct=] with the following [=struct/items=]:
185195

186196
: <dfn for="task handle">task</dfn>
@@ -289,6 +299,25 @@ A <dfn>task handle</dfn> is a [=struct=] with the following [=struct/items=]:
289299

290300
### Scheduling Tasks and Continuations ### {#sec-scheduler-alg-scheduling-tasks-and-continuations}
291301

302+
<div algorithm>
303+
To <dfn>set the current scheduling state</dfn> for |scheduler| (a {{Scheduler}}) to |state| (a
304+
[=scheduling state=]):
305+
306+
1. Let |eventLoop| be |scheduler|'s [=relevant agent=]'s [=agent/event loop=].
307+
1. [=Set the continuation state value=] for |scheduler| to |state| given |eventLoop|.
308+
309+
Note: Any key can be used for the [=continuation state/state map=] as long as it is unique to the
310+
{{Scheduler}}.
311+
</div>
312+
313+
<div algorithm>
314+
To <dfn>get the current scheduling state</dfn> for |scheduler| (a {{Scheduler}}):
315+
316+
1. Let |eventLoop| be |scheduler|'s [=relevant agent=]'s [=agent/event loop=].
317+
1. Return the result of [=getting the continuation state value=] for |scheduler| given
318+
|eventLoop|.
319+
</div>
320+
292321
<div algorithm>
293322
To <dfn>schedule a postTask task</dfn> for {{Scheduler}} |scheduler| given a
294323
{{SchedulerPostTaskCallback}} |callback| and {{SchedulerPostTaskOptions}} |options|:
@@ -316,12 +345,12 @@ A <dfn>task handle</dfn> is a [=struct=] with the following [=struct/items=]:
316345
for |scheduler| given |state|'s [=scheduling state/priority source=] and false.
317346
1. [=Schedule a task to invoke an algorithm=] for |scheduler| given |handle| and the following
318347
steps:
319-
1. Let |event loop| be the |scheduler|'s [=relevant agent=]'s [=agent/event loop=].
320-
1. Set |event loop|'s [=event loop/current scheduling state=] to |state|.
348+
1. Let |eventLoop| be the |scheduler|'s [=relevant agent=]'s [=agent/event loop=].
349+
1. [=Set the current scheduling state=] for |scheduler| to |state|.
321350
1. Let |callbackResult| be the result of [=invoking=] |callback| with « » and "`rethrow`".
322351
If that threw an exception, then [=reject=] |result| with that. Otherwise, [=resolve=]
323352
|result| with |callbackResult|.
324-
1. Set |event loop|'s [=event loop/current scheduling state=] to null.
353+
1. Set |eventLoop|'s [=event loop/current continuation state=] to null.
325354
1. Let |delay| be |options|["{{SchedulerPostTaskOptions/delay}}"].
326355
1. If |delay| is greater than 0, then [=run steps after a timeout=] given |scheduler|'s [=relevant
327356
global object=], "`scheduler-postTask`", |delay|, and the following steps:
@@ -340,8 +369,7 @@ Issue: [=Run steps after a timeout=] doesn't necessarily account for suspension;
340369
To <dfn>schedule a yield continuation</dfn> for {{Scheduler}} |scheduler|:
341370

342371
1. Let |result| be [=a new promise=].
343-
1. Let |inheritedState| be the |scheduler|'s [=relevant agent=]'s [=agent/event loop=]'s
344-
[=event loop/current scheduling state=].
372+
1. Let |inheritedState| be the result of [=getting the current scheduling state=] for |scheduler|.
345373
1. Let |abortSource| be |inheritedState|'s [=scheduling state/abort source=] if |inheritedState|
346374
is not null, or otherwise null.
347375
1. If |abortSource| is not null and |abortSource| is [=AbortSignal/aborted=], then [=reject=]

0 commit comments

Comments
 (0)