Skip to content

Commit

Permalink
Editorial: expose "run steps after a timeout"
Browse files Browse the repository at this point in the history
This factors out a new algorithm which can be used by postTask() (https://wicg.github.io/scheduling-apis/#schedule-a-posttask-task) and AbortSignal.timeout() (whatwg/dom#1032), ensuring that they correctly contribute to idle deadline computation and in general share all the appropriate logic with setTimeout() and setInterval().

This also exports the "timer task source" term since AbortSignal.abort() will want to use that.
  • Loading branch information
domenic authored and mfreed7 committed Jun 3, 2022
1 parent 4d1a90b commit bcddf41
Showing 1 changed file with 91 additions and 50 deletions.
141 changes: 91 additions & 50 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -93968,7 +93968,7 @@ import "https://example.com/foo/../module2.mjs";</code></pre>
</ol>
</li>

<li>
<li id="idle-deadline-computation">
<p>If all of the following are true

<ul class="brief">
Expand Down Expand Up @@ -96460,12 +96460,21 @@ enum <dfn enum>DOMParserSupportedType</dfn> {

<div w-nodev>

<p>Objects that implement the <code>WindowOrWorkerGlobalScope</code> mixin have a <dfn
export>map of active timers</dfn>, which is a <span>map</span>, initially empty. Each
<span data-x="map key">key</span> in this map is identified by a number, which must be unique
within the list for the lifetime of the object that implements the
<code>WindowOrWorkerGlobalScope</code> mixin, and each <span data-x="map value">value</span> is a
<code>DOMHighResTimeStamp</code>, representing the expiry time for that timer.</p>
<p>Objects that implement the <code>WindowOrWorkerGlobalScope</code> mixin have a <dfn>map of
active timers</dfn>, which is a <span>map</span>, initially empty. Each <span data-x="map
key">key</span> in this map is an identifier for a timer, and each <span data-x="map
value">value</span> is a <code>DOMHighResTimeStamp</code>, representing the expiry time for that
timer.</p>

<p class="note">For entries put in the <span>map of active timers</span> by the <span>timer
initialization steps</span>, i.e., by <code data-x="dom-setTimeout">setTimeout()</code> and <code
data-x="dom-setInterval">setInterval()</code>, the keys are numbers. For other specifications
that use the <span>run steps after a timeout</span> algorithm, the identifier is a unique
non-numeric value. Only the numeric-keyed timers are affected by <code
data-x="dom-clearTimeout">clearTimeout()</code> and <code
data-x="dom-clearInterval">clearInterval()</code>, but all timers contribute to <a
href="#idle-deadline-computation">idle deadline computation</a>, and are cleared when the
relevant global is destroyed.</p>

<hr>

Expand Down Expand Up @@ -96508,8 +96517,8 @@ enum <dfn enum>DOMParserSupportedType</dfn> {

<li><p>If <var>previousId</var> was given, let <var>id</var> be <var>previousId</var>;
otherwise, let <var>id</var> be an <span>implementation-defined</span> integer that is greater
than zero that will identify the timeout to be set by this call in <var>global</var>'s <span>map
of active timers</span>.</p></li>
than zero and does not already <span data-x="map exists">exist</span> in <var>global</var>'s
<span>map of active timers</span>.</p></li>

<li>
<p>If the <span>surrounding agent</span>'s <span data-x="concept-agent-event-loop">event
Expand Down Expand Up @@ -96623,50 +96632,19 @@ enum <dfn enum>DOMParserSupportedType</dfn> {

<li><p>Set <var>task</var>'s <dfn>timer nesting level</dfn> to <var>nesting level</var>.</p></li>

<li><p>Let <var>startTime</var> be the <span>current high resolution time</span>.</p></li>

<li><p><span data-x="map set">Set</span> <var>global</var>'s <span>map of active
timers</span>[<var>id</var>] to <var>startTime</var> plus <var>timeout</var>.</p></li>
<li><p>Let <var>completionStep</var> be an algorithm step which <span data-x="queue a global
task">queues a global task</span> on the <dfn export>timer task source</dfn> given
<var>global</var> to run <var>task</var>.</p></li>

<li>
<p>Run the following steps <span>in parallel</span>:</p>

<ol>
<li>
<p>If <var>global</var> is a <code>Window</code> object, wait until <var>global</var>'s <span
data-x="concept-document-window">associated <code>Document</code></span> has been <span>fully
active</span> for a further <var>timeout</var> milliseconds (not necessarily
consecutively).</p>

<p>Otherwise, <var>global</var> is a <code>WorkerGlobalScope</code> object; wait
until <var>timeout</var> milliseconds have passed with the worker not suspended (not
necessarily consecutively).</p>
</li>

<li><p>Wait until any invocations of this algorithm that had the same <var>global</var>, that
started before this one, and whose <var>timeout</var> is equal to or less than this one's,
have completed.</p></li>

<li>
<p>Optionally, wait a further <span>implementation-defined</span> length of time.</p>

<p class="note">This is intended to allow user agents to pad timeouts as needed to optimize
the power usage of the device. For example, some processors have a low-power mode where the
granularity of timers is reduced; on such platforms, user agents can slow timers down to fit
this schedule instead of requiring the processor to use the more accurate mode with its
associated higher power usage.</p>
</li>
<p><span>Run steps after a timeout</span> given <var>global</var>, "<code
data-x="">setTimeout/setInterval</code>", <var>timeout</var>, <var>completionStep</var>, and
<var>id</var>.</p>

<li>
<p><span>Queue a global task</span> on the <dfn>timer task source</dfn> given
<var>global</var> to run <var>task</var>.</p>

<p class="note">Once the task has been processed, if <var>repeat</var> is false, it is safe
to remove the entry for <var>id</var> from the <span>map of active timers</span> (there is no
way for the entry's existence to be detected past this point, so it does not technically
matter one way or the other).</p>
</li>
</ol>
<p class="note">Once the task has been processed, if <var>repeat</var> is false, it is safe to
remove the entry for <var>id</var> from the <span>map of active timers</span> (there is no way
for the entry's existence to be detected past this point, so it does not technically matter one
way or the other).</p>
</li>

<li><p>Return <var>id</var>.</p></li>
Expand Down Expand Up @@ -96718,6 +96696,69 @@ function scheduleWork() {
scheduleWork(); // queues a task to do lots of work</code></pre>
</div>

<div w-nodev>

<p>To <dfn export>run steps after a timeout</dfn>, given a <code>WindowOrWorkerGlobalScope</code>
<var>global</var>, a string <var>orderingIdentifier</var>, a number <var>milliseconds</var>, a
set of steps <var>completionSteps</var>, and an optional value <var>timerKey</var>:</p>

<ol>
<li><p>Assert: if <var>timerKey</var> is given, then the caller of this algorithm is the
<span>timer initialization steps</span>. (Other specifications must not pass
<var>timerKey</var>.)</p></li>

<li><p>If <var>timerKey</var> is not given, then set it to a new unique non-numeric
value.</p></li>

<li><p>Let <var>startTime</var> be the <span>current high resolution time</span>.</p></li>

<li><p><span data-x="map set">Set</span> <var>global</var>'s <span>map of active
timers</span>[<var>timerKey</var>] to <var>startTime</var> plus
<var>milliseconds</var>.</p></li>

<li>
<p>Run the following steps <span>in parallel</span>:</p>

<ol>
<li>
<p>If <var>global</var> is a <code>Window</code> object, wait until <var>global</var>'s <span
data-x="concept-document-window">associated <code>Document</code></span> has been <span>fully
active</span> for a further <var>milliseconds</var> milliseconds (not necessarily
consecutively).</p>

<p>Otherwise, <var>global</var> is a <code>WorkerGlobalScope</code> object; wait until
<var>milliseconds</var> milliseconds have passed with the worker not suspended (not
necessarily consecutively).</p>
</li>

<li><p>Wait until any invocations of this algorithm that had the same <var>global</var> and
<var>orderingIdentifier</var>, that started before this one, and whose <var>milliseconds</var>
is equal to or less than this one's, have completed.</p></li>

<li>
<p>Optionally, wait a further <span>implementation-defined</span> length of time.</p>

<p class="note">This is intended to allow user agents to pad timeouts as needed to optimize
the power usage of the device. For example, some processors have a low-power mode where the
granularity of timers is reduced; on such platforms, user agents can slow timers down to fit
this schedule instead of requiring the processor to use the more accurate mode with its
associated higher power usage.</p>
</li>

<li><p>Perform <var>completionSteps</var>.</p></li>
</ol>
</li>
</ol>

<p class="note"><span>Run steps after a timeout</span> is meant to be used by other
specifications that want to execute developer-supplied code after a developer-supplied timeout,
in a similar manner to <code data-x="dom-setTimeout">setTimeout()</code>. (Note, however, it does
not have the nesting and clamping behavior of <code data-x="dom-setTimeout">setTimeout()</code>.)
Such specifications can choose an <var>orderingIdentifier</var> to ensure ordering within their
specification's timeouts, while not constraining ordering with respect to other specification's
timeouts.</p>

</div>

<h3>Microtask queuing</h3>

Expand Down

0 comments on commit bcddf41

Please sign in to comment.