From 1c0860766fc459e8f0296f0ac819f5e60278e2eb Mon Sep 17 00:00:00 2001 From: Andreas Madsen Date: Mon, 5 Oct 2015 23:51:37 +0200 Subject: [PATCH 1/5] docs: add up to date documentation for AsyncWrap --- docs/AsyncWrap/README.md | 201 ++++++++++++++++++++++++++++++-- docs/AsyncWrap/example-run.js | 14 +++ docs/AsyncWrap/example-trace.js | 35 ++++++ 3 files changed, 243 insertions(+), 7 deletions(-) create mode 100644 docs/AsyncWrap/example-run.js create mode 100644 docs/AsyncWrap/example-trace.js diff --git a/docs/AsyncWrap/README.md b/docs/AsyncWrap/README.md index 7ddbcd1..ec450c0 100644 --- a/docs/AsyncWrap/README.md +++ b/docs/AsyncWrap/README.md @@ -1,15 +1,202 @@ Node.js tracing - AsyncWrap ================================================================================ -AsyncWrap is the central location in Node.js that handles async things -specific to Node.js. As such, it's a likely place where tracing will be -desired. Should serve as a good customer of the more general tracing story for -Node.js. +AsyncWrap is two things. One is a +[class abstraction](https://github.com/nodejs/node/blob/master/src/async-wrap.h) +there provides node internal facilities for handling async things, such as +calling a callback. +The other part is a an API for setting up hooks, that allow one to get +structural tracing information about the life of handle objects. In the context +of tracing the latter is usually what is meant. -An older-ish intro to AsyncWrap is available in Trevor Norris's blog post -[AsyncWrap Tutorial - An Introduction](http://blog.trevnorris.com/2015/02/asyncwrap-tutorial-introduction.html). +_The reasoning for the current naming confusion, is that the API part implements +the hooks through the AsyncWrap class. But there is nothing inherently correct +in doing that, for example if v8 provided those facilities the AsyncWrap class +would not need be involved in the AsyncWrap API._ -There was also some discussion in [issue #21](https://github.com/nodejs/tracing-wg/issues/21#issuecomment-142727693). +For the remaining description the API part is what is meant by AsyncWrap. + +## Handle objects + +AsyncWrap emits events (hooks) that informs the consumer about the life of all handle +objects in node. Thus in order to understand AsyncWrap one must first understand +handle objects. + +The API that node exposes is mostly defined in JavaScript files. However +ECMAScript does not define any API for creating a TCP socket, reading a file +etc.. That logic is implemented in C++ using libuv and the v8 API. The JavaScript in +nodecore interacts with this C++ layer using the handle objects. + +For example to create and connect a TCP socket: + +```javascript +const TCP = process.binding('tcp_wrap').TCP; +const TCPConnectWrap = process.binding('tcp_wrap').TCPConnectWrap; + +const req = new TCPConnectWrap(); +req.oncomplete = oncomplete; +req.address = address; +req.port = port; + +const socket = new TCP(); +socket.onread = onread; +socket.connect(req, address, port); +``` + +This example uses two handle objects. The first one (`TCPConnectWrap`) is for +connecting the socket, the second one (`TCP`) is for maintaining the connection. + +`TCPConnectWrap` gets its information by setting properties on the handle +object, like `address` and `port`. Those properties are read by the C++ layer, +but can also be inspected from the AsyncWrap hooks. When the handle is create +using `new TCPConnectWrap()` the `init` hook is called. + +A `oncomplete` property is also set, this is the callback for when the connection is made or failed. Just before calling `oncomplete` the `before` hook +is called, just after the `after` hook is called. + +The `TCP` handle works exactly the same way, except that the information +is passed as arguments to a method `.connect` and the `onread` function +is called multiply times, thus it behaves like an event. This also means that +the `before` and `after` hooks are called multiply times. + +Thus one should expect the hooks be called in the following order: + +```javascript +init // TCPConnectWrap +init // TCP +=== tick === +before // TCPConnectWrap +after // TCPConnectWrap +=== tick === +before // TCP +after // TCP +=== tick === +before // TCP +after // TCP +=== tick === +... +``` + +_tick_ indicates there is at least one tick between the text above and the text below. + +## The API + +At the moment there don't exist a high level API for AsyncWrap. It is simply exposed through `process.binding`: + +```javascript +const asyncWrap = process.binding('async_wrap'); +``` + +_Be warned that this API is not an official API and can change at any time, even if it's just patch +update._ + +To assign the hooks call: + +```javascript +asyncWrap.setupHooks(init, before, after); +function init(provider) { /* consumer code */ } +function before() { /* consumer code */ } +function after() { /* consumer code */ } +``` + +Note that calling `asyncWrap.setupHooks` again, will overwrite the previous +hooks. + +#### Enable and disable + +Because there is a small performance penalty in just calling a noop function, +AsyncWrap is not enabled by default. To enable AsyncWrap call: + +```javascript +asyncWrap.enable(); +``` + +Note that handle objects created before AsyncWrap is enabled will never +be tracked, even after `.enable()` is called. Similarly when AsyncWrap is +disabled, the handle objects there are already being tracked will continue +to be tracked. Finally there are some cases where handle objects have a parent. +For example a TCP server creates TCP sockets, thus the socket handles have the +server handle as a parent. In this case AsyncWrap should be enabled before +the server handle is created, for the TCP sockets to be tracked. + +Disabling AsyncWrap can be done with: + +```javascript +asyncWrap.disable(); +``` + +#### The hooks + +Currently there are 3 hooks: `init`, `before` and `after`. The function +signatures are quite similar. In all cases the `this` variable is the handle +object. Users may read properties from this object such as `port` and `address` in the `TCPConnectWrap` case, or set user specific properties. + +Finally the `init` has an extra argument called `provider`, this is an +integer that refer to names defined in an `async.Providers` object map. + +## example + +A classic use case for AsyncWrap is to create a long-stack-trace tool. + +```javascript +const asyncWrap = process.binding('async_wrap'); + +asyncWrap.setupHooks(init, before, after); +asyncWrap.enable(); + +// global state variable, that contains the current stack trace +let currentStack = ''; + +function init(provider) { + // When a handle is created, collect the stack trace such that we later + // can see what involved the handle constructor. + const localStack = (new Error()).stack.split('\n').slice(1).join('\n'); + // Compute the full stack and store it as a property on the handle object,˛ + // such that it can be fetched later in either `before` or `after`. + this._full_init_stack = localStack + '\n' + currentStack; +} +function before() { + // A callback is about to be called, update the `currentStack` such that + // it is correct for when another handle is initialized or `getStack` is called. + currentStack = this._full_init_stack; +} +function after() { + // At the time of writing there are some odd cases where there is no handle + // context, this line prevents that from resulting in wrong stack trace. But + // the stack trace will be shorter compared to what ideally should happen. + currentStack = ''; +} + +function getStack(message) { + const localStack = new Error(message); + return localStack.stack + '\n' + currentStack; +} +module.exports = getStack; +``` + +Please note that this example is way simpler, than what is required from a +complete long-stack-trace implementation. + +## Things you might not expect + +* `console.log` is async and thus invokes AsyncWrap, thus using `console.log` +inside one of the hooks, creates an infinite recursion. Use `fs.syncWrite(1, msg)` +or `process._rawDebug(msg)` instead. + +* `process.nextTick` never creates a handle object. You will have to money patch +this. + +* Timer functions (like `setTimeout`) shares a single Timer handle, thus you +will usually have to money patch those functions. + +## Resources + +* An intro to AsyncWrap by Trevor Norris: http://blog.trevnorris.com/2015/02/asyncwrap-tutorial-introduction.html (outdated) +* Slides from a local talk Andreas Madsen did on AsyncWrap: +https://github.com/AndreasMadsen/talk-async-wrap (outdated) +* There was also some discussion in [issue #21](https://github.com/nodejs/tracing-wg/issues/21#issuecomment-142727693). +* Complete (hopefully) long-stack-trace module that uses AsyncWrap: https://github.com/AndreasMadsen/trace +* Visualization tool for AsyncWrap wrap: https://github.com/AndreasMadsen/dprof ---- diff --git a/docs/AsyncWrap/example-run.js b/docs/AsyncWrap/example-run.js new file mode 100644 index 0000000..e2c6971 --- /dev/null +++ b/docs/AsyncWrap/example-run.js @@ -0,0 +1,14 @@ +'use strict'; + +const fs = require('fs'); +const getStack = require('./example-trace.js'); + +fs.open(__filename, 'r', function (err, fd) { + if (err) throw err; + + fs.close(fd, function (err) { + if (err) throw err; + + console.log(getStack('trace')); + }); +}) diff --git a/docs/AsyncWrap/example-trace.js b/docs/AsyncWrap/example-trace.js new file mode 100644 index 0000000..ad09951 --- /dev/null +++ b/docs/AsyncWrap/example-trace.js @@ -0,0 +1,35 @@ +'use strict'; + +const asyncWrap = process.binding('async_wrap'); + +asyncWrap.setupHooks(init, before, after); +asyncWrap.enable(); + +// global state variable, that contains the current stack trace +let currentStack = ''; + +function init(provider) { + // When a handle is created, collect the stack trace such that we later + // can see what involved the handle constructor. + const localStack = (new Error()).stack.split('\n').slice(1).join('\n'); + // Compute the full stack and store it as a property on the handle object, + // such that it can be fetched later in either `before` or `after`. + this._full_init_stack = localStack + '\n' + currentStack; +} +function before() { + // A callback is about to be called, update the `currentStack` such that + // it is correct for when another handle is initialized or `getStack` is called. + currentStack = this._full_init_stack; +} +function after() { + // At the time of writing there are some odd cases where there is no handle + // context, this line prevents that from resulting in wrong stack trace. But + // the stack trace will be shorter compared to what ideally should happen. + currentStack = ''; +} + +function getStack(message) { + const localStack = new Error(message); + return localStack.stack + '\n' + currentStack; +} +module.exports = getStack; From 35362dc9f6be8947c60df8086404658915a1266f Mon Sep 17 00:00:00 2001 From: Andreas Madsen Date: Thu, 8 Oct 2015 17:19:51 +0200 Subject: [PATCH 2/5] docs: incorporate Trevis comments into the AsyncWrap documentation --- docs/AsyncWrap/README.md | 103 +++++++++++++++++++++++++++----- docs/AsyncWrap/example-run.js | 27 +++++++-- docs/AsyncWrap/example-trace.js | 8 ++- 3 files changed, 115 insertions(+), 23 deletions(-) diff --git a/docs/AsyncWrap/README.md b/docs/AsyncWrap/README.md index ec450c0..42b5c84 100644 --- a/docs/AsyncWrap/README.md +++ b/docs/AsyncWrap/README.md @@ -27,7 +27,8 @@ ECMAScript does not define any API for creating a TCP socket, reading a file etc.. That logic is implemented in C++ using libuv and the v8 API. The JavaScript in nodecore interacts with this C++ layer using the handle objects. -For example to create and connect a TCP socket: +For example in `net.connect(port, address)` that creates a TCP connection, +two handle objects (`TCPConnectWrap` and `TCP`) are created: ```javascript const TCP = process.binding('tcp_wrap').TCP; @@ -43,15 +44,16 @@ socket.onread = onread; socket.connect(req, address, port); ``` -This example uses two handle objects. The first one (`TCPConnectWrap`) is for -connecting the socket, the second one (`TCP`) is for maintaining the connection. +The first one (`TCPConnectWrap`) is for connecting the socket, the second +one (`TCP`) is for maintaining the connection. `TCPConnectWrap` gets its information by setting properties on the handle object, like `address` and `port`. Those properties are read by the C++ layer, but can also be inspected from the AsyncWrap hooks. When the handle is create using `new TCPConnectWrap()` the `init` hook is called. -A `oncomplete` property is also set, this is the callback for when the connection is made or failed. Just before calling `oncomplete` the `before` hook +A `oncomplete` property is also set, this is the callback for when the +connection is made or failed. Just before calling `oncomplete` the `before` hook is called, just after the `after` hook is called. The `TCP` handle works exactly the same way, except that the information @@ -77,7 +79,8 @@ after // TCP ... ``` -_tick_ indicates there is at least one tick between the text above and the text below. +_tick_ indicates there is at least one tick (as in `process.nextTick()`) between +the text above and the text below. ## The API @@ -94,7 +97,7 @@ To assign the hooks call: ```javascript asyncWrap.setupHooks(init, before, after); -function init(provider) { /* consumer code */ } +function init(provider, parent) { /* consumer code */ } function before() { /* consumer code */ } function after() { /* consumer code */ } ``` @@ -128,11 +131,75 @@ asyncWrap.disable(); #### The hooks Currently there are 3 hooks: `init`, `before` and `after`. The function -signatures are quite similar. In all cases the `this` variable is the handle -object. Users may read properties from this object such as `port` and `address` in the `TCPConnectWrap` case, or set user specific properties. +signatures are quite similar. The `this` variable refers to the handle object, +and `init` hook has two extra arguments `provider` and `parent`. -Finally the `init` has an extra argument called `provider`, this is an -integer that refer to names defined in an `async.Providers` object map. +```javascript +function init(provider, parent) { this = handle; } +function before() { this = handle; } +function after() { this = handle; } +``` + +##### this +In all cases the `this` variable is the handle object. Users may read properties +from this object such as `port` and `address` in the `TCPConnectWrap` case, +or set user specific properties. However in the `init` hook the object is not +yet fully constructed, thus some properties are not safe to read. This causes +problems when doing `util.inspect(this)` or similar. + +##### provider +This is an integer that refer to names defined in an `asyncWrap.Providers` +object map. + +At the time of writing this is the current list: + +```javascript +{ NONE: 0, + CRYPTO: 1, + FSEVENTWRAP: 2, + FSREQWRAP: 3, + GETADDRINFOREQWRAP: 4, + GETNAMEINFOREQWRAP: 5, + JSSTREAM: 6, + PIPEWRAP: 7, + PIPECONNECTWRAP: 8, + PROCESSWRAP: 9, + QUERYWRAP: 10, + SHUTDOWNWRAP: 11, + SIGNALWRAP: 12, + STATWATCHER: 13, + TCPWRAP: 14, + TCPCONNECTWRAP: 15, + TIMERWRAP: 16, + TLSWRAP: 17, + TTYWRAP: 18, + UDPWRAP: 19, + UDPSENDWRAP: 20, + WRITEWRAP: 21, + ZLIB: 22 } +``` + +##### parent + +In some cases the handle was created from another handle object. In those +cases the `parent` argument is set the creating handle object. If there is +no parent handle then it is just `null`. + +The most common case is the TCP server. The TCP server itself is a `TCP` handle, +but when receiving new connection it creates another `TCP` handle that is +responsible for the new socket. It does this before emitting the `onconnection` +handle event, thus the asyncWrap hooks are called in the following order: + +``` +```javascript +init // TCP (socket) +before // TCP (server) +after // TCP (server) +``` + +This means it is not possible to know in what handle context the new socket +handle was created using the `before` and `after` hooks. However the +`parent` argument provides this information. ## example @@ -147,13 +214,15 @@ asyncWrap.enable(); // global state variable, that contains the current stack trace let currentStack = ''; -function init(provider) { +function init(provider, parent) { // When a handle is created, collect the stack trace such that we later // can see what involved the handle constructor. const localStack = (new Error()).stack.split('\n').slice(1).join('\n'); - // Compute the full stack and store it as a property on the handle object,˛ - // such that it can be fetched later in either `before` or `after`. - this._full_init_stack = localStack + '\n' + currentStack; + + // Compute the full stack and store it as a property on the handle object, + // such that it can be fetched later. + const extraStack = parent ? parent._full_init_stack : currentStack; + this._full_init_stack = localStack + '\n' + extraStack; } function before() { // A callback is about to be called, update the `currentStack` such that @@ -179,6 +248,10 @@ complete long-stack-trace implementation. ## Things you might not expect +* It is not obvious when a handle object is created. For example the TCP server +creates the `TCP` handle when `.listen` is called and it may perform an DNS +lookup before that. + * `console.log` is async and thus invokes AsyncWrap, thus using `console.log` inside one of the hooks, creates an infinite recursion. Use `fs.syncWrite(1, msg)` or `process._rawDebug(msg)` instead. @@ -186,7 +259,7 @@ or `process._rawDebug(msg)` instead. * `process.nextTick` never creates a handle object. You will have to money patch this. -* Timer functions (like `setTimeout`) shares a single Timer handle, thus you +* Timer functions (like `setTimeout`) shares a single `Timer` handle, thus you will usually have to money patch those functions. ## Resources diff --git a/docs/AsyncWrap/example-run.js b/docs/AsyncWrap/example-run.js index e2c6971..44fc409 100644 --- a/docs/AsyncWrap/example-run.js +++ b/docs/AsyncWrap/example-run.js @@ -1,14 +1,31 @@ 'use strict'; const fs = require('fs'); +const net = require('net'); const getStack = require('./example-trace.js'); -fs.open(__filename, 'r', function (err, fd) { - if (err) throw err; +Error.stackTraceLimit = Infinity; - fs.close(fd, function (err) { +const server = net.createServer(function (socket) { + + fs.open(__filename, 'r', function (err, fd) { if (err) throw err; - console.log(getStack('trace')); + fs.close(fd, function (err) { + if (err) throw err; + + console.log(getStack('trace')); + socket.end('hallo world'); + }); + }) +}); + +server.listen(0, 'localhost', function () { + const addr = server.address(); + const socket = net.connect(addr.port, addr.address, function () { + socket.once('readable', function () { + socket.read(); + socket.once('readable', server.close.bind(server)); + }); }); -}) +}); diff --git a/docs/AsyncWrap/example-trace.js b/docs/AsyncWrap/example-trace.js index ad09951..3e681e1 100644 --- a/docs/AsyncWrap/example-trace.js +++ b/docs/AsyncWrap/example-trace.js @@ -8,13 +8,15 @@ asyncWrap.enable(); // global state variable, that contains the current stack trace let currentStack = ''; -function init(provider) { +function init(provider, parent) { // When a handle is created, collect the stack trace such that we later // can see what involved the handle constructor. const localStack = (new Error()).stack.split('\n').slice(1).join('\n'); + // Compute the full stack and store it as a property on the handle object, - // such that it can be fetched later in either `before` or `after`. - this._full_init_stack = localStack + '\n' + currentStack; + // such that it can be fetched later. + const extraStack = parent ? parent._full_init_stack : currentStack; + this._full_init_stack = localStack + '\n' + extraStack; } function before() { // A callback is about to be called, update the `currentStack` such that From ffcaa9fcf19c7874e0a08e82f2eec0d4b01ea97f Mon Sep 17 00:00:00 2001 From: Andreas Madsen Date: Thu, 8 Oct 2015 22:34:57 +0200 Subject: [PATCH 3/5] docs: minor spelling corrections, thanks Stephen --- docs/AsyncWrap/README.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/AsyncWrap/README.md b/docs/AsyncWrap/README.md index 42b5c84..daa2b03 100644 --- a/docs/AsyncWrap/README.md +++ b/docs/AsyncWrap/README.md @@ -25,7 +25,7 @@ handle objects. The API that node exposes is mostly defined in JavaScript files. However ECMAScript does not define any API for creating a TCP socket, reading a file etc.. That logic is implemented in C++ using libuv and the v8 API. The JavaScript in -nodecore interacts with this C++ layer using the handle objects. +node core interacts with this C++ layer using the handle objects. For example in `net.connect(port, address)` that creates a TCP connection, two handle objects (`TCPConnectWrap` and `TCP`) are created: @@ -49,7 +49,7 @@ one (`TCP`) is for maintaining the connection. `TCPConnectWrap` gets its information by setting properties on the handle object, like `address` and `port`. Those properties are read by the C++ layer, -but can also be inspected from the AsyncWrap hooks. When the handle is create +but can also be inspected from the AsyncWrap hooks. When the handle is created using `new TCPConnectWrap()` the `init` hook is called. A `oncomplete` property is also set, this is the callback for when the @@ -58,8 +58,8 @@ is called, just after the `after` hook is called. The `TCP` handle works exactly the same way, except that the information is passed as arguments to a method `.connect` and the `onread` function -is called multiply times, thus it behaves like an event. This also means that -the `before` and `after` hooks are called multiply times. +is called multiple times, thus it behaves like an event. This also means that +the `before` and `after` hooks are called multiple times. Thus one should expect the hooks be called in the following order: @@ -84,7 +84,8 @@ the text above and the text below. ## The API -At the moment there don't exist a high level API for AsyncWrap. It is simply exposed through `process.binding`: +At the moment there is not a high level API for AsyncWrap. It is simply exposed +through `process.binding`: ```javascript const asyncWrap = process.binding('async_wrap'); @@ -135,9 +136,9 @@ signatures are quite similar. The `this` variable refers to the handle object, and `init` hook has two extra arguments `provider` and `parent`. ```javascript -function init(provider, parent) { this = handle; } -function before() { this = handle; } -function after() { this = handle; } +function init(provider, parent) { } +function before() { } +function after() { } ``` ##### this @@ -256,11 +257,11 @@ lookup before that. inside one of the hooks, creates an infinite recursion. Use `fs.syncWrite(1, msg)` or `process._rawDebug(msg)` instead. -* `process.nextTick` never creates a handle object. You will have to money patch +* `process.nextTick` never creates a handle object. You will have to monkey patch this. * Timer functions (like `setTimeout`) shares a single `Timer` handle, thus you -will usually have to money patch those functions. +will usually have to monkey patch those functions. ## Resources From f3275097b1a922cb62868481906d01701fe1290e Mon Sep 17 00:00:00 2001 From: Andreas Madsen Date: Wed, 14 Oct 2015 11:39:37 +0200 Subject: [PATCH 4/5] docs: mostly gramma and a remark about Promises --- docs/AsyncWrap/README.md | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/docs/AsyncWrap/README.md b/docs/AsyncWrap/README.md index daa2b03..03d00c1 100644 --- a/docs/AsyncWrap/README.md +++ b/docs/AsyncWrap/README.md @@ -3,29 +3,29 @@ Node.js tracing - AsyncWrap AsyncWrap is two things. One is a [class abstraction](https://github.com/nodejs/node/blob/master/src/async-wrap.h) -there provides node internal facilities for handling async things, such as +that provides an internal mechanism for handling asynchronous tasks, such as calling a callback. -The other part is a an API for setting up hooks, that allow one to get +The other part is an API for setting up hooks and allows one to get structural tracing information about the life of handle objects. In the context of tracing the latter is usually what is meant. -_The reasoning for the current naming confusion, is that the API part implements -the hooks through the AsyncWrap class. But there is nothing inherently correct -in doing that, for example if v8 provided those facilities the AsyncWrap class -would not need be involved in the AsyncWrap API._ +_The reasoning for the current naming confusion is that the API part implements +the hooks through the AsyncWrap class, but this is not inherently necessary. For +example if v8 provided those facilities the AsyncWrap class would not need be +involved in the AsyncWrap API._ For the remaining description the API part is what is meant by AsyncWrap. -## Handle objects +## Handle Objects -AsyncWrap emits events (hooks) that informs the consumer about the life of all handle +AsyncWrap emits events (hooks) that inform the consumer about the life of all handle objects in node. Thus in order to understand AsyncWrap one must first understand handle objects. -The API that node exposes is mostly defined in JavaScript files. However -ECMAScript does not define any API for creating a TCP socket, reading a file -etc.. That logic is implemented in C++ using libuv and the v8 API. The JavaScript in -node core interacts with this C++ layer using the handle objects. +Node's core API is mostly defined in JavaScript. However ECMAScript does not +define any API for creating a TCP socket, reading a file etc.. That logic is +implemented in C++ using libuv and the v8 API. The JavaScript in node core +interacts with this C++ layer using the handle objects. For example in `net.connect(port, address)` that creates a TCP connection, two handle objects (`TCPConnectWrap` and `TCP`) are created: @@ -106,7 +106,7 @@ function after() { /* consumer code */ } Note that calling `asyncWrap.setupHooks` again, will overwrite the previous hooks. -#### Enable and disable +#### Enable And Disable Because there is a small performance penalty in just calling a noop function, AsyncWrap is not enabled by default. To enable AsyncWrap call: @@ -129,7 +129,7 @@ Disabling AsyncWrap can be done with: asyncWrap.disable(); ``` -#### The hooks +#### The Hooks Currently there are 3 hooks: `init`, `before` and `after`. The function signatures are quite similar. The `this` variable refers to the handle object, @@ -202,7 +202,7 @@ This means it is not possible to know in what handle context the new socket handle was created using the `before` and `after` hooks. However the `parent` argument provides this information. -## example +## Example A classic use case for AsyncWrap is to create a long-stack-trace tool. @@ -247,7 +247,7 @@ module.exports = getStack; Please note that this example is way simpler, than what is required from a complete long-stack-trace implementation. -## Things you might not expect +## Things You Might Not Expect * It is not obvious when a handle object is created. For example the TCP server creates the `TCP` handle when `.listen` is called and it may perform an DNS @@ -255,7 +255,8 @@ lookup before that. * `console.log` is async and thus invokes AsyncWrap, thus using `console.log` inside one of the hooks, creates an infinite recursion. Use `fs.syncWrite(1, msg)` -or `process._rawDebug(msg)` instead. +or `process._rawDebug(msg)` instead. The latter is a little nicer because it +uses `util.inspect`. On the other hand `fs.syncWrite` is a documented function. * `process.nextTick` never creates a handle object. You will have to monkey patch this. @@ -263,6 +264,10 @@ this. * Timer functions (like `setTimeout`) shares a single `Timer` handle, thus you will usually have to monkey patch those functions. +* Promises are also not tracked by AsyncWrap. The `Promise` constructor can also +be monkey patched, but unfortunately it is quite difficult. See +[async-listener](https://github.com/othiym23/async-listener/blob/14d01b2b82817fff9993f065587f9009f3d2126b/index.js#L257L407) for how to do it. + ## Resources * An intro to AsyncWrap by Trevor Norris: http://blog.trevnorris.com/2015/02/asyncwrap-tutorial-introduction.html (outdated) From c7244e089b311a88e08c8949372dc576b9be1cf0 Mon Sep 17 00:00:00 2001 From: Andreas Madsen Date: Wed, 14 Oct 2015 18:34:57 +0200 Subject: [PATCH 5/5] docs: fix long lines and a single comma --- docs/AsyncWrap/README.md | 18 ++++++++++-------- docs/AsyncWrap/example-trace.js | 3 ++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/AsyncWrap/README.md b/docs/AsyncWrap/README.md index 03d00c1..fb59294 100644 --- a/docs/AsyncWrap/README.md +++ b/docs/AsyncWrap/README.md @@ -18,9 +18,9 @@ For the remaining description the API part is what is meant by AsyncWrap. ## Handle Objects -AsyncWrap emits events (hooks) that inform the consumer about the life of all handle -objects in node. Thus in order to understand AsyncWrap one must first understand -handle objects. +AsyncWrap emits events (hooks) that inform the consumer about the life of all +handle objects in node. Thus in order to understand AsyncWrap one must first +understand handle objects. Node's core API is mostly defined in JavaScript. However ECMAScript does not define any API for creating a TCP socket, reading a file etc.. That logic is @@ -91,8 +91,8 @@ through `process.binding`: const asyncWrap = process.binding('async_wrap'); ``` -_Be warned that this API is not an official API and can change at any time, even if it's just patch -update._ +_Be warned that this API is not an official API and can change at any time, even +if it's just patch update._ To assign the hooks call: @@ -227,7 +227,8 @@ function init(provider, parent) { } function before() { // A callback is about to be called, update the `currentStack` such that - // it is correct for when another handle is initialized or `getStack` is called. + // it is correct for when another handle is initialized or `getStack` is + // called. currentStack = this._full_init_stack; } function after() { @@ -244,7 +245,7 @@ function getStack(message) { module.exports = getStack; ``` -Please note that this example is way simpler, than what is required from a +Please note that this example is way simpler than what is required from a complete long-stack-trace implementation. ## Things You Might Not Expect @@ -266,7 +267,8 @@ will usually have to monkey patch those functions. * Promises are also not tracked by AsyncWrap. The `Promise` constructor can also be monkey patched, but unfortunately it is quite difficult. See -[async-listener](https://github.com/othiym23/async-listener/blob/14d01b2b82817fff9993f065587f9009f3d2126b/index.js#L257L407) for how to do it. +[async-listener](https://github.com/othiym23/async-listener/blob/14d01b2b82817fff9993f065587f9009f3d2126b/index.js#L257L407) +for how to do it. ## Resources diff --git a/docs/AsyncWrap/example-trace.js b/docs/AsyncWrap/example-trace.js index 3e681e1..14daf07 100644 --- a/docs/AsyncWrap/example-trace.js +++ b/docs/AsyncWrap/example-trace.js @@ -20,7 +20,8 @@ function init(provider, parent) { } function before() { // A callback is about to be called, update the `currentStack` such that - // it is correct for when another handle is initialized or `getStack` is called. + // it is correct for when another handle is initialized or `getStack` is + // called. currentStack = this._full_init_stack; } function after() {