Skip to content

Commit

Permalink
Layering: Refactor Job queues for host scheduling
Browse files Browse the repository at this point in the history
This layering change retains the PendingJob concept but sends
PendingJob records off to the host to be dispatched at the appropriate
time, replacing the RunJobs algorithm. The change is expected to
help specify the interaction between Promises, WeakRefs and the Web
more precisely, while maintaining all current invariants.
  • Loading branch information
domenic authored and littledan committed Nov 19, 2019
1 parent 19e88da commit 6d7c4dc
Showing 1 changed file with 34 additions and 118 deletions.
152 changes: 34 additions & 118 deletions spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -7504,7 +7504,7 @@ <h1>GetGlobalObject ( )</h1>
</emu-clause>

<emu-clause id="sec-jobs-and-job-queues">
<h1>Jobs and Job Queues</h1>
<h1>Jobs and Host Operations to Enqueue Jobs</h1>
<p>A <dfn id="job">Job</dfn> is an abstract operation that initiates an ECMAScript computation when no other ECMAScript computation is currently in progress. A Job abstract operation may be defined to accept an arbitrary set of job parameters.</p>
<p>Execution of a Job can be initiated only when there is no running execution context and the execution context stack is empty. A PendingJob is a request for the future execution of a Job. A PendingJob is an internal Record whose fields are specified in <emu-xref href="#table-25"></emu-xref>. Once execution of a Job is initiated, the Job always executes to completion. No other Job may be initiated until the currently running Job completes. However, the currently running Job or external events may cause the enqueuing of additional PendingJobs that may be initiated sometime after completion of the currently running Job.</p>
<emu-table id="table-25" caption="PendingJob Record Fields">
Expand Down Expand Up @@ -7579,47 +7579,25 @@ <h1>Jobs and Job Queues</h1>
</tbody>
</table>
</emu-table>
<p>A <dfn id="job-queue">Job Queue</dfn> is a FIFO queue of PendingJob records. Each Job Queue has a name and the full set of available Job Queues are defined by an ECMAScript implementation. Every ECMAScript implementation has at least the Job Queues defined in <emu-xref href="#table-26"></emu-xref>.</p>
<p>Each agent has its own set of named Job Queues. All references to a named job queue in this specification denote the named job queue of the surrounding agent.</p>
<emu-table id="table-26" caption="Required Job Queues">
<table>
<tbody>
<tr>
<th>
Name
</th>
<th>
Purpose
</th>
</tr>
<tr>
<td>
ScriptJobs
</td>
<td>
Jobs that validate and evaluate ECMAScript |Script| and |Module| source text. See clauses 10 and 15.
</td>
</tr>
<tr>
<td>
PromiseJobs
</td>
<td>
Jobs that are responses to the settlement of a Promise (see <emu-xref href="#sec-promise-objects"></emu-xref>).
</td>
</tr>
</tbody>
</table>
</emu-table>
<p>A request for the future execution of a Job is made by enqueueing, on a Job Queue, a PendingJob record that includes a Job abstract operation name and any necessary argument values. When there is no running execution context and the execution context stack is empty, the ECMAScript implementation removes the first PendingJob from a Job Queue and uses the information contained in it to create an execution context and starts execution of the associated Job abstract operation.</p>
<p>The PendingJob records from a single Job Queue are always initiated in FIFO order. This specification does not define the order in which multiple Job Queues are serviced. An ECMAScript implementation may interweave the FIFO evaluation of the PendingJob records of a Job Queue with the evaluation of the PendingJob records of one or more other Job Queues. An implementation must define what occurs when there are no running execution context and all Job Queues are empty.</p>
<emu-note>
<p>Typically an ECMAScript implementation will have its Job Queues pre-initialized with at least one PendingJob and one of those Jobs will be the first to be executed. An implementation might choose to free all resources and terminate if the current Job completes and all Job Queues are empty. Alternatively, it might choose to wait for a some implementation specific agent or mechanism to enqueue new PendingJob requests.</p>
</emu-note>
<p>The following abstract operations are used to create and manage Jobs and Job Queues:</p>
<p>Jobs are scheduled for execution by ECMAScript host environments with abstract operations such as HostEnqueuePromiseJob. Such operations accept a PendingJob Record _pending_ as a parameter. These abstract operations schedule the PendingJob to be performed at some future time. Their implementations must conform to the following requirements:</p>

<ul>
<li>At some future point in time, when there is no running execution context and the execution context stack is empty, the implementation must:
<ol>
<li>Push an execution context onto the execution context stack.</li>
<li>Perform any implementation-defined preparation steps.</li>
<li>Perform the abstract operation given by _pending_.[[Job]], passing the arguments _pending_.[[Arguments]].</li>
<li>Perform any implementation-defined cleanup steps.</li>
<li>Pop the previously-pushed execution context from the execution context stack.</li>
</ol>
</li>
<li>Only one Job may be actively undergoing evaluation at any point in time.</li>
<li>Once evaluation of a Job starts, it must run to completion before evaluation of any other Job starts.</li>
<li>The abstract operation must return a normal completion, implementing its own handling of errors.</li>
</ul>

<emu-clause id="sec-enqueuejob" aoid="EnqueueJob">
<h1>EnqueueJob ( _queueName_, _job_, _arguments_ )</h1>
<h1>EnqueueJob ( _hostHook_, _job_, _arguments_ )</h1>
<p>The EnqueueJob abstract operation requires three arguments: _queueName_, _job_, and _arguments_. It performs the following steps:</p>
<emu-alg>
1. Assert: Type(_queueName_) is String and its value is the name of a Job Queue recognized by this implementation.
Expand All @@ -7629,11 +7607,19 @@ <h1>EnqueueJob ( _queueName_, _job_, _arguments_ )</h1>
1. Let _callerRealm_ be _callerContext_'s Realm.
1. Let _callerScriptOrModule_ be _callerContext_'s ScriptOrModule.
1. Let _pending_ be PendingJob { [[Job]]: _job_, [[Arguments]]: _arguments_, [[Realm]]: _callerRealm_, [[ScriptOrModule]]: _callerScriptOrModule_, [[HostDefined]]: *undefined* }.
1. Perform any implementation or host environment defined processing of _pending_. This may include modifying the [[HostDefined]] field or any other field of _pending_.
1. Add _pending_ at the back of the Job Queue named by _queueName_.
1. Let _result_ be the result of performing the abstract operation named by _hostHook_ using &laquo; _pending_ &raquo; as its arguments.
1. Assert: _result_ is a normal completion.
1. Return NormalCompletion(~empty~).
</emu-alg>
</emu-clause>

<emu-clause id="sec-hostenqueuepromisejob" aoid="HostEnqueuePromiseJob">
<h1>HostEnqueuePromiseJob ( _pending_ )</h1>

<p>HostEnqueuePromiseJob is a host-defined abstract operation that schedules the PendingJob record _pending_ to be performed, at some future time. The PendingJobs records used with this algorithm are intended to be related to the handling of Promises, or otherwise, to be scheduled with equal priority to Promise handling operations.</p>

<p>The implementation of HostEnqueuePromiseJob must conform to the requirements in <emu-xref href="#sec-jobs-and-job-queues"></emu-xref>. Additionally, Jobs must be scheduled in FIFO order, with Jobs running in the same order as the HostEnqueuePromiseJob invocations which scheduled them.</p>
</emu-clause>
</emu-clause>

<emu-clause id="sec-initializehostdefinedrealm" aoid="InitializeHostDefinedRealm">
Expand All @@ -7656,33 +7642,6 @@ <h1>InitializeHostDefinedRealm ( )</h1>
</emu-alg>
</emu-clause>

<emu-clause id="sec-runjobs" aoid="RunJobs">
<h1>RunJobs ( )</h1>
<p>The abstract operation RunJobs performs the following steps:</p>
<emu-alg>
1. Perform ? InitializeHostDefinedRealm().
1. In an implementation-dependent manner, obtain the ECMAScript source texts (see clause <emu-xref href="#sec-ecmascript-language-source-code"></emu-xref>) and any associated host-defined values for zero or more ECMAScript scripts and/or ECMAScript modules. For each such _sourceText_ and _hostDefined_, do
1. If _sourceText_ is the source code of a script, then
1. Perform EnqueueJob(*"ScriptJobs"*, ScriptEvaluationJob, &laquo; _sourceText_, _hostDefined_ &raquo;).
1. Else,
1. Assert: _sourceText_ is the source code of a module.
1. Perform EnqueueJob(*"ScriptJobs"*, TopLevelModuleEvaluationJob, &laquo; _sourceText_, _hostDefined_ &raquo;).
1. Repeat,
1. Suspend the running execution context and remove it from the execution context stack.
1. Assert: The execution context stack is now empty.
1. Let _nextQueue_ be a non-empty Job Queue chosen in an implementation-defined manner. If all Job Queues are empty, the result is implementation-defined.
1. Let _nextPending_ be the PendingJob record at the front of _nextQueue_. Remove that record from _nextQueue_.
1. Let _newContext_ be a new execution context.
1. Set _newContext_'s Function to *null*.
1. Set _newContext_'s Realm to _nextPending_.[[Realm]].
1. Set _newContext_'s ScriptOrModule to _nextPending_.[[ScriptOrModule]].
1. Push _newContext_ onto the execution context stack; _newContext_ is now the running execution context.
1. Perform any implementation or host environment defined job initialization using _nextPending_.
1. Let _result_ be the result of performing the abstract operation named by _nextPending_.[[Job]] using the elements of _nextPending_.[[Arguments]] as its arguments.
1. If _result_ is an abrupt completion, perform HostReportErrors(&laquo; _result_.[[Value]] &raquo;).
</emu-alg>
</emu-clause>

<emu-clause id="sec-agents">
<h1>Agents</h1>

Expand Down Expand Up @@ -22031,20 +21990,6 @@ <h1>Runtime Semantics: GlobalDeclarationInstantiation ( _script_, _env_ )</h1>
<p>Unlike explicit var or function declarations, properties that are directly created on the global object result in global bindings that may be shadowed by let/const/class declarations.</p>
</emu-note>
</emu-clause>

<emu-clause id="sec-scriptevaluationjob" aoid="ScriptEvaluationJob">
<h1>Runtime Semantics: ScriptEvaluationJob ( _sourceText_, _hostDefined_ )</h1>
<p>The job ScriptEvaluationJob with parameters _sourceText_ and _hostDefined_ parses, validates, and evaluates _sourceText_ as a |Script|.</p>
<emu-alg>
1. Assert: _sourceText_ is an ECMAScript source text (see clause <emu-xref href="#sec-ecmascript-language-source-code"></emu-xref>).
1. Let _realm_ be the current Realm Record.
1. Let _s_ be ParseScript(_sourceText_, _realm_, _hostDefined_).
1. If _s_ is a List of errors, then
1. Perform HostReportErrors(_s_).
1. Return NormalCompletion(*undefined*).
1. Return ? ScriptEvaluation(_s_).
</emu-alg>
</emu-clause>
</emu-clause>

<emu-clause id="sec-modules">
Expand Down Expand Up @@ -23619,25 +23564,7 @@ <h1>Runtime Semantics: GetModuleNamespace ( _module_ )</h1>
</emu-note>
</emu-clause>

<emu-clause id="sec-toplevelmoduleevaluationjob" aoid="TopLevelModuleEvaluationJob">
<h1>Runtime Semantics: TopLevelModuleEvaluationJob ( _sourceText_, _hostDefined_ )</h1>
<p>A TopLevelModuleEvaluationJob with parameters _sourceText_ and _hostDefined_ is a job that parses, validates, and evaluates _sourceText_ as a |Module|.</p>
<emu-alg>
1. Assert: _sourceText_ is an ECMAScript source text (see clause <emu-xref href="#sec-ecmascript-language-source-code"></emu-xref>).
1. Let _realm_ be the current Realm Record.
1. Let _m_ be ParseModule(_sourceText_, _realm_, _hostDefined_).
1. If _m_ is a List of errors, then
1. Perform HostReportErrors(_m_).
1. Return NormalCompletion(*undefined*).
1. Perform ? _m_.Link().
1. Assert: All dependencies of _m_ have been transitively resolved and _m_ is ready for evaluation.
1. Return ? _m_.Evaluate().
</emu-alg>
<emu-note>
<p>An implementation may parse a _sourceText_ as a |Module|, analyse it for Early Error conditions, and link it prior to the execution of the TopLevelModuleEvaluationJob for that _sourceText_. An implementation may also resolve, pre-parse and pre-analyse, and pre-link module dependencies of _sourceText_. However, the reporting of any errors detected by these actions must be deferred until the TopLevelModuleEvaluationJob is actually executed.</p>
</emu-note>
</emu-clause>

<!-- es6num="15.2.1.20" -->
<emu-clause id="sec-module-semantics-runtime-semantics-evaluation">
<h1>Runtime Semantics: Evaluation</h1>
<emu-grammar>Module : [empty]</emu-grammar>
Expand Down Expand Up @@ -24285,18 +24212,7 @@ <h1>Error Handling and Language Extensions</h1>
</li>
</ul>

<emu-clause id="sec-host-report-errors" aoid="HostReportErrors">
<h1>HostReportErrors ( _errorList_ )</h1>

<p>HostReportErrors is an implementation-defined abstract operation that allows host environments to report parsing errors, early errors, and runtime errors.</p>

<p>An implementation of HostReportErrors must complete normally in all cases. The default implementation of HostReportErrors is to unconditionally return an empty normal completion.</p>

<emu-note>
<p>_errorList_ will be a List of ECMAScript language values. If the errors are parsing errors or early errors, these will always be *SyntaxError* objects. Runtime errors, however, can be any ECMAScript value.</p>
</emu-note>
</emu-clause>

<!-- es6num="16.1" -->
<emu-clause id="sec-forbidden-extensions">
<h1>Forbidden Extensions</h1>
<p>An implementation must not extend this specification in the following ways:</p>
Expand Down Expand Up @@ -39284,7 +39200,7 @@ <h1>Promise Resolve Functions</h1>
1. Let _thenAction_ be _then_.[[Value]].
1. If IsCallable(_thenAction_) is *false*, then
1. Return FulfillPromise(_promise_, _resolution_).
1. Perform EnqueueJob(*"PromiseJobs"*, PromiseResolveThenableJob, &laquo; _promise_, _resolution_, _thenAction_ &raquo;).
1. Perform EnqueueJob(HostEnqueuePromiseJob, PromiseResolveThenableJob, &laquo; _promise_, _resolution_, _thenAction_ &raquo;).
1. Return *undefined*.
</emu-alg>
<p>The *"length"* property of a promise resolve function is 1.</p>
Expand Down Expand Up @@ -39373,7 +39289,7 @@ <h1>TriggerPromiseReactions ( _reactions_, _argument_ )</h1>
<p>The abstract operation TriggerPromiseReactions takes a collection of PromiseReactionRecords and enqueues a new Job for each record. Each such Job processes the [[Type]] and [[Handler]] of the PromiseReactionRecord, and if the [[Handler]] is a function, calls it passing the given argument. If the [[Handler]] is *undefined*, the behaviour is determined by the [[Type]].</p>
<emu-alg>
1. For each _reaction_ in _reactions_, in original insertion order, do
1. Perform EnqueueJob(*"PromiseJobs"*, PromiseReactionJob, &laquo; _reaction_, _argument_ &raquo;).
1. Perform EnqueueJob(HostEnqueuePromiseJob, PromiseReactionJob, &laquo; _reaction_, _argument_ &raquo;).
1. Return *undefined*.
</emu-alg>
</emu-clause>
Expand Down Expand Up @@ -39917,12 +39833,12 @@ <h1>PerformPromiseThen ( _promise_, _onFulfilled_, _onRejected_ [ , _resultCapab
1. Append _rejectReaction_ as the last element of the List that is _promise_.[[PromiseRejectReactions]].
1. Else if _promise_.[[PromiseState]] is ~fulfilled~, then
1. Let _value_ be _promise_.[[PromiseResult]].
1. Perform EnqueueJob(*"PromiseJobs"*, PromiseReactionJob, &laquo; _fulfillReaction_, _value_ &raquo;).
1. Perform EnqueueJob(HostEnqueuePromiseJob, PromiseReactionJob, &laquo; _fulfillReaction_, _value_ &raquo;).
1. Else,
1. Assert: The value of _promise_.[[PromiseState]] is ~rejected~.
1. Let _reason_ be _promise_.[[PromiseResult]].
1. If _promise_.[[PromiseIsHandled]] is *false*, perform HostPromiseRejectionTracker(_promise_, *"handle"*).
1. Perform EnqueueJob(*"PromiseJobs"*, PromiseReactionJob, &laquo; _rejectReaction_, _reason_ &raquo;).
1. Perform EnqueueJob(HostEnqueuePromiseJob, PromiseReactionJob, &laquo; _rejectReaction_, _reason_ &raquo;).
1. Set _promise_.[[PromiseIsHandled]] to *true*.
1. If _resultCapability_ is *undefined*, then
1. Return *undefined*.
Expand Down

0 comments on commit 6d7c4dc

Please sign in to comment.