Skip to content

Commit

Permalink
stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
mrkmarron committed Nov 19, 2017
1 parent 2e1e28f commit 4d98c5a
Showing 1 changed file with 94 additions and 37 deletions.
131 changes: 94 additions & 37 deletions Async-Context-Definitions.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,42 @@ questions about a Node application:
**TODO** -- say a bit more here on why these are important in introduce details
of the application + the code sample we are going to use.

# Terminology
A fundamental challenge for asynchronous execution tracking is that, in the
Node.js model, asynchronous relationships are defined at both different layers
Node.js model, asynchronous relationships are defined both at different layers
of the runtime as well as by through implicit conventions. Thus, our
definitions and mechanisims for tracking asynchronous execution rely on:
1. A notion of `client` code which sees an _asynchnonous execution_ based on
a series of executed JavaScript functions and fundamental relationships
between them.
a series of executed JavaScript functions from different asynchronous
contexts and fundamental relationships between them.
2. A notion of `runtime` code, which can be native C++, JavaScript in Node
core, or even other user code that is surfacing an asynchronous API to the
`client`.
`client` even if the underlying execution mixes code from different
asynchronous client contexts in a single synchronous execution.
3. A set of explicit tagging API's that will notify us of which API's are
involved in these boundaries and which relations they affect.

Informally, our definitions of asynchronous executions are based on three
binary relations over the executions of JavaScript functions:
These issues are illustrated by the Node `timers` API which, as described
[here](https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/),
is implemented in JavaScript code and uses a list of timer callbacks to
track _every_ function to be executed when a given timespan has elapsed.
When the timespan expires a single synchronous function is called from the
LibUV library, `listOnTimeout`, that iterates over the list and executes
every function. Thus, from a runtime viewpoint all of the functions are
part of the same asynchronous context regardless of which (logically
different) client asynchronous contexts added them.

# Terminology
Our definitions of asynchronous executions are based on three
binary relations over the executions of logically ascynchrounous JavaScript
functions:
- **link** -- when the execution one function, _f_, stores a second
function, _g_, into a storage location (native, Node.js core, or user defined)
for later execution we say _f_ `links` _g_.
for later ascynhronous execution we say _f_ `links` _g_.
- **causal** -- when the execution of a function, _f_, is the `client` code
that is locaically responsible (according to the `runtime` API) for causing
the execution of a previously **linked** _g_ we say _f_ `causes` _g_.
- **happens before** -- whea function, _f_, is executed before a second
function, _g_, we say _f_ `happens before` _g_.
- **happens before** -- when a function, _f_, is asynchrnously executed
before a second function, _g_, we say _f_ `happens before` _g_.

As each of these events is defined with respect to an executing parent function
we also want to have a notion of ordering on them. Thus, we want to timestamp
Expand All @@ -64,9 +76,13 @@ generateNextTime() {
let currentExecutingContext = undefined;
contextify(f, args, label) {
contextify(f, args, label, aspromise) {
const functionCtx = generateFreshContext();
return {label: label, function: f, argv: args, ctx: functionCtx};
return { label: label,
function: f,
argv: args,
ctx: functionCtx,
asPromise: aspromise };
}
link(ctxf) {
Expand All @@ -77,16 +93,12 @@ cause(ctxf) {
emit("cause", currentExecutingContext, ctxf.ctx, generateNextTime());
}
externalCause(ctxf, data) {
emit("externalCause", ctxf.ctx, generateNextTime(), data);
}
execute(ctxf) {
currentExecutingContext = ctxf.ctx;
emit("executeBegin", ctxf.ctx, generateNextTime());
try {
ctxf.execute(ctxf.argv); //as needed for functions or promises
ctxf.apply(null, ctxf.argv);
}
finally {
emit("executeEnd", ctxf.ctx, generateNextTime());
Expand All @@ -102,49 +114,94 @@ asynchronous execution. At each later phase the `link`, `cause`, optional
`externalCause`, and `execute` functions will need to be invoked to drive the
asynchronous execution and update/write the appropriate context information.

# Properties of Asynchronous Event Traces
An asynchronous execution is implicitly defined by the placement of
these operations and, as seen in the code, produces an asynchronous event
trace. A trace is well-formed if it preserves the following properties:
1. For each link/cause entry `currentExecutingContext` < `ctxf.ctx`
2. For each `ctxf.ctx` the timestamps (if present) for "link" < "cause"
< "externalCause" < "executeBegin" < "executeEnd"
3. others?
< "externalCause" (optional) < "executeBegin" < "fail" (optional) < "executeEnd"

Using the core definitions we can define the following states of an
asynchronous function or execution:
* `Queued asynchronous function` is one where the "link" event has been
emitted /\ the "cause" event has not been emited.

* `Ready asynchronous function` is one where the "cause" event has been
emited /\ the "executeBegin" event has not been emited.

* `Executing asynchronous function` is one where the "executeBegin" event
has been emitted /\ the "executeEnd" event has not been emited.

* `Completed asynchronous function` is one where the "executeEnd" event
has been emited.

Each `AsyncOperation` node will be in exactly one of the following states at any given time. An `AsyncOperation` node can be in each state exactly once.
**TODO** say more here

# Async Call (Sub)Tree
A tree that represents the asynchronous execution flow in a Node.js program.

* `Active asynchronous execution` is **TODO**

# Asynchronous Annotations for Core Node APIs
* `Retired asynchronous execution` is **TODO**

# Asynchronous Annotations Examples
To illustrate how the asyncronous annotation code can be used to convert a
`runtime` API into one that tracks asynchronous events for client code we look
at applying them to a sample of the `fs` API.
at applying them to a sample of a callback based API as well as
the promise API.

**TODO:** that
## Callback API
**TODO:**

Promises can be augmented as well...
## Promise API
**TODO:**

# Enriched Terminology
The definitions in the "Terminology" section provide basic asynchronous
lifecycle events but do not capture many important features including,
canceled or failed rejections and, in cases of asynchronous events that
depend on environmental interaction, what external events may be relevant.

To address these
```
cancel(ctxf) {
emit("cancel", ctxf.ctx, generateNextTime());
}
### **Async Call Tree**
A tree that represents the asynchronous execution flow in a Node.js program.
externalCause(ctxf, data) {
emit("externalCause", ctxf.ctx, generateNextTime(), data);
}
failed(ctxf) {
emit("fail", ctxf.ctx, generateNextTime());
}
rejected(ctxf) {
emit("rejected", ctxf.ctx, generateNextTime());
}
```

A `canceled asynchronous execution` is one in which **TODO**

A `failed asynchronous execution` is one in which **TODO**

A `rejected asynchronous execution` is one in which **TODO**

## Asynchronous Operation Metadata
Each `AsyncOperation` node will be in exactly one of the following states at any given time. An `AsyncOperation` node can be in each state exactly once.
A `rejected and unhandled asynchronous execution` is one in which **TODO**

1. Queued - in execution queue, but not yet ready to execute.
2. Ready - in execution queue and ready to execute, but not yet executing
3. Executing - currently executing in the event loop.
4. ChildrenPending - execution is finished, and there are one more more `AsyncOperation` child nodes not in the `Retired` state.
5. Retired - execution is finished, and there are no children in the `ChildrenPending` state.
# Asynchronous Operation Metadata

## Use Cases
# Use Cases

### Post-Mortem Use Cases
## Post-Mortem Use Cases
Post-mortem use cases occur after program execution has ended. These include reconstruction of async context state at some point in time, or understanding timing details.

1. Understand execution & timing details of an async call path
2. Understand parent/child relationshipo in async call paths
3. Reconstruct async call tree to some point in time given an event stream

### Online Use Cases
## Online Use Cases
Online use cases rely on some accurate "live tree" to navigate.

1. Continuation Local Storage
Expand Down

0 comments on commit 4d98c5a

Please sign in to comment.