From d00cde3964612a866270a0a4828921edb3ad0fee Mon Sep 17 00:00:00 2001 From: Jean-Francois Date: Thu, 5 Dec 2013 11:48:05 +0100 Subject: [PATCH] Fix for AVATAR_JS-95, support for domains reviewed-by: asquare --- build.xml | 2 + patches/test/simple/test-domain.js.patch | 29 +++ project.properties | 11 + .../net/java/avatar/js/eventloop/Event.java | 15 +- .../java/avatar/js/eventloop/EventLoop.java | 101 ++++++++- .../js/eventloop/LoopCallbackHandler.java | 196 +++++++++++++----- src/main/js/lib/process.js | 39 +++- src/main/js/net/java/avatar/js/init.js | 64 +++++- src/main/js/net/java/avatar/js/timer_wrap.js | 7 + 9 files changed, 389 insertions(+), 75 deletions(-) create mode 100644 patches/test/simple/test-domain.js.patch diff --git a/build.xml b/build.xml index ccdc3b5..d3629cb 100644 --- a/build.xml +++ b/build.xml @@ -285,6 +285,7 @@ + @@ -361,6 +362,7 @@ + diff --git a/patches/test/simple/test-domain.js.patch b/patches/test/simple/test-domain.js.patch new file mode 100644 index 0000000..bef12e9 --- /dev/null +++ b/patches/test/simple/test-domain.js.patch @@ -0,0 +1,29 @@ +--- ../node/test/simple/test-domain.js 2013-08-22 13:48:54.000000000 +0200 ++++ test/simple/test-domain.js 2013-12-04 14:07:46.000000000 +0100 +@@ -70,7 +70,7 @@ + assert.equal(er.domainThrown, true); + break; + +- case "ENOENT, open 'this file does not exist'": ++ case "ENOENT, no such file or directory 'this file does not exist'": + assert.equal(er.domain, d); + assert.equal(er.domainThrown, false); + assert.equal(typeof er.domainBound, 'function'); +@@ -80,7 +80,7 @@ + assert.equal(typeof er.errno, 'number'); + break; + +- case "ENOENT, open 'stream for nonexistent file'": ++ case "ENOENT, no such file or directory 'stream for nonexistent file'": + assert.equal(typeof er.errno, 'number'); + assert.equal(er.code, 'ENOENT'); + assert.equal(er_path, 'stream for nonexistent file'); +@@ -104,7 +104,7 @@ + assert.ok(!er.domainBound); + break; + +- case 'Cannot call method \'isDirectory\' of undefined': ++ case 'Cannot read property "isDirectory" from undefined': + assert.equal(er.domain, d); + assert.ok(!er.domainEmitter); + assert.ok(!er.domainBound); diff --git a/project.properties b/project.properties index 53bf9be..de05644 100644 --- a/project.properties +++ b/project.properties @@ -142,6 +142,17 @@ source.test.simple.list = \ test-dgram-send-bad-arguments.js \ test-dgram-udp4.js \ test-dgram-unref.js \ + test-domain-crypto.js \ + test-domain-exit-dispose.js \ + test-domain-from-timer.js \ + test-domain-http-server.js \ + test-domain-implicit-fs.js \ + test-domain-multi.js \ + test-domain-nested-throw.js \ + test-domain-nested.js \ + test-domain-stack.js \ + test-domain-timers.js \ + test-domain.js \ test-error-reporting.js \ test-event-emitter-add-listeners.js \ test-event-emitter-check-listener-leaks.js \ diff --git a/src/main/java/net/java/avatar/js/eventloop/Event.java b/src/main/java/net/java/avatar/js/eventloop/Event.java index ce18f58..8595601 100644 --- a/src/main/java/net/java/avatar/js/eventloop/Event.java +++ b/src/main/java/net/java/avatar/js/eventloop/Event.java @@ -27,8 +27,8 @@ package net.java.avatar.js.eventloop; import java.security.AccessControlContext; import java.security.AccessController; +import jdk.nashorn.api.scripting.ScriptObjectMirror; -import net.java.avatar.js.eventloop.Callback; import net.java.avatar.js.Server; public final class Event { @@ -37,12 +37,15 @@ public final class Event { private final Callback callback; private final Object[] args; private final AccessControlContext ctx; - public Event(final String name, final Callback callback) { - this(name, callback, (Object[]) null); + private final ScriptObjectMirror domain; + + public Event(final String name, final ScriptObjectMirror domain, final Callback callback) { + this(name, domain, callback, (Object[]) null); } - public Event(final String name, final Callback callback, final Object arg) { + public Event(final String name, final ScriptObjectMirror domain, final Callback callback, final Object arg) { this.name = name; + this.domain = domain; this.callback = callback; this.args = new Object[1]; this.args[0] = arg; @@ -55,6 +58,7 @@ public final class Event { public Event(final String name, final Callback callback, final Object... args) { this.name = name; + this.domain = null; this.callback = callback; this.args = args == null ? null : args.clone(); if(System.getSecurityManager() != null) { @@ -107,4 +111,7 @@ public final class Event { return sb.toString(); } + public ScriptObjectMirror getDomain() { + return domain; + } } diff --git a/src/main/java/net/java/avatar/js/eventloop/EventLoop.java b/src/main/java/net/java/avatar/js/eventloop/EventLoop.java index e7f0135..8bb1f58 100644 --- a/src/main/java/net/java/avatar/js/eventloop/EventLoop.java +++ b/src/main/java/net/java/avatar/js/eventloop/EventLoop.java @@ -41,12 +41,16 @@ import javax.script.ScriptException; import jdk.nashorn.api.scripting.NashornException; import jdk.nashorn.internal.runtime.ECMAException; import jdk.nashorn.internal.runtime.ScriptObject; +import jdk.nashorn.api.scripting.ScriptObjectMirror; import net.java.avatar.js.dns.DNS; import net.java.avatar.js.log.Logger; import net.java.avatar.js.log.Logging; import net.java.libuv.LibUV; import net.java.libuv.cb.AsyncCallback; import net.java.libuv.cb.CallbackExceptionHandler; +import net.java.libuv.cb.CallbackDomainProvider; +import net.java.libuv.cb.CallbackHandler; +import net.java.libuv.cb.CallbackHandlerFactory; import net.java.libuv.handles.AsyncHandle; import net.java.libuv.handles.LoopHandle; @@ -69,6 +73,8 @@ public final class EventLoop { private Callback uncaughtExceptionHandler = null; private Exception pendingException = null; + private ScriptObjectMirror domain; + public static final class Handle implements AutoCloseable { private final AtomicInteger hooks; @@ -106,9 +112,14 @@ public final class EventLoop { uncaughtExceptionHandler = handler; } - public void nextTick(final Event event) { + public void nextTick(final Callback cb) { assert Thread.currentThread() == mainThread : "called from non-event thread " + Thread.currentThread().getName(); - eventQueue.add(event); + eventQueue.add(new Event("nextTick", cb)); + } + + public void nextTickWithDomain(final Callback cb, ScriptObjectMirror evtDomain) { + assert Thread.currentThread() == mainThread : "called from non-event thread " + Thread.currentThread().getName(); + eventQueue.add(new Event("nextTickWithDomain", evtDomain, cb)); } public void post(final Callback cb, Object... args) { @@ -141,7 +152,17 @@ public final class EventLoop { for (Event event = eventQueue.poll(); event != null; event = eventQueue.poll()) { - processEvent(event); + ScriptObjectMirror evtDomain = event.getDomain(); + if (evtDomain != null) { + if (isDisposed(evtDomain)) { + continue; + } + enterDomain(evtDomain); + processEvent(event); + exitDomain(evtDomain); + } else { + processEvent(event); + } } } } @@ -198,19 +219,25 @@ public final class EventLoop { private static final String UNCAUGHT_EXCEPTION_NAME = "uncaughtException"; public boolean handleCallbackException(final Exception ex) { - + boolean handled = true; // callback to check if an uncaught exception handler has been registered by the user final Object[] registeredArgs = {null}; if (isHandlerRegistered != null) { try { isHandlerRegistered.call(UNCAUGHT_EXCEPTION_NAME, registeredArgs); } catch (final Exception e) { - return false; + handled = false; } } if (registeredArgs[0] == null || uncaughtExceptionHandler == null) { // no handler registered - rethrow uncaught exceptions + handled = false; + } + + if (!handled && domain == null) { + // No domain and no uncaughtException Handler registered + // rethrowing return false; } @@ -333,6 +360,8 @@ public final class EventLoop { this.uvVersion = uvVersion; this.logging = logging; this.dns = new DNS(this); + + final LoopCallbackHandler defaultHandler = new LoopCallbackHandler(this); this.uvLoop = new LoopHandle(new CallbackExceptionHandler() { @Override public void handle(final Exception ex) { @@ -345,8 +374,23 @@ public final class EventLoop { stop(); } } - }, new LoopCallbackHandler(this)); - + }, + new CallbackHandlerFactory() { + @Override + public CallbackHandler newCallbackHandlerWithDomain(Object domain) { + return new LoopCallbackHandler(EventLoop.this, domain); + } + @Override + public CallbackHandler newCallbackHandler() { + return defaultHandler; + } + }, + new CallbackDomainProvider() { + @Override + public Object getDomain() { + return EventLoop.this.getDomain(); + } + }); this.instanceNumber = instanceNumber; this.executor = executor; @@ -399,4 +443,47 @@ public final class EventLoop { return uvLoop; } + public void setDomain(ScriptObjectMirror obj) { + assert Thread.currentThread() == mainThread : "called from non-event thread " + Thread.currentThread().getName(); + domain = obj; + } + + public ScriptObjectMirror getDomain() { + assert Thread.currentThread() == mainThread : "called from non-event thread " + Thread.currentThread().getName(); + return domain; + } + + public boolean isDisposed(ScriptObjectMirror domain) { + assert Thread.currentThread() == mainThread : "called from non-event thread " + Thread.currentThread().getName(); + return Boolean.TRUE.equals(domain.getMember("_disposed")); + } + + public void enterDomain(ScriptObjectMirror domain) { + assert Thread.currentThread() == mainThread : "called from non-event thread " + Thread.currentThread().getName(); + domain.callMember("enter"); + } + + public void exitDomain(ScriptObjectMirror domain) { + assert Thread.currentThread() == mainThread : "called from non-event thread " + Thread.currentThread().getName(); + domain.callMember("exit"); + } + + public boolean isDisposed(Object domain) { + if (domain instanceof ScriptObjectMirror) { + isDisposed((ScriptObjectMirror) domain); + } + return false; + } + + public void enterDomain(Object domain) { + if (domain instanceof ScriptObjectMirror) { + enterDomain((ScriptObjectMirror) domain); + } + } + + public void exitDomain(Object domain) { + if (domain instanceof ScriptObjectMirror) { + exitDomain((ScriptObjectMirror) domain); + } + } } diff --git a/src/main/java/net/java/avatar/js/eventloop/LoopCallbackHandler.java b/src/main/java/net/java/avatar/js/eventloop/LoopCallbackHandler.java index 73fad47..035aa7c 100644 --- a/src/main/java/net/java/avatar/js/eventloop/LoopCallbackHandler.java +++ b/src/main/java/net/java/avatar/js/eventloop/LoopCallbackHandler.java @@ -63,16 +63,42 @@ import net.java.libuv.cb.UDPSendCallback; final class LoopCallbackHandler implements CallbackHandler { private final EventLoop eventLoop; - + private final Object domain; public LoopCallbackHandler(EventLoop eventLoop) { + this(eventLoop, null); + } + + public LoopCallbackHandler(EventLoop eventLoop, Object domain) { this.eventLoop = eventLoop; + this.domain = domain; } + private boolean shouldCall() { + if (domain != null) { + if (eventLoop.isDisposed(domain)) { + return false; + } + + eventLoop.enterDomain(domain); + } + + return true; + } + + private void post() throws Exception { + if (domain != null) { + eventLoop.exitDomain(domain); + } + eventLoop.processQueuedEvents(); + } + @Override public void handleAsyncCallback(final AsyncCallback cb, final int status) { try { - cb.onSend(status); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onSend(status); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -81,8 +107,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleCheckCallback(final CheckCallback cb, final int status) { try { - cb.onCheck(status); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onCheck(status); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -91,8 +119,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleSignalCallback(final SignalCallback cb, final int signum) { try { - cb.onSignal(signum); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onSignal(signum); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -101,8 +131,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleStreamReadCallback(final StreamReadCallback cb, final ByteBuffer data) { try { - cb.onRead(data); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onRead(data); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -111,8 +143,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleStreamRead2Callback(final StreamRead2Callback cb, final ByteBuffer data, final long handle, final int type) { try { - cb.onRead2(data, handle, type); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onRead2(data, handle, type); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -121,8 +155,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleStreamWriteCallback(final StreamWriteCallback cb, final int status, final Exception error) { try { - cb.onWrite(status, error); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onWrite(status, error); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -131,8 +167,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleStreamConnectCallback(final StreamConnectCallback cb, final int status, final Exception error) { try { - cb.onConnect(status, error); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onConnect(status, error); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -141,8 +179,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleStreamConnectionCallback(final StreamConnectionCallback cb, final int status, final Exception error) { try { - cb.onConnection(status, error); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onConnection(status, error); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -151,8 +191,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleStreamCloseCallback(final StreamCloseCallback cb) { try { - cb.onClose(); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onClose(); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -161,8 +203,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleStreamShutdownCallback(final StreamShutdownCallback cb, final int status, final Exception error) { try { - cb.onShutdown(status, error); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onShutdown(status, error); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -171,8 +215,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleFileCallback(final FileCallback cb, final Object context, final Exception error) { try { - cb.onDone(context, error); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onDone(context, error); + post(); + } } catch (final Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -182,8 +228,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleFileCloseCallback(final FileCloseCallback cb, final Object context, final int fd, final Exception error) { try { - cb.onClose(context, fd, error); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onClose(context, fd, error); + post(); + } } catch (final Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -192,8 +240,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleFileOpenCallback(final FileOpenCallback cb, final Object context, final int fd, final Exception error) { try { - cb.onOpen(context, fd, error); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onOpen(context, fd, error); + post(); + } } catch (final Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -202,8 +252,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleFileReadCallback(final FileReadCallback cb, final Object context, final int bytesRead, final ByteBuffer data, final Exception error) { try { - cb.onRead(context, bytesRead, data, error); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onRead(context, bytesRead, data, error); + post(); + } } catch (final Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -212,8 +264,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleFileReadDirCallback(final FileReadDirCallback cb, final Object context, final String[] names, final Exception error) { try { - cb.onReadDir(context, names, error); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onReadDir(context, names, error); + post(); + } } catch (final Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -222,8 +276,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleFileReadLinkCallback(final FileReadLinkCallback cb, final Object context, final String name, final Exception error) { try { - cb.onReadLink(context, name, error); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onReadLink(context, name, error); + post(); + } } catch (final Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -232,8 +288,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleFileStatsCallback(final FileStatsCallback cb, final Object context, final Stats stats, final Exception error) { try { - cb.onStats(context, stats, error); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onStats(context, stats, error); + post(); + } } catch (final Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -242,8 +300,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleFileUTimeCallback(final FileUTimeCallback cb, final Object context, final long time, final Exception error) { try { - cb.onUTime(context, time, error); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onUTime(context, time, error); + post(); + } } catch (final Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -252,8 +312,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleFileWriteCallback(final FileWriteCallback cb, final Object context, final int bytesWritten, final Exception error) { try { - cb.onWrite(context, bytesWritten, error); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onWrite(context, bytesWritten, error); + post(); + } } catch (final Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -262,8 +324,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleFileEventCallback(FileEventCallback cb, int status, String event, String filename) { try { - cb.onEvent(status, event, filename); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onEvent(status, event, filename); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -273,7 +337,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleFilePollCallback(FilePollCallback cb, int status, Stats previous, Stats current) { try { - cb.onPoll(status, previous, current); + if (shouldCall()) { + cb.onPoll(status, previous, current); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -282,7 +349,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleFilePollStopCallback(FilePollStopCallback cb) { try { - cb.onStop(); + if (shouldCall()) { + cb.onStop(); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -291,7 +361,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleProcessCloseCallback(ProcessCloseCallback cb) { try { - cb.onClose(); + if (shouldCall()) { + cb.onClose(); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -300,7 +373,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleProcessExitCallback(ProcessExitCallback cb, int status, int signal, Exception error) { try { - cb.onExit(status, signal, error); + if (shouldCall()) { + cb.onExit(status, signal, error); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -309,8 +385,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleTimerCallback(final TimerCallback cb, final int status) { try { - cb.onTimer(status); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onTimer(status); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -319,8 +397,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleUDPRecvCallback(final UDPRecvCallback cb, final int nread, final ByteBuffer data, final Address address) { try { - cb.onRecv(nread, data, address); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onRecv(nread, data, address); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -329,8 +409,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleUDPSendCallback(final UDPSendCallback cb, final int status, final Exception error) { try { - cb.onSend(status, error); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onSend(status, error); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -339,8 +421,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleUDPCloseCallback(final UDPCloseCallback cb) { try { - cb.onClose(); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onClose(); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } @@ -349,8 +433,10 @@ final class LoopCallbackHandler implements CallbackHandler { @Override public void handleIdleCallback(IdleCallback cb, int status) { try { - cb.onIdle(status); - eventLoop.processQueuedEvents(); + if (shouldCall()) { + cb.onIdle(status); + post(); + } } catch (Exception ex) { eventLoop.loop().getExceptionHandler().handle(ex); } diff --git a/src/main/js/lib/process.js b/src/main/js/lib/process.js index fcb95ec..0f3c3c8 100644 --- a/src/main/js/lib/process.js +++ b/src/main/js/lib/process.js @@ -61,7 +61,6 @@ var eventloop = __avatar.eventloop; var LibUV = Packages.net.java.libuv.LibUV; var Process = Packages.net.java.avatar.js.os.Process; var Server = Packages.net.java.avatar.js.Server; -var Event = Packages.net.java.avatar.js.eventloop.Event var Constants = Packages.net.java.avatar.js.constants.Constants; var Signals = Packages.net.java.libuv.handles.SignalHandle; @@ -430,16 +429,14 @@ Object.defineProperty(exports, 'hrtime', { Object.defineProperty(exports, 'nextTick', { enumerable: true, + configurable: true, value: function(callback) { if (this._exiting) { return; } - eventloop.nextTick( - new Event('nextTick', function(name, args) { - callback(); - } - ) - ); + eventloop.nextTick(function(name, args) { + callback(); + }); } }); @@ -588,8 +585,36 @@ exports.openStdin = function() { } exports._usingDomains = function() { + // redefine nextTick at this time to speedup + // domain event posting + Object.defineProperty(exports, 'nextTick', { + enumerable: true, + value: function(callback) { + if (this._exiting) { + return; + } + eventloop.nextTickWithDomain(function(name, args) { + callback(); + }, process.domain) + } + }); } +var ScriptUtils = Packages.jdk.nashorn.api.scripting.ScriptUtils +Object.defineProperty(exports, 'domain', { + enumerable : true, + set : function(domain) { + if (domain) { + eventloop.domain = domain; + } else { + eventloop.domain = null; + } + }, + get : function() { + return ScriptUtils.unwrap(eventloop.domain); + } +}); + var Check = Packages.net.java.libuv.handles.CheckHandle; var Idle = Packages.net.java.libuv.handles.IdleHandle; var checkHandle = new Check(eventloop.loop()); diff --git a/src/main/js/net/java/avatar/js/init.js b/src/main/js/net/java/avatar/js/init.js index 8e312a6..9a1e875 100644 --- a/src/main/js/net/java/avatar/js/init.js +++ b/src/main/js/net/java/avatar/js/init.js @@ -200,6 +200,67 @@ var gc = global.gc; console.log(this.stack); }; + var fatalProcessing = function(er) { + var caught = false; + if (process.domain) { // From nodejs + var domain = process.domain; + var domainModule = NativeModule.require('domain'); + var domainStack = domainModule._stack; + + // ignore errors on disposed domains. + // + // XXX This is a bit stupid. We should probably get rid of + // domain.dispose() altogether. It's almost always a terrible + // idea. --isaacs + if (domain._disposed) + return true; + + er.domain = domain; + er.domainThrown = true; + // wrap this in a try/catch so we don't get infinite throwing + try { + // One of three things will happen here. + // + // 1. There is a handler, caught = true + // 2. There is no handler, caught = false + // 3. It throws, caught = false + // + // If caught is false after this, then there's no need to exit() + // the domain, because we're going to crash the process anyway. + caught = domain.emit('error', er); + // Exit all domains on the stack. Uncaught exceptions end the + // current tick and no domains should be left on the stack + // between ticks. + var domainModule = NativeModule.require('domain'); + domainStack.length = 0; + domainModule.active = process.domain = null; + } catch (er2) { + // The domain error handler threw! oh no! + // See if another domain can catch THIS error, + // or else crash on the original one. + // If the user already exited it, then don't double-exit. + if (domain === domainModule.active) + domainStack.pop(); + if (domainStack.length) { + var parentDomain = domainStack[domainStack.length - 1]; + process.domain = domainModule.active = parentDomain; + caught = fatalProcessing(er2); + } else + caught = false; + } + } else { + caught = process.emit('uncaughtException', er); + } + + // Avatar-js specific handling, + // exit is handled in the class that is catching the rethrown er + if (!caught) { + throw er; + } + + return caught; + }; + __avatar.eventloop.setUncaughtExceptionHandler( function(name, args) { var listeners = process.listeners('uncaughtException'); @@ -208,13 +269,12 @@ var gc = global.gc; } }, function(name, args) { - var ctx = Packages.net.java.avatar.js.eventloop.EventLoop; var e = new Error(); var ex = args[0]; for (var k in ex) { e[k] = ex[k]; } - process.emit(name, e); + fatalProcessing(e); } ); diff --git a/src/main/js/net/java/avatar/js/timer_wrap.js b/src/main/js/net/java/avatar/js/timer_wrap.js index afc085b..5e809ab 100644 --- a/src/main/js/net/java/avatar/js/timer_wrap.js +++ b/src/main/js/net/java/avatar/js/timer_wrap.js @@ -32,7 +32,14 @@ var that = this; this._timer.setTimerFiredCallback(function(status) { if (that.ontimeout) { + // When a timer is unref, the domain is set on the wrap. + if (that.domain) { + that.domain.enter(); + } that.ontimeout(); + if (that.domain) { + that.domain.exit(); + } } }); }