Bug 1449135 part 1 - Remove shell code/tests for cooperative scheduling. r=luke

This commit is contained in:
Jan de Mooij 2018-03-27 17:59:10 +02:00
Родитель e0c66b0b55
Коммит 1ec1961661
11 изменённых файлов: 10 добавлений и 371 удалений

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

@ -1,20 +0,0 @@
if (helperThreadCount() == 0)
quit();
// The new Debugger here should throw but we don't have a way to verify this
// (exceptions that worker threads throw do not cause the test to fail).
evalInCooperativeThread('cooperativeYield(); var dbg = new Debugger();');
var dbg = new Debugger;
assertEq(dbg.addAllGlobalsAsDebuggees(), undefined);
function assertThrows(f) {
var exception = false;
try { f(); } catch (e) { exception = true; }
assertEq(exception, true);
}
var dbg = new Debugger;
dbg.onNewGlobalObject = function(global) {};
assertThrows(() => evalInCooperativeThread("var x = 3"));
assertThrows(cooperativeYield);

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

@ -1,8 +0,0 @@
if (helperThreadCount() == 0)
quit();
evalInCooperativeThread("var x = 3");
let PromiseCtor = Promise;
let promises = [];
let p = new PromiseCtor(function(res_, rej_) {});
promises.push(p);
let allPromise = getWaitForAllPromise(promises);

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

@ -1,3 +0,0 @@
if (helperThreadCount() === 0)
quit();
evalInCooperativeThread('cooperativeYield(); var dbg = new Debugger();');

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

@ -1,6 +0,0 @@
if (helperThreadCount() === 0)
quit();
evalInCooperativeThread(`
startgc(9469, "shrinking");
offThreadCompileScript("");
`);

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

@ -1,6 +0,0 @@
if (helperThreadCount() === 0)
quit();
evalInCooperativeThread(`
const dbg = new Debugger();
evalInWorker("");
`);

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

@ -1,16 +0,0 @@
if (helperThreadCount() === 0)
quit();
setInterruptCallback(function() { print("MainThread Interrupt!"); cooperativeYield(); return true; });
evalInCooperativeThread('\
setInterruptCallback(function() { print("Worker Interrupt!"); cooperativeYield(); return true; });\
for (var i = 0; i < 10; i++) {\
print("Worker: " + i);\
interruptIf(true);\
}\
print("Worker Done!");\
');
for (var i = 0; i < 10; i++) {
print("MainThread: " + i);
interruptIf(true);
}
print("MainThread Done!");

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

@ -1,7 +0,0 @@
if (helperThreadCount() === 0)
quit();
evalInCooperativeThread("var x = 3");
evalInCooperativeThread("cooperativeYield()");
cooperativeYield();
evalInCooperativeThread("cooperativeYield(); gc();");
gc();

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

@ -1,6 +0,0 @@
if (helperThreadCount() === 0)
quit();
offThreadCompileScript("");
evalInCooperativeThread("");
runOffThreadScript();

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

@ -1,3 +0,0 @@
if (helperThreadCount() === 0)
quit();
evalInCooperativeThread("enableGeckoProfiling()");

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

@ -1,8 +0,0 @@
if (helperThreadCount() === 0)
quit();
evalInCooperativeThread(`
(new Promise(function(resolve, reject) { resolve(); })).then(() => {});
drainJobQueue();
`);

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

@ -1739,9 +1739,6 @@ ConvertTranscodeResultToJSException(JSContext* cx, JS::TranscodeResult rv)
}
}
static bool
CooperativeThreadMayYield(JSContext* cx);
static bool
Evaluate(JSContext* cx, unsigned argc, Value* vp)
{
@ -1827,52 +1824,6 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
}
}
if (!JS_GetProperty(cx, opts, "zoneGroup", &v))
return false;
if (!v.isUndefined()) {
if (global != JS_GetGlobalForObject(cx, &args.callee())) {
JS_ReportErrorASCII(cx, "zoneGroup and global cannot both be specified.");
return false;
}
// Find all eligible globals to execute in.
JS::AutoObjectVector eligibleGlobals(cx);
for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
// Compartments without globals and the self hosting global may
// not be entered.
if (!c->maybeGlobal() || cx->runtime()->isSelfHostingGlobal(c->maybeGlobal()))
continue;
// Globals in zone groups which are not in use by a cooperative
// thread may be entered.
if (!c->zone()->group()->ownerContext().context()) {
if (!eligibleGlobals.append(c->maybeGlobal()))
return false;
}
// Globals in zone groups which use exclusive locking may be
// entered, in which case this thread will yield until the zone
// group is available.
if (c->zone()->group()->useExclusiveLocking() && CooperativeThreadMayYield(cx)) {
if (!eligibleGlobals.append(c->maybeGlobal()))
return false;
}
}
if (eligibleGlobals.empty()) {
JS_ReportErrorASCII(cx, "zoneGroup can only be used if another"
" cooperative thread has called cooperativeYield(true).");
return false;
}
// Pick an eligible global to use based on the value of the zoneGroup property.
int32_t which;
if (!ToInt32(cx, v, &which))
return false;
which = Min<int32_t>(Max(which, 0), eligibleGlobals.length() - 1);
global = eligibleGlobals[which];
}
if (!JS_GetProperty(cx, opts, "catchTermination", &v))
return false;
if (!v.isUndefined())
@ -3540,151 +3491,6 @@ EvalInContext(JSContext* cx, unsigned argc, Value* vp)
return true;
}
struct CooperationState
{
CooperationState()
: lock(mutexid::ShellThreadCooperation)
, idle(false)
, numThreads(0)
, yieldCount(0)
, singleThreaded(false)
{}
Mutex lock;
ConditionVariable cvar;
bool idle;
size_t numThreads;
uint64_t yieldCount;
bool singleThreaded;
};
static CooperationState* cooperationState = nullptr;
static void
CooperativeBeginWait(JSContext* cx)
{
MOZ_ASSERT(cx == TlsContext.get());
JS_YieldCooperativeContext(cx);
}
static void
CooperativeEndWait(JSContext* cx)
{
MOZ_ASSERT(cx == TlsContext.get());
LockGuard<Mutex> lock(cooperationState->lock);
cooperationState->cvar.wait(lock, [&] { return cooperationState->idle; });
JS_ResumeCooperativeContext(cx);
cooperationState->idle = false;
cooperationState->yieldCount++;
cooperationState->cvar.notify_all();
}
static void
CooperativeYield(bool terminating = false)
{
LockGuard<Mutex> lock(cooperationState->lock);
MOZ_ASSERT(!cooperationState->idle);
cooperationState->idle = true;
cooperationState->cvar.notify_all();
// Wait until another thread takes over control before returning, if there
// is another thread to do so.
if (!terminating && cooperationState->numThreads) {
uint64_t count = cooperationState->yieldCount;
cooperationState->cvar.wait(lock, [&] { return cooperationState->yieldCount != count; });
}
}
static bool
CooperativeThreadMayYield(JSContext* cx)
{
if (!cx->runtime()->gc.canChangeActiveContext(cx))
return false;
if (GetShellContext(cx)->isWorker)
return false;
if (cooperationState->singleThreaded)
return false;
// To avoid contention issues between threads, yields are not allowed while
// a thread has access to zone groups other than its original one, i.e. if
// the thread is inside an evaluate() call with a different zone group.
// This is not a limit which the browser has, but is necessary in the
// shell: the shell can have arbitrary interleavings between cooperative
// threads, whereas the browser has more control over which threads are
// running at different times.
for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next()) {
if (group->ownerContext().context() == cx && group != cx->zone()->group())
return false;
}
return true;
}
static void
CooperativeYieldCallback(JSContext* cx)
{
MOZ_ASSERT(CooperativeThreadMayYield(cx));
CooperativeBeginWait(cx);
CooperativeYield();
CooperativeEndWait(cx);
}
static bool
CooperativeYieldThread(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (!CooperativeThreadMayYield(cx)) {
JS_ReportErrorASCII(cx, "Yielding is not currently allowed");
return false;
}
{
Maybe<JS::AutoRelinquishZoneGroups> artzg;
if ((args.length() > 0) && ToBoolean(args[0]))
artzg.emplace(cx);
CooperativeBeginWait(cx);
CooperativeYield();
CooperativeEndWait(cx);
}
args.rval().setUndefined();
return true;
}
static void
CooperativeBeginSingleThreadedExecution(JSContext* cx)
{
MOZ_ASSERT(!cooperationState->singleThreaded);
// Yield until all other threads have exited any zone groups they are in.
while (true) {
bool done = true;
for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next()) {
if (!group->ownedByCurrentThread() && group->ownerContext().context())
done = false;
}
if (done)
break;
CooperativeBeginWait(cx);
CooperativeYield();
CooperativeEndWait(cx);
}
cooperationState->singleThreaded = true;
}
static void
CooperativeEndSingleThreadedExecution(JSContext* cx)
{
if (cooperationState)
cooperationState->singleThreaded = false;
}
static bool
EnsureGeckoProfilingStackInstalled(JSContext* cx, ShellContext* sc)
{
@ -3707,16 +3513,11 @@ EnsureGeckoProfilingStackInstalled(JSContext* cx, ShellContext* sc)
struct WorkerInput
{
JSRuntime* parentRuntime;
JSContext* siblingContext;
char16_t* chars;
size_t length;
WorkerInput(JSRuntime* parentRuntime, char16_t* chars, size_t length)
: parentRuntime(parentRuntime), siblingContext(nullptr), chars(chars), length(length)
{}
WorkerInput(JSContext* siblingContext, char16_t* chars, size_t length)
: parentRuntime(nullptr), siblingContext(siblingContext), chars(chars), length(length)
: parentRuntime(parentRuntime), chars(chars), length(length)
{}
~WorkerInput() {
@ -3731,16 +3532,12 @@ static void
WorkerMain(void* arg)
{
WorkerInput* input = (WorkerInput*) arg;
MOZ_ASSERT(!!input->parentRuntime != !!input->siblingContext);
MOZ_ASSERT(input->parentRuntime);
JSContext* cx = input->parentRuntime
? JS_NewContext(8L * 1024L * 1024L, 2L * 1024L * 1024L, input->parentRuntime)
: JS_NewCooperativeContext(input->siblingContext);
JSContext* cx = JS_NewContext(8L * 1024L * 1024L, 2L * 1024L * 1024L, input->parentRuntime);
if (!cx)
return;
SetCooperativeYieldCallback(cx, CooperativeYieldCallback);
ShellContext* sc = js_new<ShellContext>(cx);
if (!sc)
return;
@ -3749,10 +3546,6 @@ WorkerMain(void* arg)
CancelOffThreadJobsForContext(cx);
JS_DestroyContext(cx);
js_delete(sc);
if (input->siblingContext) {
cooperationState->numThreads--;
CooperativeYield(/* terminating = */ true);
}
js_delete(input);
});
@ -3790,8 +3583,6 @@ WorkerMain(void* arg)
JS::CompartmentOptions compartmentOptions;
SetStandardCompartmentOptions(compartmentOptions);
if (input->siblingContext)
compartmentOptions.creationOptions().setNewZoneInNewZoneGroup();
RootedObject global(cx, NewGlobalObject(cx, compartmentOptions, nullptr));
if (!global)
@ -3831,7 +3622,7 @@ class MOZ_RAII AutoLockWorkerThreads : public LockGuard<Mutex>
};
static bool
EvalInThread(JSContext* cx, unsigned argc, Value* vp, bool cooperative)
EvalInWorker(JSContext* cx, unsigned argc, Value* vp)
{
if (!CanUseExtraThreads()) {
JS_ReportErrorASCII(cx, "Can't create threads with --no-threads");
@ -3851,25 +3642,6 @@ EvalInThread(JSContext* cx, unsigned argc, Value* vp, bool cooperative)
}
#endif
if (cooperative && GetShellContext(cx)->isWorker) {
// Disallowing cooperative multithreading in worker runtimes allows
// yield state to be process wide, and some other simplifications.
// When we have a better idea of how cooperative multithreading will be
// used in the browser this restriction might be relaxed.
JS_ReportErrorASCII(cx, "Cooperative multithreading in worker runtimes is not supported");
return false;
}
if (cooperative && !cx->runtime()->gc.canChangeActiveContext(cx)) {
JS_ReportErrorASCII(cx, "Cooperating multithreading context switches are not currently allowed");
return false;
}
if (cooperative && cooperationState->singleThreaded) {
JS_ReportErrorASCII(cx, "Creating cooperative threads is not allowed while single threaded");
return false;
}
if (!args[0].toString()->ensureLinear(cx))
return false;
@ -3891,20 +3663,12 @@ EvalInThread(JSContext* cx, unsigned argc, Value* vp, bool cooperative)
CopyChars(chars, *str);
WorkerInput* input =
cooperative
? js_new<WorkerInput>(cx, chars, str->length())
: js_new<WorkerInput>(JS_GetParentRuntime(cx), chars, str->length());
WorkerInput* input = js_new<WorkerInput>(JS_GetParentRuntime(cx), chars, str->length());
if (!input) {
ReportOutOfMemory(cx);
return false;
}
if (cooperative) {
cooperationState->numThreads++;
CooperativeBeginWait(cx);
}
Thread* thread;
{
AutoEnterOOMUnsafeRegion oomUnsafe;
@ -3913,33 +3677,17 @@ EvalInThread(JSContext* cx, unsigned argc, Value* vp, bool cooperative)
oomUnsafe.crash("EvalInThread");
}
if (cooperative) {
CooperativeEndWait(cx);
} else {
AutoLockWorkerThreads alwt;
if (!workerThreads.append(thread)) {
ReportOutOfMemory(cx);
thread->join();
return false;
}
}
args.rval().setUndefined();
return true;
}
static bool
EvalInWorker(JSContext* cx, unsigned argc, Value* vp)
{
return EvalInThread(cx, argc, vp, false);
}
static bool
EvalInCooperativeThread(JSContext* cx, unsigned argc, Value* vp)
{
return EvalInThread(cx, argc, vp, true);
}
static bool
ShapeOf(JSContext* cx, unsigned argc, JS::Value* vp)
{
@ -4122,13 +3870,6 @@ KillWorkerThreads(JSContext* cx)
{
MOZ_ASSERT_IF(!CanUseExtraThreads(), workerThreads.empty());
// Yield until all other cooperative threads in the main runtime finish.
while (cooperationState->numThreads) {
CooperativeBeginWait(cx);
CooperativeYield();
CooperativeEndWait(cx);
}
if (!workerThreadsLock) {
MOZ_ASSERT(workerThreads.empty());
return;
@ -4152,9 +3893,6 @@ KillWorkerThreads(JSContext* cx)
js_delete(workerThreadsLock);
workerThreadsLock = nullptr;
js_delete(cooperationState);
cooperationState = nullptr;
}
static void
@ -6972,16 +6710,6 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
"evalInWorker(str)",
" Evaluate 'str' in a separate thread with its own runtime.\n"),
JS_FN_HELP("evalInCooperativeThread", EvalInCooperativeThread, 1, 0,
"evalInCooperativeThread(str)",
" Evaluate 'str' in a separate cooperatively scheduled thread using the same runtime.\n"),
JS_FN_HELP("cooperativeYield", CooperativeYieldThread, 1, 0,
"cooperativeYield(leaveZoneGroup)",
" Yield execution to another cooperatively scheduled thread using the same runtime.\n"
" If leaveZoneGroup is specified then other threads may execute code in the\n"
" current thread's zone group via evaluate(..., {zoneGroup:N}).\n"),
JS_FN_HELP("getSharedArrayBuffer", GetSharedArrayBuffer, 0, 0,
"getSharedArrayBuffer()",
" Retrieve the SharedArrayBuffer object from the cross-worker mailbox.\n"
@ -9419,12 +9147,6 @@ main(int argc, char** argv, char** envp)
js::SetPreserveWrapperCallback(cx, DummyPreserveWrapperCallback);
cooperationState = js_new<CooperationState>();
JS::SetSingleThreadedExecutionCallbacks(cx,
CooperativeBeginSingleThreadedExecution,
CooperativeEndSingleThreadedExecution);
SetCooperativeYieldCallback(cx, CooperativeYieldCallback);
result = Shell(cx, &op, envp);
#ifdef DEBUG