Skip to content

Commit

Permalink
async_wrap: notify post if intercepted exception
Browse files Browse the repository at this point in the history
The second argument of the post callback is a boolean indicating whether
the callback threw and was intercepted by uncaughtException or a domain.

Currently node::MakeCallback has no way of retrieving a uid for the
object. This is coming in a future patch.

Ref: #7048
PR-URL: #5756
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: Andreas Madsen <[email protected]>
  • Loading branch information
trevnorris authored and Myles Borins committed Jul 12, 2016
1 parent 0642a14 commit c06e2b0
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 2 deletions.
5 changes: 4 additions & 1 deletion src/async-wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "v8-profiler.h"

using v8::Array;
using v8::Boolean;
using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
Expand Down Expand Up @@ -231,7 +232,9 @@ Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
Local<Value> ret = cb->Call(context, argc, argv);

if (ran_init_callback() && !post_fn.IsEmpty()) {
if (post_fn->Call(context, 1, &uid).IsEmpty())
Local<Value> did_throw = Boolean::New(env()->isolate(), ret.IsEmpty());
Local<Value> vals[] = { uid, did_throw };
if (post_fn->Call(context, arraysize(vals), vals).IsEmpty())
FatalError("node::AsyncWrap::MakeCallback", "post hook threw");
}

Expand Down
7 changes: 6 additions & 1 deletion src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1172,7 +1172,12 @@ Local<Value> MakeCallback(Environment* env,
Local<Value> ret = callback->Call(recv, argc, argv);

if (ran_init_callback && !post_fn.IsEmpty()) {
if (post_fn->Call(object, 0, nullptr).IsEmpty())
Local<Value> did_throw = Boolean::New(env->isolate(), ret.IsEmpty());
// Currently there's no way to retrieve an uid from node::MakeCallback().
// This needs to be fixed.
Local<Value> vals[] =
{ Undefined(env->isolate()).As<Value>(), did_throw };
if (post_fn->Call(object, arraysize(vals), vals).IsEmpty())
FatalError("node::MakeCallback", "post hook threw");
}

Expand Down
34 changes: 34 additions & 0 deletions test/parallel/test-async-wrap-post-did-throw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict';

require('../common');
const assert = require('assert');
const async_wrap = process.binding('async_wrap');
var asyncThrows = 0;
var uncaughtExceptionCount = 0;

process.on('uncaughtException', (e) => {
assert.equal(e.message, 'oh noes!', 'error messages do not match');
});

process.on('exit', () => {
process.removeAllListeners('uncaughtException');
assert.equal(uncaughtExceptionCount, 1);
assert.equal(uncaughtExceptionCount, asyncThrows);
});

function init() { }
function post(id, threw) {
if (threw)
uncaughtExceptionCount++;
}

async_wrap.setupHooks({ init, post });
async_wrap.enable();

// Timers still aren't supported, so use crypto API.
// It's also important that the callback not happen in a nextTick, like many
// error events in core.
require('crypto').randomBytes(0, () => {
asyncThrows++;
throw new Error('oh noes!');
});

0 comments on commit c06e2b0

Please sign in to comment.