https://tc39.es/ecma262/#sec-agents
9.7 Agents
An agent comprises a set of ECMAScript execution contexts, an execution context stack, a running execution context, an Agent Record, and an executing thread. Except for the executing thread, the constituents of an agent belong exclusively to that agent.
An agent‘s executing thread executes algorithmic steps on the agent‘s execution contexts independently of other agents, except that an executing thread may be used as the executing thread by multiple agents, provided none of the agents sharing the thread have an Agent Record whose [[CanBlock]] field is true. Note 1
Some web browsers share a single executing thread across multiple unrelated tabs of a browser window, for example.
While an agent‘s executing thread is executing algorithmic steps, the agent is the surrounding agent for those steps. The steps use the surrounding agent to access the specification-level execution objects held within the agent: the running execution context, the execution context stack, and the Agent Record‘s fields.
An agent signifier is a globally-unique opaque value used to identify an Agent. Table 29: Agent Record Fields
Field Name | Value | Meaning |
---|---|---|
[[LittleEndian]] | a Boolean | The default value computed for the isLittleEndian parameter when it is needed by the algorithms GetValueFromBuffer and SetValueInBuffer. The choice is implementation-defined and should be the alternative that is most efficient for the implementation. Once the value has been observed it cannot change. |
[[CanBlock]] | a Boolean | Determines whether the agent can block or not. |
[[Signifier]] | an agent signifier | Uniquely identifies the agent within its agent cluster. |
[[IsLockFree1]] | a Boolean | true if atomic operations on one-byte values are lock-free, false otherwise. |
[[IsLockFree2]] | a Boolean | true if atomic operations on two-byte values are lock-free, false otherwise. |
[[IsLockFree8]] | a Boolean | true if atomic operations on eight-byte values are lock-free, false otherwise. |
[[CandidateExecution]] | a candidate execution Record | See the memory model. |
[[KeptAlive]] | a List of either Objects or Symbols | Initially a new empty List, representing the list of objects and/or symbols to be kept alive until the end of the current Job |
Once the values of [[Signifier]], [[IsLockFree1]], and [[IsLockFree2]] have been observed by any agent in the agent cluster they cannot change. Note 2
The values of [[IsLockFree1]] and [[IsLockFree2]] are not necessarily determined by the hardware, but may also reflect implementation choices that can vary over time and between ECMAScript implementations.
There is no [[IsLockFree4]] field: 4-byte atomic operations are always lock-free.
In practice, if an atomic operation is implemented with any type of lock the operation is not lock-free. Lock-free does not imply wait-free: there is no upper bound on how many machine steps may be required to complete a lock-free atomic operation.
That an atomic access of size n is lock-free does not imply anything about the (perceived) atomicity of non-atomic accesses of size n, specifically, non-atomic accesses may still be performed as a sequence of several separate memory accesses. See ReadSharedMemory and WriteSharedMemory for details. Note 3
An agent is a specification mechanism and need not correspond to any particular artefact of an ECMAScript implementation.
9.7.1 AgentSignifier ( )
The abstract operation AgentSignifier takes no arguments and returns an agent signifier. It performs the following steps when called:
- 1. Let AR be the Agent Record of the surrounding agent.
- 2. Return AR.[[Signifier]].
9.7.2 AgentCanSuspend ( )
The abstract operation AgentCanSuspend takes no arguments and returns a Boolean. It performs the following steps when called:
- 1. Let AR be the Agent Record of the surrounding agent.
- 2. Return AR.[[CanBlock]].
Note
In some environments it may not be reasonable for a given agent to suspend. For example, in a web browser environment, it may be reasonable to disallow suspending a document’s main event handling thread, while still allowing workers’ event handling threads to suspend.
9.8 Agent Clusters
An agent cluster is a maximal set of agents that can communicate by operating on shared memory. Note 1
Programs within different agents may share memory by unspecified means. At a minimum, the backing memory for SharedArrayBuffers can be shared among the agents in the cluster.
There may be agents that can communicate by message passing that cannot share memory; they are never in the same agent cluster.
Every agent belongs to exactly one agent cluster. Note 2
The agents in a cluster need not all be alive at some particular point in time. If agent A creates another agent B, after which A terminates and B creates agent C, the three agents are in the same cluster if A could share some memory with B and B could share some memory with C.
All agents within a cluster must have the same value for the [[LittleEndian]] field in their respective Agent Records. Note 3
If different agents within an agent cluster have different values of [[LittleEndian]] it becomes hard to use shared memory for multi-byte data.
All agents within a cluster must have the same values for the [[IsLockFree1]] field in their respective Agent Records; similarly for the [[IsLockFree2]] field.
All agents within a cluster must have different values for the [[Signifier]] field in their respective Agent Records.
An embedding may deactivate (stop forward progress) or activate (resume forward progress) an agent without the agent‘s knowledge or cooperation. If the embedding does so, it must not leave some agents in the cluster active while other agents in the cluster are deactivated indefinitely. Note 4
The purpose of the preceding restriction is to avoid a situation where an agent deadlocks or starves because another agent has been deactivated. For example, if an HTML shared worker that has a lifetime independent of documents in any windows were allowed to share memory with the dedicated worker of such an independent document, and the document and its dedicated worker were to be deactivated while the dedicated worker holds a lock (say, the document is pushed into its window’s history), and the shared worker then tries to acquire the lock, then the shared worker will be blocked until the dedicated worker is activated again, if ever. Meanwhile other workers trying to access the shared worker from other windows will starve.
The implication of the restriction is that it will not be possible to share memory between agents that don’t belong to the same suspend/wake collective within the embedding.
An embedding may terminate an agent without any of the agent‘s cluster’s other agents‘ prior knowledge or cooperation. If an agent is terminated not by programmatic action of its own or of another agent in the cluster but by forces external to the cluster, then the embedding must choose one of two strategies: Either terminate all the agents in the cluster, or provide reliable APIs that allow the agents in the cluster to coordinate so that at least one remaining member of the cluster will be able to detect the termination, with the termination data containing enough information to identify the agent that was terminated. Note 5
Examples of that type of termination are: operating systems or users terminating agents that are running in separate processes; the embedding itself terminating an agent that is running in-process with the other agents when per-agent resource accounting indicates that the agent is runaway.
Each of the following specification values, and values transitively reachable from them, belong to exactly one agent cluster.
Prior to any evaluation of any ECMAScript code by any agent in a cluster, the [[CandidateExecution]] field of the Agent Record for all agents in the cluster is set to the initial candidate execution. The initial candidate execution is an empty candidate execution whose [[EventsRecords]] field is a List containing, for each agent, an Agent Events Record whose [[AgentSignifier]] field is that agent‘s agent signifier, and whose [[EventList]] and [[AgentSynchronizesWith]] fields are empty Lists. Note 6
All agents in an agent cluster share the same candidate execution in its Agent Record‘s [[CandidateExecution]] field. The candidate execution is a specification mechanism used by the memory model. Note 7
An agent cluster is a specification mechanism and need not correspond to any particular artefact of an ECMAScript implementation.
9.9 Forward Progress
For an agent to make forward progress is for it to perform an evaluation step according to this specification.
An agent becomes blocked when its running execution context waits synchronously and indefinitely for an external event. Only agents whose Agent Record‘s [[CanBlock]] field is true can become blocked in this sense. An unblocked agent is one that is not blocked.
Implementations must ensure that:
- every unblocked agent with a dedicated executing thread eventually makes forward progress
- in a set of agents that share an executing thread, one agent eventually makes forward progress
- an agent does not cause another agent to become blocked except via explicit APIs that provide blocking.
Note
This, along with the liveness guarantee in the memory model, ensures that all seq-cst writes eventually become observable to all agents.