improve locking of class initialization

This commit is contained in:
Andreas Gal 2014-07-28 12:42:42 -07:00
Родитель 11394b361b
Коммит eb7b9eec14
3 изменённых файлов: 41 добавлений и 38 удалений

Просмотреть файл

@ -32,7 +32,6 @@ Context.prototype.popFrame = function() {
Context.prototype.pushClassInitFrame = function(classInfo) { Context.prototype.pushClassInitFrame = function(classInfo) {
if (classInfo.initialized) if (classInfo.initialized)
return; return;
// console.log("starting initialization of", classInfo.className, this.thread.toString());
classInfo.thread = this.thread; classInfo.thread = this.thread;
var syntheticMethod = { var syntheticMethod = {
classInfo: { classInfo: {
@ -53,8 +52,12 @@ Context.prototype.pushClassInitFrame = function(classInfo) {
code: [ code: [
0x2a, // aload_0 0x2a, // aload_0
0x59, // dup 0x59, // dup
0x59, // dup
0x59, // dup
0xc2, // monitorenter
0xb7, 0x00, 0x01, // invokespecial <idx=1> 0xb7, 0x00, 0x01, // invokespecial <idx=1>
0xb7, 0x00, 0x07, // invokespecial <idx=7> 0xb7, 0x00, 0x07, // invokespecial <idx=7>
0xc3, // monitorexit
0xb1, // return 0xb1, // return
], ],
exception_table: [], exception_table: [],
@ -185,30 +188,6 @@ Context.prototype.resume = function() {
this.start(this.stopFrame); this.start(this.stopFrame);
} }
Context.prototype.wait = function(obj) {
if (!obj.waiters)
obj.waiters = [];
obj.waiters.push(this);
throw VM.Pause;
}
Context.prototype.notify = function(obj) {
if (obj.waiters && obj.waiters.length) {
if (!obj.ready)
obj.ready = [];
obj.ready.push(obj.waiters.pop());
}
if (!obj.ready || !obj.ready.length)
return;
var ctx = obj.ready.pop();
window.setZeroTimeout(VM.execute.bind(null, ctx));
}
Context.prototype.notifyAll = function(obj) {
while ((obj.waiters && obj.waiters.length) || (obj.ready && obj.ready.length))
this.notify(obj);
}
Context.prototype.monitorEnter = function(obj) { Context.prototype.monitorEnter = function(obj) {
var lock = obj.lock; var lock = obj.lock;
if (!lock) { if (!lock) {
@ -219,7 +198,10 @@ Context.prototype.monitorEnter = function(obj) {
++lock.count; ++lock.count;
return; return;
} }
this.wait(obj); if (!obj.ready)
obj.ready = [];
obj.ready.push(this);
throw VM.Pause;
} }
Context.prototype.monitorExit = function(obj) { Context.prototype.monitorExit = function(obj) {
@ -232,5 +214,29 @@ Context.prototype.monitorExit = function(obj) {
return; return;
} }
obj.lock = null; obj.lock = null;
this.notifyAll(obj); if (obj.ready && obj.ready.length) {
var ctx = obj.ready.pop();
ctx.monitorEnter(obj);
ctx.resume();
}
}
Context.prototype.wait = function(obj, timeout) {
if (!obj.lock || obj.lock.thread !== this.thread || obj.lock.count !== 1)
this.raiseException("java/lang/IllegalMonitorStateException");
this.monitorExit(obj);
if (!obj.waiting)
obj.waiting = [];
obj.waiting.push(obj);
throw VM.Pause;
}
Context.prototype.notify = function(obj, notifyAll) {
if (!obj.lock || obj.lock.thread !== this.thread)
this.raiseException("java/lang/IllegalMonitorStateException");
while (obj.waiting && obj.waiting.length) {
obj.ready.push(obj.waiting.pop());
if (!notifyAll)
break;
}
} }

Просмотреть файл

@ -103,7 +103,6 @@ Native["java/lang/Object.hashCode.()I"] = function(ctx, stack) {
var hashCode = obj.hashCode; var hashCode = obj.hashCode;
while (!hashCode) while (!hashCode)
hashCode = obj.hashCode = util.id(); hashCode = obj.hashCode = util.id();
console.log(hashCode);
stack.push(hashCode); stack.push(hashCode);
} }
@ -113,7 +112,11 @@ Native["java/lang/Object.wait.(J)V"] = function(ctx, stack) {
} }
Native["java/lang/Class.invoke_clinit.()V"] = function(ctx, stack) { Native["java/lang/Class.invoke_clinit.()V"] = function(ctx, stack) {
var classInfo = stack.pop().vmClass; var classObject = stack.pop();
var classInfo = classObject.vmClass;
if (classInfo.initialized || classInfo.pending)
return;
classInfo.pending = true;
var clinit = CLASSES.getMethod(classInfo, "<clinit>", "()V", true); var clinit = CLASSES.getMethod(classInfo, "<clinit>", "()V", true);
if (clinit) if (clinit)
ctx.pushFrame(clinit, 0); ctx.pushFrame(clinit, 0);
@ -125,9 +128,10 @@ Native["java/lang/Class.invoke_clinit.()V"] = function(ctx, stack) {
Native["java/lang/Class.init9.()V"] = function(ctx, stack) { Native["java/lang/Class.init9.()V"] = function(ctx, stack) {
var classObject = stack.pop(); var classObject = stack.pop();
var classInfo = classObject.vmClass; var classInfo = classObject.vmClass;
if (classInfo.initialized)
return;
classInfo.pending = false;
classInfo.initialized = true; classInfo.initialized = true;
classInfo.thread = null;
ctx.notifyAll(classObject);
} }
Native["java/lang/Class.getName.()Ljava/lang/String;"] = function(ctx, stack) { Native["java/lang/Class.getName.()Ljava/lang/String;"] = function(ctx, stack) {

7
vm.js
Просмотреть файл

@ -95,13 +95,6 @@ VM.execute = function(ctx) {
function classInitCheck(classInfo, ip) { function classInitCheck(classInfo, ip) {
if (classInfo.initialized) if (classInfo.initialized)
return; return;
if (classInfo.thread) {
// Nothing to do if class initialization is currently in progress on this thread.
if (classInfo.thread === ctx.thread)
return;
ctx.wait(classInfo.getClassObject());
// not reached
}
frame.ip = ip; frame.ip = ip;
ctx.pushClassInitFrame(classInfo); ctx.pushClassInitFrame(classInfo);
throw VM.Yield; throw VM.Yield;