mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
Domain feature
This is a squashed commit of the main work done on the domains-wip branch.
The original commit messages are preserved for posterity:
* Implicitly add EventEmitters to active domain
* Implicitly add timers to active domain
* domain: add members, remove ctor cb
* Don't hijack bound callbacks for Domain error events
* Add dispose method
* Add domain.remove(ee) method
* A test of multiple domains in process at once
* Put the active domain on the process object
* Only intercept error arg if explicitly requested
* Typo
* Don't auto-add new domains to the current domain
While an automatic parent/child relationship is sort of neat,
and leads to some nice error-bubbling characteristics, it also
results in keeping a reference to every EE and timer created,
unless domains are explicitly disposed of.
* Explicitly adding one domain to another is still fine, of course.
* Don't allow circular domain->domain memberships
* Disposing of a domain removes it from its parent
* Domain disposal turns functions into no-ops
* More documentation of domains
* More thorough dispose() semantics
* An example using domains in an HTTP server
* Don't handle errors on a disposed domain
* Need to push, even if the same domain is entered multiple times
* Array.push is too slow for the EE Ctor
* lint domain
* domain: docs
* Also call abort and destroySoon to clean up event emitters
* domain: Wrap destroy methods in a try/catch
* Attach tick callbacks to active domain
* domain: Only implicitly bind timers, not explicitly
* domain: Don't fire timers when disposed.
* domain: Simplify naming so that MakeCallback works on Timers
* Add setInterval and nextTick to domain test
* domain: Make stack private
This commit is contained in:
1 parent
a26bee8fa1
commit
963459d736
11 files changed
+879
-3
No files matched your search
@@ -8,6 +8,7 @@
|
||||
* [Process](process.html)
|
||||
* [Utilities](util.html)
|
||||
* [Events](events.html)
|
||||
* [Domain](domain.html)
|
||||
* [Buffer](buffer.html)
|
||||
* [Stream](stream.html)
|
||||
* [Crypto](crypto.html)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
@include process
|
||||
@include util
|
||||
@include events
|
||||
@include domain
|
||||
@include buffer
|
||||
@include stream
|
||||
@include crypto
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
# Domain
|
||||
|
||||
Stability: 1 - Experimental
|
||||
|
||||
Domains provide a way to handle multiple different IO operations as a
|
||||
single group. If any of the event emitters or callbacks registered to a
|
||||
domain emit an `error` event, or throw an error, then the domain object
|
||||
will be notified, rather than losing the context of the error in the
|
||||
`process.on('uncaughtException')` handler, or causing the program to
|
||||
exit with an error code.
|
||||
|
||||
This feature is new in Node version 0.8. It is a first pass, and is
|
||||
expected to change significantly in future versions. Please use it and
|
||||
provide feedback.
|
||||
|
||||
Due to their experimental nature, the Domains features are disabled unless
|
||||
the `domain` module is loaded at least once. No domains are created or
|
||||
registered by default. This is by design, to prevent adverse effects on
|
||||
current programs. It is expected to be enabled by default in future
|
||||
Node.js versions.
|
||||
|
||||
## Additions to Error objects
|
||||
|
||||
<!-- type=misc -->
|
||||
|
||||
Any time an Error object is routed through a domain, a few extra fields
|
||||
are added to it.
|
||||
|
||||
* `error.domain` The domain that first handled the error.
|
||||
* `error.domain_emitter` The event emitter that emitted an 'error' event
|
||||
with the error object.
|
||||
* `error.domain_bound` The callback function which was bound to the
|
||||
domain, and passed an error as its first argument.
|
||||
* `error.domain_thrown` A boolean indicating whether the error was
|
||||
thrown, emitted, or passed to a bound callback function.
|
||||
|
||||
## Implicit Binding
|
||||
|
||||
<!--type=misc-->
|
||||
|
||||
If domains are in use, then all new EventEmitter objects (including
|
||||
Stream objects, requests, responses, etc.) will be implicitly bound to
|
||||
the active domain at the time of their creation.
|
||||
|
||||
Additionally, callbacks passed to lowlevel event loop requests (such as
|
||||
to fs.open, or other callback-taking methods) will automatically be
|
||||
bound to the active domain. If they throw, then the domain will catch
|
||||
the error.
|
||||
|
||||
In order to prevent excessive memory usage, Domain objects themselves
|
||||
are not implicitly added as children of the active domain. If they
|
||||
were, then it would be too easy to prevent request and response objects
|
||||
from being properly garbage collected.
|
||||
|
||||
If you *want* to nest Domain objects as children of a parent Domain,
|
||||
then you must explicitly add them, and then dispose of them later.
|
||||
|
||||
Implicit binding routes thrown errors and `'error'` events to the
|
||||
Domain's `error` event, but does not register the EventEmitter on the
|
||||
Domain, so `domain.dispose()` will not shut down the EventEmitter.
|
||||
Implicit binding only takes care of thrown errors and `'error'` events.
|
||||
|
||||
## domain.create()
|
||||
|
||||
* return: {Domain}
|
||||
|
||||
Returns a new Domain object.
|
||||
|
||||
## Class: Domain
|
||||
|
||||
The Domain class encapsulates the functionality of routing errors and
|
||||
uncaught exceptions to the active Domain object.
|
||||
|
||||
Domain is a child class of EventEmitter. To handle the errors that it
|
||||
catches, listen to its `error` event.
|
||||
|
||||
### domain.members
|
||||
|
||||
* {Array}
|
||||
|
||||
An array of timers and event emitters that have been explicitly added
|
||||
to the domain.
|
||||
|
||||
### domain.add(emitter)
|
||||
|
||||
* `emitter` {EventEmitter | Timer} emitter or timer to be added to the domain
|
||||
|
||||
Explicitly adds an emitter to the domain. If any event handlers called by
|
||||
the emitter throw an error, or if the emitter emits an `error` event, it
|
||||
will be routed to the domain's `error` event, just like with implicit
|
||||
binding.
|
||||
|
||||
This also works with timers that are returned from `setInterval` and
|
||||
`setTimeout`. If their callback function throws, it will be caught by
|
||||
the domain 'error' handler.
|
||||
|
||||
If the Timer or EventEmitter was already bound to a domain, it is removed
|
||||
from that one, and bound to this one instead.
|
||||
|
||||
### domain.remove(emitter)
|
||||
|
||||
* `emitter` {EventEmitter | Timer} emitter or timer to be removed from the domain
|
||||
|
||||
The opposite of `domain.add(emitter)`. Removes domain handling from the
|
||||
specified emitter.
|
||||
|
||||
### domain.bind(cb)
|
||||
|
||||
* `cb` {Function} The callback function
|
||||
* return: {Function} The bound function
|
||||
|
||||
The returned function will be a wrapper around the supplied callback
|
||||
function. When the returned function is called, any errors that are
|
||||
thrown will be routed to the domain's `error` event.
|
||||
|
||||
#### Example
|
||||
|
||||
var d = domain.create();
|
||||
|
||||
function readSomeFile(filename, cb) {
|
||||
fs.readFile(filename, d.bind(function(er, data) {
|
||||
// if this throws, it will also be passed to the domain
|
||||
return cb(er, JSON.parse(data));
|
||||
}));
|
||||
}
|
||||
|
||||
d.on('error', function(er) {
|
||||
// an error occurred somewhere.
|
||||
// if we throw it now, it will crash the program
|
||||
// with the normal line number and stack message.
|
||||
});
|
||||
|
||||
### domain.intercept(cb)
|
||||
|
||||
* `cb` {Function} The callback function
|
||||
* return: {Function} The intercepted function
|
||||
|
||||
This method is almost identical to `domain.bind(cb)`. However, in
|
||||
addition to catching thrown errors, it will also intercept `Error`
|
||||
objects sent as the first argument to the function.
|
||||
|
||||
In this way, the common `if (er) return cb(er);` pattern can be replaced
|
||||
with a single error handler in a single place.
|
||||
|
||||
#### Example
|
||||
|
||||
var d = domain.create();
|
||||
|
||||
function readSomeFile(filename, cb) {
|
||||
fs.readFile(filename, d.intercept(function(er, data) {
|
||||
// if this throws, it will also be passed to the domain
|
||||
// additionally, we know that 'er' will always be null,
|
||||
// so the error-handling logic can be moved to the 'error'
|
||||
// event on the domain instead of being repeated throughout
|
||||
// the program.
|
||||
return cb(er, JSON.parse(data));
|
||||
}));
|
||||
}
|
||||
|
||||
d.on('error', function(er) {
|
||||
// an error occurred somewhere.
|
||||
// if we throw it now, it will crash the program
|
||||
// with the normal line number and stack message.
|
||||
});
|
||||
|
||||
### domain.dispose()
|
||||
|
||||
The dispose method destroys a domain, and makes a best effort attempt to
|
||||
clean up any and all IO that is associated with the domain. Streams are
|
||||
aborted, ended, closed, and/or destroyed. Timers are cleared.
|
||||
Explicitly bound callbacks are no longer called. Any error events that
|
||||
are raised as a result of this are ignored.
|
||||
|
||||
The intention of calling `dispose` is generally to prevent cascading
|
||||
errors when a critical part of the Domain context is found to be in an
|
||||
error state.
|
||||
|
||||
Note that IO might still be performed. However, to the highest degree
|
||||
possible, once a domain is disposed, further errors from the emitters in
|
||||
that set will be ignored. So, even if some remaining actions are still
|
||||
in flight, Node.js will not communicate further about them.
|
||||
Reference in new issue
Block a user