Fix for AVATAR_JS-34, Reviewed-by: asquare

This commit is contained in:
jfdenise 2013-10-24 14:11:41 +02:00
parent 1623ac3f40
commit 804927eaa3
10 changed files with 180 additions and 547 deletions

View File

@ -2,6 +2,7 @@ source.compatible.version = 0.10.18
libuv.compatible.version = 0.10.15
source.lib.modules = \
_linklist.js \
_stream_duplex.js \
_stream_passthrough.js \
_stream_readable.js \
@ -28,6 +29,7 @@ source.lib.modules = \
readline.js \
stream.js \
string_decoder.js \
timers.js \
tls.js \
tty.js \
url.js \

View File

@ -151,7 +151,6 @@ public final class Server {
throw ex;
}
} finally {
eventLoop.timer().clearAll();
eventLoop.stop();
logging.shutdown();
}

View File

@ -55,13 +55,13 @@ import javax.script.ScriptException;
import net.java.avatar.js.dns.DNS;
import net.java.avatar.js.log.Logger;
import net.java.avatar.js.log.Logging;
import net.java.avatar.js.timers.Timers;
import net.java.libuv.Callback;
import net.java.libuv.CallbackExceptionHandler;
import net.java.libuv.CallbackHandler;
import net.java.libuv.CheckCallback;
import net.java.libuv.FileCallback;
import net.java.libuv.IdleCallback;
import net.java.libuv.LibUV;
import net.java.libuv.ProcessCallback;
import net.java.libuv.SignalCallback;
@ -94,7 +94,6 @@ public final class EventLoop {
private final String version;
private final String uvVersion;
private final Logging logging;
private final Timers timer;
private final DNS dns;
private final LoopHandle uvLoop;
private final int instanceNumber;
@ -140,7 +139,7 @@ public final class EventLoop {
private Thread mainThread = null;
private Exception pendingException = null;
private volatile boolean closed;
public static final class Handle implements AutoCloseable {
private final AtomicInteger hooks;
@ -199,13 +198,12 @@ public final class EventLoop {
hooks.get() != 0 ||
!maybeIdle.get() ||
activeTasks.get() != 0 ||
timer.isPending() ||
// LinkedBlockingQueue.isEmpty() is quick but not always accurate
// peek first element to make sure the queues are really empty
// do this last to avoid unnecessary iteration
tasks.peek() != null ||
eventQueue.peek() != null)) {
// throw pending exception, if any
if (pendingException != null) {
final Exception pex = pendingException;
@ -319,7 +317,6 @@ public final class EventLoop {
public void release() {
hooks.set(0);
timer.clearAll();
tasks.clear();
}
@ -459,7 +456,6 @@ public final class EventLoop {
this.version = version;
this.uvVersion = uvVersion;
this.logging = logging;
this.timer = new Timers(this);
this.dns = new DNS(this);
this.uvLoop = new LoopHandle(new CallbackExceptionHandler() {
@Override
@ -543,8 +539,18 @@ public final class EventLoop {
uvLoop.getExceptionHandler().handle(ex);
}
}
});
@Override
public void handleIdleCallback(IdleCallback cb, int status) {
maybeIdle.set(false);
try {
cb.call(status);
} catch (Exception ex) {
uvLoop.getExceptionHandler().handle(ex);
}
}
});
this.instanceNumber = instanceNumber;
LibUV.chdir(workDir);
@ -568,10 +574,6 @@ public final class EventLoop {
return logging.get(name);
}
public Timers timer() {
return timer;
}
public DNS dns() {
return dns;
}

View File

@ -1,178 +0,0 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package net.java.avatar.js.timers;
import net.java.libuv.Callback;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import net.java.avatar.js.eventloop.DaemonThreadFactory;
import net.java.avatar.js.eventloop.Event;
import net.java.avatar.js.eventloop.EventLoop;
public final class Timers {
public final class TimerHandle {
final int id;
int referenceCount;
TimerHandle(final int id) {
this.id = id;
this.referenceCount = 1;
}
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(final Object other) {
if (other instanceof TimerHandle) {
final TimerHandle timerHandle = (TimerHandle) other;
return id == timerHandle.id;
}
return false;
}
@Override
public String toString() {
return "{name:" + TimerHandle.class.getSimpleName() +
", id:" + Integer.toString(id) +
", ref:" + Integer.toString(referenceCount) + "}";
}
public void ref() {
referenceCount++;
}
public TimerHandle unref() {
referenceCount--;
return this;
}
public boolean isReferenced() {
return referenceCount > 0;
}
}
final class Immediate implements Callback {
private final Callback cb;
private boolean cleared;
Immediate(final Callback cb) {
this.cb = cb;
}
void clear() {
cleared = true;
}
@Override
public void call(String name, Object[] args) throws Exception {
if (!cleared) {
cb.call(name, args);
}
}
}
private final EventLoop eventLoop;
private final AtomicInteger NEXT_ID = new AtomicInteger();
private final Map<TimerHandle, TimersTask> SCHEDULED = new ConcurrentHashMap<>();
private final Map<TimerHandle, Immediate> IMMEDIATE = new ConcurrentHashMap<>();
private final ScheduledExecutorService TIMER =
Executors.newSingleThreadScheduledExecutor(new DaemonThreadFactory("avatar-js.timer"));
public Timers(final EventLoop eventLoop) {
this.eventLoop = eventLoop;
}
public TimerHandle setTimeout(final boolean repeating,
final long delay,
final Callback callback) {
// adjust zero or negative (invalid) delay values to the smallest positive delay
final long positiveDelay = delay > 0 ? delay : 1;
final TimerHandle id = new TimerHandle(NEXT_ID.incrementAndGet());
final TimersTask task = new TimersTask(this, eventLoop, id, positiveDelay, repeating, callback);
SCHEDULED.put(id, task);
return id;
}
public void start(final TimerHandle id) {
final TimersTask task = SCHEDULED.get(id);
if (task != null) {
task.start();
}
}
public void clearTimeout(final TimerHandle timeoutId) {
final TimersTask task = SCHEDULED.remove(timeoutId);
if (task != null) {
task.cancel();
}
}
public void clearAll() {
for (final TimersTask task : SCHEDULED.values()) {
task.cancel();
}
SCHEDULED.clear();
}
public boolean isPending() {
for (final Map.Entry<TimerHandle, TimersTask> entry : SCHEDULED.entrySet()) {
if (entry.getKey().isReferenced()) {
return true;
}
}
return false;
}
public TimerHandle setImmediate(final Callback callback) {
final Immediate immediate = new Immediate(callback);
final TimerHandle id = new TimerHandle(NEXT_ID.incrementAndGet());
IMMEDIATE.put(id, immediate);
eventLoop.post(new Event("timer.immediate." + id, immediate));
return id;
}
public void clearImmediate(final TimerHandle immediateId) {
final Immediate task = IMMEDIATE.remove(immediateId);
if (task != null) {
task.clear();
}
}
ScheduledExecutorService timer() {
return TIMER;
}
}

View File

@ -1,122 +0,0 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package net.java.avatar.js.timers;
import net.java.libuv.Callback;
import java.util.TimerTask;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import net.java.avatar.js.eventloop.Event;
import net.java.avatar.js.eventloop.EventLoop;
final class TimersTask extends TimerTask {
private final Timers timers;
private final EventLoop eventLoop;
private final long delay;
private final boolean repeating;
private final Callback callback;
private final Timers.TimerHandle id;
private final AtomicBoolean done = new AtomicBoolean(false);
private ScheduledFuture<?> future = null;
TimersTask(final Timers timers,
final EventLoop eventLoop,
final Timers.TimerHandle id,
final long delay,
final boolean repeating,
final Callback callback) {
this.timers = timers;
this.eventLoop = eventLoop;
this.delay = delay;
this.repeating = repeating;
this.callback = callback;
this.id = id;
}
void start() {
future = repeating ?
timers.timer().scheduleAtFixedRate(this, delay, delay, TimeUnit.MILLISECONDS) :
timers.timer().schedule(this, delay, TimeUnit.MILLISECONDS);
}
@Override
public void run() {
if (!repeating) {
if (done.getAndSet(true)) {
return;
}
// Need to post before to clear, otherwise EvtLoop can exit
// after the timer has been removed.
postEvent();
timers.clearTimeout(id);
} else {
postEvent();
}
}
private void postEvent() {
final Callback guard = new Callback() {
@Override
public void call(String name, Object[] args) throws Exception {
if (repeating && done.get()) {
//Already cleared.
return;
}
callback.call(name, args);
}
};
eventLoop.post(new Event("timer"+id, guard));
}
@Override
public boolean equals(final Object obj) {
if (obj instanceof TimersTask) {
return id == ((TimersTask) obj).id;
}
return false;
}
@Override
public int hashCode() {
return this.id.id;
}
@Override
public boolean cancel() {
done.set(true);
if (future != null) {
future.cancel(true);
}
return super.cancel();
}
}

View File

@ -563,6 +563,45 @@ exports.openStdin = function() {
return process.stdin;
}
var Check = Packages.net.java.libuv.handles.CheckHandle;
var Idle = Packages.net.java.libuv.handles.IdleHandle;
var checkHandle = new Check(eventloop.loop());
checkHandle.setCheckCallback(checkImmediate);
// this handle should not keep the event loop from terminating
checkHandle.unref();
// During the lifetime of an immediate callback, we want to keep the loop running.
// This is why an Idle is started/stopped when the unrefed Check is started/stopped
// Nodejs core.cc
var idleHandle = new Idle(eventloop.loop());
idleHandle.setIdleCallback(IdleImmediateDummy);
function checkImmediate() {
// This needs to be treated as a callback, all ticked events need to be handled.
if (exports._immediateCallback) {
exports._immediateCallback();
}
}
function IdleImmediateDummy() {
// Just to keep the loop running.
}
Object.defineProperty(exports, '_needImmediateCallback', {
enumerable : true,
set : function(need) {
if (checkHandle) {
if (need) {
checkHandle.start();
idleHandle.start();
} else {
checkHandle.stop();
idleHandle.stop();
}
}
}
});
var signalHandle = new Signals(eventloop.loop());
// this handle should not keep the event loop from terminating
signalHandle.unref();

View File

@ -1,75 +0,0 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// Simple re-export of globals
exports.setTimeout = setTimeout;
exports.setInterval = setInterval;
exports.clearTimeout = clearTimeout;
exports.clearInterval = clearInterval;
exports.setImmediate = setImmediate;
exports.clearImmediate = clearImmediate;
function enroll(item, delay, repeat) {
var that = item;
unenroll(item);
item._timerId = setTimeout(function() {
that._onTimeout();
}, delay);
item._idleTimeout = delay;
item._enrolled = true;
}
exports.enroll = enroll;
function unenroll(item) {
if (item._timerId) {
clearTimeout(item._timerId);
item._enrolled = false;
item._idleTimeout = -1;
}
}
exports.unenroll = unenroll;
function _unrefActive(item) {
var msecs = item._idleTimeout;
if (msecs >= 0 && !item._enrolled) {
clearTimeout(item._timerId);
var that = item;
item._timerId = setTimeout(function() {
that._onTimeout();
}, msecs);
item._idleTimeout = msecs;
}
}
exports._unrefActive = _unrefActive;

View File

@ -158,115 +158,36 @@ var gc = global.gc;
global.Buffer = Buffer;
// timer functions
var timer = __avatar.eventloop.timer();
var _setTimeout = function() {
var repeated = arguments[0];
var callargs = arguments[1];
var len = callargs.length;
if (len < 1) {
throw new Error('timer callback not specified');
}
var after = callargs[1];
if (len < 2) {
after = 1; // default to 1ms if not specified
}
var callback = callargs[0];
if (typeof callback !== 'function') {
throw new Error('timer callback is not a function (' + (typeof callback) + ')');
}
after *= 1; // coalesce to number or NaN
if (!(after >= 1 && after <= java.lang.Integer.MAX_VALUE)) {
after = 1;
}
var callbackargs = [];
for (var i = 2; i < len; i++) {
callbackargs.push(callargs[i]);
}
var id = timer.setTimeout(repeated, after, function(name, args) {
callback.apply(id, callbackargs);
});
// defer starting the timer until the next tick
process.nextTick(function() {
timer.start(id);
});
return id;
global.setTimeout = function() {
var t = NativeModule.require('timers');
return t.setTimeout.apply(this, arguments);
};
Object.defineProperty(global, 'setTimeout', {
enumerable : true,
value : function() {
return _setTimeout(false, arguments);
}
});
global.setInterval = function() {
var t = NativeModule.require('timers');
return t.setInterval.apply(this, arguments);
};
Object.defineProperty(global, 'clearTimeout', {
enumerable : true,
value : function() {
if (arguments.length > 0) {
if (arguments[0]) {
timer.clearTimeout(arguments[0]);
}
} else {
timer.clearAll();
}
}
});
global.clearTimeout = function() {
var t = NativeModule.require('timers');
return t.clearTimeout.apply(this, arguments);
};
Object.defineProperty(global, 'setInterval', {
enumerable : true,
value : function() {
return _setTimeout(true, arguments);
}
});
global.clearInterval = function() {
var t = NativeModule.require('timers');
return t.clearInterval.apply(this, arguments);
};
Object.defineProperty(global, 'clearInterval', {
enumerable : true,
value : function() {
if (arguments.length > 0) {
if (arguments[0]) {
timer.clearTimeout(arguments[0]);
}
} else {
timer.clearAll();
}
}
});
Object.defineProperty(global, 'setImmediate', {
enumerable : true,
value : function() {
var cb = arguments[0]
var callbackargs = [];
for (var i = 1; i < arguments.length; i++) {
callbackargs.push(arguments[i]);
}
var ctx = {};
var wrapper = function() {
cb.apply(ctx, callbackargs);
}
ctx.id = timer.setImmediate(wrapper);
return ctx;
}
});
Object.defineProperty(global, 'clearImmediate', {
enumerable : true,
value : function() {
if (arguments.length > 0) {
if (arguments[0]) {
timer.clearImmediate(arguments[0].id);
}
}
}
});
global.setImmediate = function() {
var t = NativeModule.require('timers');
return t.setImmediate.apply(this, arguments);
};
global.clearImmediate = function() {
var t = NativeModule.require('timers');
return t.clearImmediate.apply(this, arguments);
};
// console require timers that require setTimeout.
var console = NativeModule.require('console');
global.console = console;

View File

@ -0,0 +1,101 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
(function(exports) {
var TimerHandle = Packages.net.java.libuv.handles.TimerHandle;
var loop = __avatar.eventloop.loop();
function Timer() {
Object.defineProperty(this, '_timer', { value: new TimerHandle(loop) });
var that = this;
this._timer.setTimerFiredCallback(function(status) {
if (that.ontimeout) {
that.ontimeout();
}
});
}
Timer.prototype.start = function(timeout, repeat) {
try {
this._timer.start(timeout, repeat);
} catch(err) {
if (!err.errnoString) {
throw err;
}
process._errno = err.errnoString();
throw err;
}
}
Timer.prototype.stop = function() {
try {
this._timer.stop();
} catch(err) {
if (!err.errnoString) {
throw err;
}
process._errno = err.errnoString();
throw err;
}
}
Timer.prototype.again = function() {
try {
this._timer.again();
} catch(err) {
if (!err.errnoString) {
throw err;
}
process._errno = err.errnoString();
throw err;
}
}
Timer.prototype.setRepeat = function(repeat) {
this._timer.setRepeat(repeat);
}
Timer.prototype.getRepeat = function() {
return this._timer.getRepeat();
}
Timer.prototype.ref = function() {
this._timer.ref();
}
Timer.prototype.unref = function() {
this._timer.unref();
}
Timer.prototype.close = function() {
if (!this.closed) {
this.closed = true;
this._timer.close();
}
}
exports.Timer = Timer;
});

View File

@ -1,56 +0,0 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
(function(exports) {
exports.Timer = function() {
return new Timer();
}
function Timer() {
this.timer = new Packages.net.java.avatar.js.timers.Timers(__eventloop_instance);
}
Timer.prototype.start = function(timeout, repeat) {
var that = this;
this.timer.setTimeout(repeat, timeout, function() {
that.ontimeout();
})
}
Timer.prototype.close = function() {
this.timer.clearAll();
this.timer = undefined;
}
exports.setTimeout = setTimeout;
exports.setInterval = setInterval;
exports.clearTimeout = clearTimeout;
exports.clearInterval = clearInterval;
});