Skip to content

Commit 7c0673d

Browse files
committed
Merge pull request #37 from AndreasMadsen/async-wrap-update-2
Async wrap update - destroy hook and uid
2 parents 7086c24 + 1a3e496 commit 7c0673d

File tree

3 files changed

+77
-42
lines changed

3 files changed

+77
-42
lines changed

docs/AsyncWrap/README.md

Lines changed: 75 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ involved in the AsyncWrap API._
1616

1717
For the remaining description the API part is what is meant by AsyncWrap.
1818

19+
## Table of Content
20+
21+
* [Handle Objects](#handle-objects)
22+
* [The API](#the-api)
23+
* [Example](#example)
24+
* [Things You Might Not Expect](#things-you-might-not-expect)
25+
* [Resources](#resources)
26+
1927
## Handle Objects
2028

2129
AsyncWrap emits events (hooks) that inform the consumer about the life of all
@@ -42,41 +50,53 @@ req.port = port;
4250
const socket = new TCP();
4351
socket.onread = onread;
4452
socket.connect(req, address, port);
53+
54+
// later
55+
socket.destroy();
4556
```
4657

47-
The first one (`TCPConnectWrap`) is for connecting the socket, the second
48-
one (`TCP`) is for maintaining the connection.
58+
The first handle object (`TCPConnectWrap`) is for connecting the socket, the
59+
second one (`TCP`) is for maintaining the connection.
4960

5061
`TCPConnectWrap` gets its information by setting properties on the handle
5162
object, like `address` and `port`. Those properties are read by the C++ layer,
5263
but can also be inspected from the AsyncWrap hooks. When the handle is created
5364
using `new TCPConnectWrap()` the `init` hook is called.
5465

55-
A `oncomplete` property is also set, this is the callback for when the
56-
connection is made or failed. Just before calling `oncomplete` the `before` hook
57-
is called, just after the `after` hook is called.
66+
An `oncomplete` property is also set, this is the callback for when the
67+
connection is made or failed. Just before calling `oncomplete` the `pre` hook
68+
is called, just after the `post` hook is called.
5869

5970
The `TCP` handle works exactly the same way, except that the information
6071
is passed as arguments to a method `.connect` and the `onread` function
6172
is called multiple times, thus it behaves like an event. This also means that
62-
the `before` and `after` hooks are called multiple times.
73+
the `pre` and `post` hooks are called multiple times.
74+
75+
At some later time the `socket.destroy()` is called, this will call the
76+
`destroy` hook for the `socket` handle. Other handle objects aren't explicitly
77+
destroyed, in that case the `destroy` hook is called when the handle object is
78+
garbage collected by v8.
6379

6480
Thus one should expect the hooks be called in the following order:
6581

6682
```javascript
6783
init // TCPConnectWrap
6884
init // TCP
6985
=== tick ===
70-
before // TCPConnectWrap
71-
after // TCPConnectWrap
86+
pre // TCPConnectWrap
87+
post // TCPConnectWrap
7288
=== tick ===
73-
before // TCP
74-
after // TCP
89+
pre // TCP
90+
post // TCP
7591
=== tick ===
76-
before // TCP
77-
after // TCP
92+
pre // TCP
93+
post // TCP
7894
=== tick ===
7995
...
96+
=== tick ===
97+
destroy // TCP
98+
=== tick ===
99+
destroy // TCPConnectWrap
80100
```
81101

82102
_tick_ indicates there is at least one tick (as in `process.nextTick()`) between
@@ -97,14 +117,15 @@ if it's just patch update._
97117
To assign the hooks call:
98118

99119
```javascript
100-
asyncWrap.setupHooks(init, before, after);
101-
function init(provider, parent) { /* consumer code */ }
102-
function before() { /* consumer code */ }
103-
function after() { /* consumer code */ }
120+
asyncWrap.setupHooks(init, pre, post, destroy);
121+
function init(provider, uid, parent) { /* consumer code */ }
122+
function pre() { /* consumer code */ }
123+
function post() { /* consumer code */ }
124+
function destroy(uid) { /* consumer code */ }
104125
```
105126

106-
Note that calling `asyncWrap.setupHooks` again, will overwrite the previous
107-
hooks.
127+
Note that only the `init` function is required and that calling
128+
`asyncWrap.setupHooks` again will overwrite the previous hooks.
108129

109130
#### Enable And Disable
110131

@@ -131,25 +152,33 @@ asyncWrap.disable();
131152

132153
#### The Hooks
133154

134-
Currently there are 3 hooks: `init`, `before` and `after`. The function
135-
signatures are quite similar. The `this` variable refers to the handle object,
136-
and `init` hook has two extra arguments `provider` and `parent`.
155+
Currently there are 4 hooks: `init`, `pre`, `post` `destroy`. The `this`
156+
variable refers to the handle object. The `init` hook has three extra arguments
157+
`provider`, `uid` and `parent`. The `destroy` hook also has the `uid` argument.
137158

138159
```javascript
139-
function init(provider, parent) { }
140-
function before() { }
141-
function after() { }
160+
function init(provider, uid, parent) { }
161+
function pre() { }
162+
function post() { }
163+
function destroy(uid) { }
142164
```
143165

144166
##### this
145-
In all cases the `this` variable is the handle object. Users may read properties
146-
from this object such as `port` and `address` in the `TCPConnectWrap` case,
147-
or set user specific properties. However in the `init` hook the object is not
148-
yet fully constructed, thus some properties are not safe to read. This causes
149-
problems when doing `util.inspect(this)` or similar.
167+
168+
In the `init`, `pre` and `post` cases the `this` variable is the handle object.
169+
Users may read properties from this object such as `port` and `address` in the
170+
`TCPConnectWrap` case, or set user specific properties.
171+
172+
In the `init` hook the handle object is not yet fully constructed, thus some
173+
properties are not safe to read. This causes problems when doing
174+
`util.inspect(this)` or similar.
175+
176+
In the `destroy` hook `this` is `null`, this is because the handle objects has
177+
been deleted by the garbage collector and thus doesn't exists.
150178

151179
##### provider
152-
This is an integer that refer to names defined in an `asyncWrap.Providers`
180+
181+
This is an integer that refer to names defined in the `asyncWrap.Providers`
153182
object map.
154183

155184
At the time of writing this is the current list:
@@ -180,6 +209,13 @@ At the time of writing this is the current list:
180209
ZLIB: 22 }
181210
```
182211

212+
##### uid
213+
214+
The `uid` is a unique integer that identify each handle object. Because the
215+
`destroy` hook isn't called with the handle object, this is particular useful
216+
for storing information related to the handle object, that the user require in
217+
the `destroy` hook.
218+
183219
##### parent
184220

185221
In some cases the handle was created from another handle object. In those
@@ -191,15 +227,14 @@ but when receiving new connection it creates another `TCP` handle that is
191227
responsible for the new socket. It does this before emitting the `onconnection`
192228
handle event, thus the asyncWrap hooks are called in the following order:
193229

194-
```
195230
```javascript
196231
init // TCP (socket)
197-
before // TCP (server)
198-
after // TCP (server)
232+
pre // TCP (server)
233+
post // TCP (server)
199234
```
200235

201236
This means it is not possible to know in what handle context the new socket
202-
handle was created using the `before` and `after` hooks. However the
237+
handle was created using the `pre` and `post` hooks. However the
203238
`parent` argument provides this information.
204239

205240
## Example
@@ -209,13 +244,13 @@ A classic use case for AsyncWrap is to create a long-stack-trace tool.
209244
```javascript
210245
const asyncWrap = process.binding('async_wrap');
211246

212-
asyncWrap.setupHooks(init, before, after);
247+
asyncWrap.setupHooks(init, pre, post);
213248
asyncWrap.enable();
214249

215250
// global state variable, that contains the current stack trace
216251
let currentStack = '';
217252

218-
function init(provider, parent) {
253+
function init(provider, uid, parent) {
219254
// When a handle is created, collect the stack trace such that we later
220255
// can see what involved the handle constructor.
221256
const localStack = (new Error()).stack.split('\n').slice(1).join('\n');
@@ -225,13 +260,13 @@ function init(provider, parent) {
225260
const extraStack = parent ? parent._full_init_stack : currentStack;
226261
this._full_init_stack = localStack + '\n' + extraStack;
227262
}
228-
function before() {
263+
function pre() {
229264
// A callback is about to be called, update the `currentStack` such that
230265
// it is correct for when another handle is initialized or `getStack` is
231266
// called.
232267
currentStack = this._full_init_stack;
233268
}
234-
function after() {
269+
function post() {
235270
// At the time of writing there are some odd cases where there is no handle
236271
// context, this line prevents that from resulting in wrong stack trace. But
237272
// the stack trace will be shorter compared to what ideally should happen.
@@ -272,12 +307,12 @@ for how to do it.
272307

273308
## Resources
274309

310+
* Status overview of AsyncWrap: https://github.com/nodejs/tracing-wg/issues/29
275311
* An intro to AsyncWrap by Trevor Norris: http://blog.trevnorris.com/2015/02/asyncwrap-tutorial-introduction.html (outdated)
276312
* Slides from a local talk Andreas Madsen did on AsyncWrap:
277313
https://github.com/AndreasMadsen/talk-async-wrap (outdated)
278-
* There was also some discussion in [issue #21](https://github.com/nodejs/tracing-wg/issues/21#issuecomment-142727693).
279314
* Complete (hopefully) long-stack-trace module that uses AsyncWrap: https://github.com/AndreasMadsen/trace
280-
* Visualization tool for AsyncWrap wrap: https://github.com/AndreasMadsen/dprof
315+
* Visualization tool for AsyncWrap: https://github.com/AndreasMadsen/dprof
281316

282317
----
283318

docs/AsyncWrap/example-run.js renamed to docs/AsyncWrap/example-trace/run.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const fs = require('fs');
44
const net = require('net');
5-
const getStack = require('./example-trace.js');
5+
const getStack = require('./trace.js');
66

77
Error.stackTraceLimit = Infinity;
88

docs/AsyncWrap/example-trace.js renamed to docs/AsyncWrap/example-trace/trace.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ asyncWrap.enable();
88
// global state variable, that contains the current stack trace
99
let currentStack = '';
1010

11-
function init(provider, parent) {
11+
function init(provider, uid, parent) {
1212
// When a handle is created, collect the stack trace such that we later
1313
// can see what involved the handle constructor.
1414
const localStack = (new Error()).stack.split('\n').slice(1).join('\n');

0 commit comments

Comments
 (0)