Merge branch 'with-exclusive-lock'
Conflicts: js-assembler/runtime-src/runtime.js version.rkt web-world/main.rkt
This commit is contained in:
commit
976b6112f1
|
@ -128,66 +128,77 @@
|
|||
|
||||
var coerseClosureToJavaScript = function (v, MACHINE) {
|
||||
var f = function (succ, fail) {
|
||||
succ = succ || function () {};
|
||||
fail = fail || function () {};
|
||||
|
||||
if (!(baselib.arity.isArityMatching(v.racketArity, arguments.length - 2))) {
|
||||
var msg = baselib.format.format(
|
||||
"arity mismatch: ~s expected ~s argument(s) but received ~s",
|
||||
[v.displayName, v.racketArity, arguments.length - 2]);
|
||||
return fail(new baselib.exceptions.RacketError(
|
||||
msg,
|
||||
baselib.exceptions.makeExnFailContractArity(msg,
|
||||
MACHINE.captureContinuationMarks())));
|
||||
}
|
||||
|
||||
var oldVal = MACHINE.v;
|
||||
var oldArgcount = MACHINE.a;
|
||||
var oldProc = MACHINE.p;
|
||||
|
||||
var oldErrorHandler = MACHINE.params['currentErrorHandler'];
|
||||
var afterGoodInvoke = function (MACHINE) {
|
||||
plt.runtime.PAUSE(
|
||||
function (restart) {
|
||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||
var returnValue = MACHINE.v;
|
||||
MACHINE.v = oldVal;
|
||||
MACHINE.a = oldArgcount;
|
||||
MACHINE.p = oldProc;
|
||||
succ(returnValue);
|
||||
});
|
||||
};
|
||||
afterGoodInvoke.mvr = function (MACHINE) {
|
||||
plt.runtime.PAUSE(
|
||||
function (restart) {
|
||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||
var returnValues = [MACHINE.v], i;
|
||||
for (i = 0; i < MACHINE.a - 1; i++) {
|
||||
returnValues.push(MACHINE.e.pop());
|
||||
}
|
||||
MACHINE.v = oldVal;
|
||||
MACHINE.a = oldArgcount;
|
||||
MACHINE.p = oldProc;
|
||||
succ.apply(null, returnValues);
|
||||
});
|
||||
};
|
||||
|
||||
MACHINE.c.push(
|
||||
new baselib.frames.CallFrame(afterGoodInvoke, v));
|
||||
MACHINE.a = arguments.length - 2;
|
||||
var args = [];
|
||||
var i;
|
||||
for (i = 0; i < arguments.length - 2; i++) {
|
||||
MACHINE.e.push(arguments[arguments.length - 1 - i]);
|
||||
for (i = 0; i < arguments.length; i++) {
|
||||
args.push(arguments[i]);
|
||||
}
|
||||
MACHINE.p = v;
|
||||
MACHINE.params['currentErrorHandler'] = function (MACHINE, e) {
|
||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||
MACHINE.v = oldVal;
|
||||
MACHINE.a = oldArgcount;
|
||||
MACHINE.p = oldProc;
|
||||
fail(e);
|
||||
};
|
||||
MACHINE.trampoline(v.label);
|
||||
|
||||
MACHINE.exclusiveLock.acquire(
|
||||
"js-as-closure",
|
||||
function(releaseLock) {
|
||||
succ = succ || function () {};
|
||||
fail = fail || function () {};
|
||||
if (!(baselib.arity.isArityMatching(v.racketArity, args.length - 2))) {
|
||||
var msg = baselib.format.format(
|
||||
"arity mismatch: ~s expected ~s argument(s) but received ~s",
|
||||
[v.displayName, v.racketArity, args.length - 2]);
|
||||
releaseLock();
|
||||
return fail(new baselib.exceptions.RacketError(
|
||||
msg,
|
||||
baselib.exceptions.makeExnFailContractArity(msg,
|
||||
MACHINE.captureContinuationMarks())));
|
||||
}
|
||||
|
||||
var oldVal = MACHINE.v;
|
||||
var oldArgcount = MACHINE.a;
|
||||
var oldProc = MACHINE.p;
|
||||
var oldErrorHandler = MACHINE.params['currentErrorHandler'];
|
||||
|
||||
var afterGoodInvoke = function (MACHINE) {
|
||||
plt.runtime.PAUSE(
|
||||
function (restart) {
|
||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||
var returnValue = MACHINE.v;
|
||||
MACHINE.v = oldVal;
|
||||
MACHINE.a = oldArgcount;
|
||||
MACHINE.p = oldProc;
|
||||
succ(returnValue);
|
||||
});
|
||||
};
|
||||
afterGoodInvoke.mvr = function (MACHINE) {
|
||||
plt.runtime.PAUSE(
|
||||
function (restart) {
|
||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||
var returnValues = [MACHINE.v], i;
|
||||
for (i = 0; i < MACHINE.a - 1; i++) {
|
||||
returnValues.push(MACHINE.e.pop());
|
||||
}
|
||||
MACHINE.v = oldVal;
|
||||
MACHINE.a = oldArgcount;
|
||||
MACHINE.p = oldProc;
|
||||
succ.apply(null, returnValues);
|
||||
});
|
||||
};
|
||||
|
||||
MACHINE.c.push(
|
||||
new baselib.frames.CallFrame(afterGoodInvoke, v));
|
||||
MACHINE.a = args.length - 2;
|
||||
var i;
|
||||
for (i = 0; i < args.length - 2; i++) {
|
||||
MACHINE.e.push(args[args.length - 1 - i]);
|
||||
}
|
||||
MACHINE.p = v;
|
||||
MACHINE.params['currentErrorHandler'] = function (MACHINE, e) {
|
||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||
MACHINE.v = oldVal;
|
||||
MACHINE.a = oldArgcount;
|
||||
MACHINE.p = oldProc;
|
||||
fail(e);
|
||||
};
|
||||
|
||||
MACHINE._trampoline(v.label, false, releaseLock);
|
||||
});
|
||||
};
|
||||
return f;
|
||||
};
|
||||
|
@ -214,72 +225,89 @@
|
|||
|
||||
|
||||
// internallCallDuringPause: call a Racket procedure and get its results.
|
||||
// The use assumes the machine is in a running-but-paused state.
|
||||
// The use assumes the machine is in a running-but-paused state, where the
|
||||
// lock is still in effect. The lock will continue to be in effect
|
||||
// after coming back from the internal call.
|
||||
var internalCallDuringPause = function (MACHINE, proc, success, fail) {
|
||||
var args = [];
|
||||
var i;
|
||||
var oldArgcount, oldVal, oldProc, oldErrorHandler;
|
||||
if (! baselib.arity.isArityMatching(proc.racketArity, arguments.length - 4)) {
|
||||
var msg = baselib.format.format("arity mismatch: ~s expected ~s arguments, but received ~s",
|
||||
[proc.displayName, proc.racketArity, arguments.length - 4]);
|
||||
return fail(baselib.exceptions.makeExnFailContractArity(msg,
|
||||
MACHINE.captureContinuationMarks()));
|
||||
for (i = 0; i < arguments.length; i++) {
|
||||
args.push(arguments[i]);
|
||||
}
|
||||
|
||||
if (isClosure(proc)) {
|
||||
oldVal = MACHINE.v;
|
||||
oldArgcount = MACHINE.a;
|
||||
oldProc = MACHINE.p;
|
||||
var i;
|
||||
var oldArgcount, oldVal, oldProc, oldErrorHandler, oldControlLength, oldEnvLength;
|
||||
if (! baselib.arity.isArityMatching(proc.racketArity, args.length - 4)) {
|
||||
var msg = baselib.format.format("arity mismatch: ~s expected ~s arguments, but received ~s",
|
||||
[proc.displayName, proc.racketArity, args.length - 4]);
|
||||
fail(baselib.exceptions.makeExnFailContractArity(msg,
|
||||
MACHINE.captureContinuationMarks()));
|
||||
}
|
||||
|
||||
oldErrorHandler = MACHINE.params['currentErrorHandler'];
|
||||
var afterGoodInvoke = function (MACHINE) {
|
||||
plt.runtime.PAUSE(function (restart) {
|
||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||
var returnValue = MACHINE.v;
|
||||
MACHINE.v = oldVal;
|
||||
MACHINE.a = oldArgcount;
|
||||
MACHINE.p = oldProc;
|
||||
success(returnValue);
|
||||
});
|
||||
};
|
||||
afterGoodInvoke.mvr = function (MACHINE) {
|
||||
plt.runtime.PAUSE(function (restart) {
|
||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||
var returnValues = [MACHINE.v];
|
||||
var i;
|
||||
for (i = 0; i < MACHINE.a - 1; i++) {
|
||||
returnValues.push(MACHINE.e.pop());
|
||||
}
|
||||
MACHINE.v = oldVal;
|
||||
MACHINE.a = oldArgcount;
|
||||
MACHINE.p = oldProc;
|
||||
success.apply(null, returnValues);
|
||||
});
|
||||
};
|
||||
|
||||
MACHINE.c.push(
|
||||
new baselib.frames.CallFrame(afterGoodInvoke, proc));
|
||||
MACHINE.a = arguments.length - 4;
|
||||
for (i = 0; i < arguments.length - 4; i++) {
|
||||
MACHINE.e.push(arguments[arguments.length - 1 - i]);
|
||||
}
|
||||
MACHINE.p = proc;
|
||||
MACHINE.params['currentErrorHandler'] = function (MACHINE, e) {
|
||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||
MACHINE.v = oldVal;
|
||||
MACHINE.a = oldArgcount;
|
||||
MACHINE.p = oldProc;
|
||||
fail(e);
|
||||
};
|
||||
MACHINE.trampoline(proc.label);
|
||||
} else {
|
||||
if (! isClosure(proc)) {
|
||||
fail(baselib.exceptions.makeExnFail(
|
||||
baselib.format.format(
|
||||
"Not a procedure: ~e",
|
||||
proc),
|
||||
MACHINE.captureContinuationMarks()));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
oldVal = MACHINE.v;
|
||||
oldArgcount = MACHINE.a;
|
||||
oldProc = MACHINE.p;
|
||||
oldControlLength = MACHINE.c.length;
|
||||
oldEnvLength = MACHINE.e.length;
|
||||
|
||||
oldErrorHandler = MACHINE.params['currentErrorHandler'];
|
||||
var afterGoodInvoke = function (MACHINE) {
|
||||
plt.runtime.PAUSE(function (restart) {
|
||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||
var returnValue = MACHINE.v;
|
||||
MACHINE.v = oldVal;
|
||||
MACHINE.a = oldArgcount;
|
||||
MACHINE.p = oldProc;
|
||||
success(returnValue);
|
||||
});
|
||||
};
|
||||
afterGoodInvoke.mvr = function (MACHINE) {
|
||||
plt.runtime.PAUSE(function (restart) {
|
||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||
var returnValues = [MACHINE.v];
|
||||
var i;
|
||||
for (i = 0; i < MACHINE.a - 1; i++) {
|
||||
returnValues.push(MACHINE.e.pop());
|
||||
}
|
||||
MACHINE.v = oldVal;
|
||||
MACHINE.a = oldArgcount;
|
||||
MACHINE.p = oldProc;
|
||||
success.apply(null, returnValues);
|
||||
});
|
||||
};
|
||||
|
||||
MACHINE.c.push(
|
||||
new baselib.frames.CallFrame(afterGoodInvoke, proc));
|
||||
MACHINE.a = args.length - 4;
|
||||
for (i = 0; i < args.length - 4; i++) {
|
||||
MACHINE.e.push(args[args.length - 1 - i]);
|
||||
}
|
||||
MACHINE.p = proc;
|
||||
MACHINE.params['currentErrorHandler'] = function (MACHINE, e) {
|
||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||
MACHINE.v = oldVal;
|
||||
MACHINE.a = oldArgcount;
|
||||
MACHINE.p = oldProc;
|
||||
MACHINE.c.length = oldControlLength;
|
||||
MACHINE.e.length = oldEnvLength;
|
||||
fail(e);
|
||||
};
|
||||
MACHINE._trampoline(proc.label,
|
||||
false,
|
||||
function() {
|
||||
// The lock should still being held, so we don't
|
||||
// automatically unlock control.
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -62,19 +62,28 @@
|
|||
if (this.isInvoked) {
|
||||
succ();
|
||||
} else {
|
||||
MACHINE.params['currentErrorHandler'] = function (MACHINE, anError) {
|
||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||
fail(MACHINE, anError);
|
||||
};
|
||||
MACHINE.c.push(new plt.baselib.frames.CallFrame(afterGoodInvoke, null));
|
||||
if (isInternal) {
|
||||
MACHINE.params['currentErrorHandler'] = function (MACHINE, anError) {
|
||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||
fail(MACHINE, anError);
|
||||
};
|
||||
MACHINE.c.push(new plt.baselib.frames.CallFrame(afterGoodInvoke, null));
|
||||
throw that.label;
|
||||
} else {
|
||||
MACHINE.trampoline(that.label);
|
||||
MACHINE.exclusiveLock.acquire(
|
||||
undefined,
|
||||
function(release) {
|
||||
MACHINE.params['currentErrorHandler'] = function (MACHINE, anError) {
|
||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||
fail(MACHINE, anError);
|
||||
};
|
||||
MACHINE.c.push(new plt.baselib.frames.CallFrame(afterGoodInvoke, null));
|
||||
MACHINE._trampoline(that.label, false, release);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
exports.ModuleRecord = ModuleRecord;
|
||||
|
|
|
@ -114,7 +114,7 @@
|
|||
var ArityAtLeast = baselib.arity.ArityAtLeast;
|
||||
var makeArityAtLeast = baselib.arity.makeArityAtLeast;
|
||||
var isArityMatching = baselib.arity.isArityMatching;
|
||||
|
||||
|
||||
|
||||
var testArgument = baselib.check.testArgument;
|
||||
var testArity = baselib.check.testArity;
|
||||
|
@ -138,17 +138,17 @@
|
|||
|
||||
|
||||
var defaultCurrentPrintImplementation = function (MACHINE) {
|
||||
if(--MACHINE.cbt < 0) {
|
||||
throw defaultCurrentPrintImplementation;
|
||||
if(--MACHINE.cbt < 0) {
|
||||
throw defaultCurrentPrintImplementation;
|
||||
}
|
||||
var oldArgcount = MACHINE.a;
|
||||
|
||||
var elt = MACHINE.e[MACHINE.e.length - 1];
|
||||
var outputPort =
|
||||
var outputPort =
|
||||
MACHINE.params.currentOutputPort;
|
||||
if (elt !== VOID) {
|
||||
outputPort.writeDomNode(
|
||||
MACHINE,
|
||||
MACHINE,
|
||||
toDomNode(elt, MACHINE.params['print-mode']));
|
||||
outputPort.writeDomNode(MACHINE, toDomNode("\n", 'display'));
|
||||
}
|
||||
|
@ -161,6 +161,76 @@
|
|||
defaultCurrentPrintImplementation);
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Exclusive Locks. Even though JavaScript is a single-threaded
|
||||
// evaluator, we still have a need to create exclusive regions
|
||||
// of evaluation, since we might inadvertantly access some state
|
||||
// with two computations, with use of setTimeout.
|
||||
var ExclusiveLock = function() {
|
||||
this.locked = false; // (U false string)
|
||||
this.waiters = [];
|
||||
};
|
||||
|
||||
// makeRandomNonce: -> string
|
||||
// Creates a randomly-generated nonce.
|
||||
ExclusiveLock.makeRandomNonce = function() {
|
||||
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
|
||||
var LEN = 32;
|
||||
var result = [];
|
||||
var i;
|
||||
for (i = 0; i < LEN; i++) {
|
||||
result.push(chars.charAt(Math.floor(Math.random() * chars.length)));
|
||||
}
|
||||
return result.join('');
|
||||
};
|
||||
|
||||
ExclusiveLock.prototype.acquire = function(id, onAcquire) {
|
||||
var that = this;
|
||||
if (!id) {
|
||||
id = ExclusiveLock.makeRandomNonce();
|
||||
}
|
||||
|
||||
var alreadyReleased = false;
|
||||
|
||||
if (this.locked === false) {
|
||||
this.locked = id;
|
||||
onAcquire.call(
|
||||
that,
|
||||
// releaseLock
|
||||
function() {
|
||||
var waiter;
|
||||
if (alreadyReleased) {
|
||||
throw new Error(
|
||||
"Internal error: trying to release the lock, but already released");
|
||||
}
|
||||
if (that.locked === false) {
|
||||
throw new Error(
|
||||
"Internal error: trying to unlock the lock, but already unlocked");
|
||||
}
|
||||
that.locked = false;
|
||||
alreadyReleased = true;
|
||||
if (that.waiters.length > 0) {
|
||||
waiter = that.waiters.shift();
|
||||
setTimeout(
|
||||
function() {
|
||||
that.acquire(waiter.id, waiter.onAcquire);
|
||||
},
|
||||
0);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.waiters.push({ id: id,
|
||||
onAcquire: onAcquire } );
|
||||
}
|
||||
};
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////]
|
||||
// The MACHINE
|
||||
|
||||
|
@ -188,7 +258,7 @@
|
|||
'currentDisplayer': function(MACHINE, domNode) {
|
||||
$(domNode).appendTo(document.body);
|
||||
},
|
||||
|
||||
|
||||
// currentErrorDisplayer: DomNode -> Void
|
||||
// currentErrorDisplayer is responsible for displaying errors to the browser.
|
||||
'currentErrorDisplayer': function(MACHINE, domNode) {
|
||||
|
@ -196,7 +266,7 @@
|
|||
},
|
||||
|
||||
'currentInspector': baselib.inspectors.DEFAULT_INSPECTOR,
|
||||
|
||||
|
||||
'currentOutputPort': new StandardOutputPort(),
|
||||
'currentErrorPort': new StandardErrorPort(),
|
||||
'currentInputPort': new StandardInputPort(),
|
||||
|
@ -206,9 +276,9 @@
|
|||
MACHINE,
|
||||
toDomNode(exn, MACHINE.params['print-mode']));
|
||||
},
|
||||
|
||||
|
||||
'currentNamespace': {},
|
||||
|
||||
|
||||
// These parameters control how often
|
||||
// control yields back to the browser
|
||||
// for response. The implementation is a
|
||||
|
@ -229,8 +299,9 @@
|
|||
|
||||
};
|
||||
this.primitives = Primitives;
|
||||
this.exclusiveLock = new ExclusiveLock();
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Try to get the continuation mark key used for procedure application tracing.
|
||||
var getTracedAppKey = function(MACHINE) {
|
||||
|
@ -260,7 +331,7 @@
|
|||
return MACHINE.c.slice(i + 1,
|
||||
MACHINE.c.length - skip);
|
||||
}
|
||||
}
|
||||
}
|
||||
raise(MACHINE, new Error("captureControl: unable to find tag " + tag));
|
||||
};
|
||||
|
||||
|
@ -268,20 +339,20 @@
|
|||
|
||||
// restoreControl clears the control stack (up to, but not including the
|
||||
// prompt tagged by tag), and then appends the rest of the control frames.
|
||||
// At the moment, the rest of the control frames is assumed to be in the
|
||||
// At the moment, the rest of the control frames is assumed to be in the
|
||||
// top of the environment.
|
||||
Machine.prototype.restoreControl = function(tag) {
|
||||
var MACHINE = this;
|
||||
var i;
|
||||
for (i = MACHINE.c.length - 1; i >= 0; i--) {
|
||||
if (MACHINE.c[i].tag === tag) {
|
||||
MACHINE.c =
|
||||
MACHINE.c =
|
||||
MACHINE.c.slice(0, i+1).concat(
|
||||
MACHINE.e[MACHINE.e.length - 1]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
raise(MACHINE, new Error("restoreControl: unable to find tag " + tag));
|
||||
raise(MACHINE, new Error("restoreControl: unable to find tag " + tag));
|
||||
|
||||
};
|
||||
|
||||
|
@ -309,11 +380,11 @@
|
|||
var lst = NULL;
|
||||
var i;
|
||||
for (i = 0; i < length; i++) {
|
||||
lst = makePair(MACHINE.e[MACHINE.e.length - depth - length + i],
|
||||
lst = makePair(MACHINE.e[MACHINE.e.length - depth - length + i],
|
||||
lst);
|
||||
}
|
||||
MACHINE.e.splice(MACHINE.e.length - depth - length,
|
||||
length,
|
||||
length,
|
||||
lst);
|
||||
MACHINE.a = MACHINE.a - length + 1;
|
||||
};
|
||||
|
@ -347,8 +418,8 @@
|
|||
if (control[i].marks.length !== 0) {
|
||||
kvLists.push(control[i].marks);
|
||||
}
|
||||
|
||||
if (tracedCalleeKey !== null &&
|
||||
|
||||
if (tracedCalleeKey !== null &&
|
||||
control[i] instanceof CallFrame &&
|
||||
control[i].p !== null) {
|
||||
kvLists.push([[tracedCalleeKey, control[i].p]]);
|
||||
|
@ -356,7 +427,7 @@
|
|||
}
|
||||
return new baselib.contmarks.ContinuationMarkSet(kvLists);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -376,26 +447,27 @@
|
|||
//
|
||||
var recomputeMaxNumBouncesBeforeYield;
|
||||
|
||||
var scheduleTrampoline = function(MACHINE, f) {
|
||||
// FIXME: at the moment, the setTimeout is breaking when we get
|
||||
// a rapid set of events from web-world. We are running into
|
||||
// a very ugly re-entrancy bug. https://github.com/dyoo/whalesong/issues/70
|
||||
|
||||
// setTimeout(
|
||||
// function() {
|
||||
return MACHINE.trampoline(f);
|
||||
// },
|
||||
// 0);
|
||||
var scheduleTrampoline = function(MACHINE, f, release) {
|
||||
setTimeout(
|
||||
function() {
|
||||
MACHINE._trampoline(f, false, release);
|
||||
},
|
||||
0);
|
||||
};
|
||||
|
||||
// Creates a restarting function, that reschedules f in a context
|
||||
// with the old argcount in place.
|
||||
// with the old argcount in place.
|
||||
// Meant to be used only by the trampoline.
|
||||
var makeRestartFunction = function(MACHINE) {
|
||||
var makeRestartFunction = function(MACHINE, release, pauseLock) {
|
||||
var oldArgcount = MACHINE.a;
|
||||
return function(f) {
|
||||
MACHINE.a = oldArgcount;
|
||||
return scheduleTrampoline(MACHINE, f);
|
||||
return function(f) {
|
||||
pauseLock.acquire(
|
||||
undefined,
|
||||
function(pauseReleaseLock) {
|
||||
MACHINE.a = oldArgcount;
|
||||
MACHINE._trampoline(f, false, release);
|
||||
pauseReleaseLock();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -419,18 +491,35 @@
|
|||
};
|
||||
|
||||
|
||||
// WARNING WARNING WARNING
|
||||
//
|
||||
// Make sure to get an exclusive lock before jumping into trampoline.
|
||||
// Otherwise, Bad Things will happen.
|
||||
//
|
||||
// e.g. machine.lock.acquire('id', function(release) { machine.trampoline... release();});
|
||||
Machine.prototype.trampoline = function(initialJump, noJumpingOff) {
|
||||
var thunk = initialJump;
|
||||
var startTime = (new Date()).valueOf();
|
||||
this.cbt = STACK_LIMIT_ESTIMATE;
|
||||
this.params.numBouncesBeforeYield =
|
||||
this.params.maxNumBouncesBeforeYield;
|
||||
this.running = true;
|
||||
var that = this;
|
||||
|
||||
while(true) {
|
||||
that.exclusiveLock.acquire(
|
||||
'trampoline',
|
||||
function(release) {
|
||||
that._trampoline(initialJump, noJumpingOff, release);
|
||||
});
|
||||
};
|
||||
|
||||
Machine.prototype._trampoline = function(initialJump, noJumpingOff, release) {
|
||||
var that = this;
|
||||
var thunk = initialJump;
|
||||
var startTime = (new Date()).valueOf();
|
||||
that.cbt = STACK_LIMIT_ESTIMATE;
|
||||
that.params.numBouncesBeforeYield =
|
||||
that.params.maxNumBouncesBeforeYield;
|
||||
that.running = true;
|
||||
|
||||
while(true) {
|
||||
try {
|
||||
thunk(this);
|
||||
break;
|
||||
thunk(that);
|
||||
break;
|
||||
} catch (e) {
|
||||
// There are a few kinds of things that can get thrown
|
||||
// during racket evaluation:
|
||||
|
@ -452,9 +541,9 @@
|
|||
// Everything else: otherwise, we send the exception value
|
||||
// to the current error handler and exit.
|
||||
// The running flag is set to false.
|
||||
if (typeof(e) === 'function') {
|
||||
if (typeof(e) === 'function') {
|
||||
thunk = e;
|
||||
this.cbt = STACK_LIMIT_ESTIMATE;
|
||||
that.cbt = STACK_LIMIT_ESTIMATE;
|
||||
|
||||
|
||||
// If we're running an a model that prohibits
|
||||
|
@ -463,48 +552,88 @@
|
|||
continue;
|
||||
}
|
||||
|
||||
if (this.params.numBouncesBeforeYield-- < 0) {
|
||||
recomputeMaxNumBouncesBeforeYield(
|
||||
this,
|
||||
(new Date()).valueOf() - startTime);
|
||||
scheduleTrampoline(this, thunk);
|
||||
return;
|
||||
}
|
||||
} else if (e instanceof Pause) {
|
||||
var restart = makeRestartFunction(this);
|
||||
e.onPause(restart);
|
||||
if (that.params.numBouncesBeforeYield-- < 0) {
|
||||
recomputeMaxNumBouncesBeforeYield(
|
||||
that,
|
||||
(new Date()).valueOf() - startTime);
|
||||
scheduleTrampoline(that, thunk, release);
|
||||
return;
|
||||
}
|
||||
} else if (e instanceof Pause) {
|
||||
var pauseLock = new ExclusiveLock();
|
||||
var oldArgcount = that.a;
|
||||
var restarted = false;
|
||||
var restart = function(f) {
|
||||
pauseLock.acquire(
|
||||
undefined,
|
||||
function(releasePauseLock) {
|
||||
restarted = true;
|
||||
that.a = oldArgcount;
|
||||
that._trampoline(f, false, release);
|
||||
releasePauseLock();
|
||||
});
|
||||
};
|
||||
var internalCall = function(proc, success, fail) {
|
||||
var i;
|
||||
if (restarted) {
|
||||
return;
|
||||
}
|
||||
var args = [];
|
||||
for (i = 3; i < arguments.length; i++) {
|
||||
args.push(arguments[i]);
|
||||
}
|
||||
pauseLock.acquire(
|
||||
undefined,
|
||||
function(release) {
|
||||
var newSuccess = function() {
|
||||
success.apply(null, arguments);
|
||||
release();
|
||||
};
|
||||
var newFail = function() {
|
||||
fail.apply(null, arguments);
|
||||
release();
|
||||
};
|
||||
baselib.functions.internalCallDuringPause.apply(
|
||||
null, [that, proc, newSuccess, newFail].concat(args));
|
||||
});
|
||||
};
|
||||
e.onPause(restart, internalCall);
|
||||
return;
|
||||
} else if (e instanceof HaltError) {
|
||||
this.running = false;
|
||||
e.onHalt(this);
|
||||
that.running = false;
|
||||
e.onHalt(that);
|
||||
release();
|
||||
return;
|
||||
} else {
|
||||
// General error condition: just exit out
|
||||
// of the trampoline and call the current error handler.
|
||||
this.running = false;
|
||||
this.params.currentErrorHandler(this, e);
|
||||
return;
|
||||
}
|
||||
// General error condition: just exit out
|
||||
// of the trampoline and call the current error handler.
|
||||
that.running = false;
|
||||
that.params.currentErrorHandler(that, e);
|
||||
release();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.running = false;
|
||||
var that = this;
|
||||
this.params.currentSuccessHandler(this);
|
||||
return;
|
||||
}
|
||||
that.running = false;
|
||||
that.params.currentSuccessHandler(that);
|
||||
release();
|
||||
return;
|
||||
|
||||
};
|
||||
|
||||
|
||||
// recomputeGas: state number -> number
|
||||
recomputeMaxNumBouncesBeforeYield = function(MACHINE, observedDelay) {
|
||||
// We'd like to see a delay of DESIRED_DELAY_BETWEEN_BOUNCES so
|
||||
// that we get MACHINE.params.desiredYieldsPerSecond bounces per
|
||||
// second.
|
||||
var DESIRED_DELAY_BETWEEN_BOUNCES =
|
||||
var DESIRED_DELAY_BETWEEN_BOUNCES =
|
||||
(1000 / MACHINE.params.desiredYieldsPerSecond);
|
||||
var ALPHA = 50;
|
||||
var delta = (ALPHA * ((DESIRED_DELAY_BETWEEN_BOUNCES -
|
||||
observedDelay) /
|
||||
observedDelay) /
|
||||
DESIRED_DELAY_BETWEEN_BOUNCES));
|
||||
MACHINE.params.maxNumBouncesBeforeYield =
|
||||
MACHINE.params.maxNumBouncesBeforeYield =
|
||||
Math.max(MACHINE.params.maxNumBouncesBeforeYield + delta,
|
||||
1);
|
||||
};
|
||||
|
@ -538,7 +667,7 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -684,7 +813,7 @@
|
|||
exports['installPrimitiveProcedure'] = installPrimitiveProcedure;
|
||||
exports['makePrimitiveProcedure'] = makePrimitiveProcedure;
|
||||
exports['Primitives'] = Primitives;
|
||||
|
||||
|
||||
exports['ready'] = ready;
|
||||
// Private: the runtime library will set this flag to true when
|
||||
// the library has finished loading.
|
||||
|
@ -699,7 +828,7 @@
|
|||
exports['ModuleRecord'] = ModuleRecord;
|
||||
exports['VariableReference'] = VariableReference;
|
||||
exports['ContinuationPromptTag'] = ContinuationPromptTag;
|
||||
exports['DEFAULT_CONTINUATION_PROMPT_TAG'] =
|
||||
exports['DEFAULT_CONTINUATION_PROMPT_TAG'] =
|
||||
DEFAULT_CONTINUATION_PROMPT_TAG;
|
||||
exports['NULL'] = NULL;
|
||||
exports['VOID'] = VOID;
|
||||
|
|
|
@ -114,58 +114,74 @@ Prerequisites: at least @link["http://racket-lang.org/"]{Racket
|
|||
Here are a collection of programs that use the @emph{web-world} library described
|
||||
later in this document:
|
||||
@itemize[
|
||||
@item{@link["http://hashcollision.org/whalesong/examples/attr-animation/attr-animation.html"]{attr-animation.html} [@link["http://hashcollision.org/whalesong/examples/attr-animation/attr-animation.rkt"]{src}] Uses @racket[update-view-attr] and @racket[on-tick] to perform a simple color animation.}
|
||||
@item{@link["http://hashcollision.org/whalesong/examples/attr-animation/attr-animation.html"]{attr-animation.html}
|
||||
[@link["http://hashcollision.org/whalesong/examples/attr-animation/attr-animation.rkt"]{src}
|
||||
@link["http://hashcollision.org/whalesong/examples/attr-animation/index.html"]{index.html}
|
||||
@link["http://hashcollision.org/whalesong/examples/attr-animation/style.css"]{style.css}]
|
||||
Uses @racket[update-view-attr] and @racket[on-tick] to perform a simple color animation.}
|
||||
|
||||
|
||||
@item{@link["http://hashcollision.org/whalesong/examples/boid/boid.html"]{boid.html} [@link["http://hashcollision.org/whalesong/examples/boid/boid.rkt"]{src}] Uses @racket[update-view-css] and @racket[on-tick] to perform an animation of a flock of @link["http://en.wikipedia.org/wiki/Boids"]{boids}.}
|
||||
@item{@link["http://hashcollision.org/whalesong/examples/boid/boid.html"]{boid.html}
|
||||
[@link["http://hashcollision.org/whalesong/examples/boid/boid.rkt"]{src}
|
||||
@link["http://hashcollision.org/whalesong/examples/boid/index.html"]{index.html}] Uses @racket[update-view-css] and @racket[on-tick] to perform an animation of a flock of @link["http://en.wikipedia.org/wiki/Boids"]{boids}.}
|
||||
|
||||
|
||||
@item{@link["http://hashcollision.org/whalesong/examples/dwarves/dwarves.html"]{dwarves.html}
|
||||
[@link["http://hashcollision.org/whalesong/examples/dwarves/dwarves.rkt"]{src}]
|
||||
[@link["http://hashcollision.org/whalesong/examples/dwarves/dwarves.rkt"]{src}
|
||||
@link["http://hashcollision.org/whalesong/examples/dwarves/index.html"]{index.html}]
|
||||
Uses @racket[view-show] and @racket[view-hide] to manipulate a view. Click on a dwarf to make them hide.
|
||||
}
|
||||
|
||||
@item{@link["http://hashcollision.org/whalesong/examples/dwarves-with-remove/dwarves-with-remove.html"]{dwarves-with-remove.html}
|
||||
[@link["http://hashcollision.org/whalesong/examples/dwarves-with-remove/dwarves-with-remove.rkt"]{src}]
|
||||
[@link["http://hashcollision.org/whalesong/examples/dwarves-with-remove/dwarves-with-remove.rkt"]{src}
|
||||
@link["http://hashcollision.org/whalesong/examples/dwarves-with-remove/index.html"]{index.html}]
|
||||
Uses @racket[view-focus?] and @racket[view-remove] to see if a dwarf should be removed from the view.
|
||||
}
|
||||
|
||||
@item{@link["http://hashcollision.org/whalesong/examples/field/field.html"]{field.html}
|
||||
[@link["http://hashcollision.org/whalesong/examples/field/field.rkt"]{src}]
|
||||
[@link["http://hashcollision.org/whalesong/examples/field/field.rkt"]{src}
|
||||
@link["http://hashcollision.org/whalesong/examples/field/index.html"]{index.html}]
|
||||
Uses @racket[view-bind] to read a text field, and @racket[update-view-text] to change
|
||||
the text content of an element.
|
||||
}
|
||||
|
||||
@item{@link["http://hashcollision.org/whalesong/examples/phases/phases.html"]{phases.html}
|
||||
[@link["http://hashcollision.org/whalesong/examples/phases/phases.rkt"]{src}]
|
||||
[@link["http://hashcollision.org/whalesong/examples/phases/phases.rkt"]{src}
|
||||
@link["http://hashcollision.org/whalesong/examples/phases/index1.html"]{index1.html}
|
||||
@link["http://hashcollision.org/whalesong/examples/phases/index2.html"]{index2.html}]
|
||||
Switches out one view entirely in place of another. Different views can correspond to phases in a program.
|
||||
}
|
||||
|
||||
|
||||
@item{@link["http://hashcollision.org/whalesong/examples/tick-tock/tick-tock.html"]{tick-tock.html}
|
||||
[@link["http://hashcollision.org/whalesong/examples/tick-tock/tick-tock.rkt"]{src}]
|
||||
[@link["http://hashcollision.org/whalesong/examples/tick-tock/tick-tock.rkt"]{src}
|
||||
@link["http://hashcollision.org/whalesong/examples/tick-tock/index.html"]{index.html}]
|
||||
Uses @racket[on-tick] to show a timer counting up.
|
||||
}
|
||||
|
||||
@item{@link["http://hashcollision.org/whalesong/examples/redirected/redirected.html"]{redirected.html}
|
||||
[@link["http://hashcollision.org/whalesong/examples/redirected/redirected.rkt"]{src}]
|
||||
[@link["http://hashcollision.org/whalesong/examples/redirected/redirected.rkt"]{src}
|
||||
@link["http://hashcollision.org/whalesong/examples/redirected/index.html"]{index.html}]
|
||||
Uses @racket[on-tick] to show a timer counting up, and also uses @racket[open-output-element] to
|
||||
pipe side-effecting @racket[printf]s to a hidden @tt{div}.
|
||||
}
|
||||
|
||||
@item{@link["http://hashcollision.org/whalesong/examples/todo/todo.html"]{todo.html}
|
||||
[@link["http://hashcollision.org/whalesong/examples/todo/todo.rkt"]{src}]
|
||||
[@link["http://hashcollision.org/whalesong/examples/todo/todo.rkt"]{src}
|
||||
@link["http://hashcollision.org/whalesong/examples/todo/index.html"]{index.html}]
|
||||
A simple TODO list manager.
|
||||
}
|
||||
|
||||
@item{@link["http://hashcollision.org/whalesong/examples/where-am-i/where-am-i.html"]{where-am-i.html}
|
||||
[@link["http://hashcollision.org/whalesong/examples/where-am-i/where-am-i.rkt"]{src}]
|
||||
[@link["http://hashcollision.org/whalesong/examples/where-am-i/where-am-i.rkt"]{src}
|
||||
@link["http://hashcollision.org/whalesong/examples/where-am-i/index.html"]{index.html}]
|
||||
Uses @racket[on-location-change] and @racket[on-mock-location-change] to demonstrate location services.
|
||||
}
|
||||
|
||||
|
||||
@item{@link["http://hashcollision.org/whalesong/examples/hot-cross-buns/hot-cross-buns.html"]{hot-cross-buns.html}
|
||||
[@link["http://hashcollision.org/whalesong/examples/hot-cross-buns/hot-cross-buns.rkt"]{src}]
|
||||
[@link["http://hashcollision.org/whalesong/examples/hot-cross-buns/hot-cross-buns.rkt"]{src}
|
||||
@link["http://hashcollision.org/whalesong/examples/hot-cross-buns/index.html"]{index.html}]
|
||||
Demonstrates use of checkboxes. Uses @racket[view-has-attr?] to see if a checkbox has been
|
||||
checked, and @racket[remove-view-attr] to change the @emph{checked} attribute when the user
|
||||
wants to reset the page.
|
||||
|
|
|
@ -7,4 +7,4 @@
|
|||
(provide version)
|
||||
(: version String)
|
||||
|
||||
(define version "1.101")
|
||||
(define version "1.102")
|
||||
|
|
|
@ -1171,7 +1171,7 @@
|
|||
MACHINE.params.currentOutputPort = find(handlers, isWithOutputToHandler).outputPort;
|
||||
}
|
||||
|
||||
PAUSE(function(restart) {
|
||||
PAUSE(function(restart, internalCall) {
|
||||
var onCleanRestart, onMessyRestart,
|
||||
startEventHandlers, stopEventHandlers,
|
||||
startEventHandler, stopEventHandler,
|
||||
|
@ -1260,7 +1260,7 @@
|
|||
var onGoodWorldUpdate =
|
||||
function(newWorld) {
|
||||
world = newWorld;
|
||||
stopWhen(MACHINE,
|
||||
stopWhen(internalCall,
|
||||
world,
|
||||
mockView,
|
||||
function(shouldStop) {
|
||||
|
@ -1274,14 +1274,14 @@
|
|||
fail);
|
||||
};
|
||||
if (plt.baselib.arity.isArityMatching(racketWorldCallback.racketArity, 3)) {
|
||||
racketWorldCallback(MACHINE,
|
||||
racketWorldCallback(internalCall,
|
||||
world,
|
||||
mockView,
|
||||
data,
|
||||
onGoodWorldUpdate,
|
||||
fail);
|
||||
} else {
|
||||
racketWorldCallback(MACHINE,
|
||||
racketWorldCallback(internalCall,
|
||||
world,
|
||||
mockView,
|
||||
onGoodWorldUpdate,
|
||||
|
@ -1299,7 +1299,7 @@
|
|||
// update, and have to do it from scratch.
|
||||
var nonce = Math.random();
|
||||
var originalMockView = view.getMockAndResetFocus(nonce);
|
||||
toDraw(MACHINE,
|
||||
toDraw(internalCall,
|
||||
world,
|
||||
originalMockView,
|
||||
function(newMockView) {
|
||||
|
@ -1334,15 +1334,14 @@
|
|||
};
|
||||
|
||||
var wrapFunction = function(proc) {
|
||||
var f = function(MACHINE) {
|
||||
var f = function(internalCall) {
|
||||
var success = arguments[arguments.length - 2];
|
||||
var fail = arguments[arguments.length - 1];
|
||||
var args = [].slice.call(arguments, 1, arguments.length - 2);
|
||||
return plt.baselib.functions.internalCallDuringPause.apply(null,
|
||||
[MACHINE,
|
||||
proc,
|
||||
success,
|
||||
fail].concat(args));
|
||||
return internalCall.apply(null,
|
||||
[proc,
|
||||
success,
|
||||
fail].concat(args));
|
||||
};
|
||||
f.racketArity = proc.racketArity;
|
||||
return f;
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
(require "impl.rkt"
|
||||
"helpers.rkt"
|
||||
"event.rkt")
|
||||
"event.rkt"
|
||||
(for-syntax racket/base))
|
||||
|
||||
(require (for-syntax racket/base racket/stxparam-exptime)
|
||||
(only-in "../lang/kernel.rkt" define-syntax-parameter syntax-parameterize))
|
||||
|
@ -18,6 +19,7 @@
|
|||
(all-from-out "helpers.rkt")
|
||||
(all-from-out "event.rkt"))
|
||||
|
||||
(provide view-bind*)
|
||||
|
||||
(provide (rename-out [internal-big-bang big-bang]
|
||||
[big-bang big-bang/f]
|
||||
|
@ -76,4 +78,21 @@
|
|||
on-mock-location-change
|
||||
on-location-change
|
||||
to-draw))
|
||||
|
||||
|
||||
|
||||
|
||||
;; A syntactic form to make it more convenient to focus and bind multiple things
|
||||
;; (view-bind* a-view
|
||||
;; [id type function]
|
||||
;; [id type function] ...)
|
||||
(define-syntax (view-bind* stx)
|
||||
(syntax-case stx ()
|
||||
[(_ a-view [a-selector a-type a-function] ...)
|
||||
(foldl (lambda (a-selector a-type a-function a-view-stx)
|
||||
#'(view-bind (view-focus #,a-view-stx #,a-selector)
|
||||
#,a-type
|
||||
#,a-function))
|
||||
#'a-view
|
||||
(syntax->list #'(a-selector ...))
|
||||
(syntax->list #'(a-type ...))
|
||||
(syntax->list #'(a-function ...)))]))
|
||||
|
|
Loading…
Reference in New Issue
Block a user