diff -ruN src/shell/Makefile.in src.new/shell/Makefile.in --- shell/Makefile.in 2011-04-01 06:08:36.000000000 +1100 +++ shell/Makefile.in 2013-03-23 22:01:26.876752286 +1100 @@ -47,7 +47,6 @@ PROGRAM = js$(BIN_SUFFIX) CPPSRCS = \ js.cpp \ - jsworkers.cpp \ $(NULL) DEFINES += -DEXPORT_JS_API diff -ruN src/shell/js.cpp src.new/shell/js.cpp --- shell/js.cpp 2011-04-01 06:08:36.000000000 +1100 +++ shell/js.cpp 2013-03-23 22:02:46.436700725 +1100 @@ -91,8 +91,6 @@ #endif /* JSDEBUGGER_C_UI */ #endif /* JSDEBUGGER */ -#include "jsworkers.h" - #include "jsinterpinlines.h" #include "jsobjinlines.h" #include "jsscriptinlines.h" @@ -194,10 +192,6 @@ JSBool gQuitting = JS_FALSE; FILE *gErrFile = NULL; FILE *gOutFile = NULL; -#ifdef JS_THREADSAFE -JSObject *gWorkers = NULL; -js::workers::ThreadPool *gWorkerThreadPool = NULL; -#endif static JSBool reportWarnings = JS_TRUE; static JSBool compileOnly = JS_FALSE; @@ -1315,10 +1309,6 @@ JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "/ i", &gExitCode); gQuitting = JS_TRUE; -#ifdef JS_THREADSAFE - if (gWorkerThreadPool) - js::workers::terminateAll(JS_GetRuntime(cx), gWorkerThreadPool); -#endif return JS_FALSE; } @@ -4150,10 +4140,6 @@ gCanceled = true; if (gExitCode == 0) gExitCode = EXITCODE_TIMEOUT; -#ifdef JS_THREADSAFE - if (gWorkerThreadPool) - js::workers::terminateAll(rt, gWorkerThreadPool); -#endif JS_TriggerAllOperationCallbacks(rt); static const char msg[] = "Script runs for too long, terminating.\n"; @@ -5681,29 +5667,8 @@ #endif /* JSDEBUGGER_C_UI */ #endif /* JSDEBUGGER */ -#ifdef JS_THREADSAFE - class ShellWorkerHooks : public js::workers::WorkerHooks { - public: - JSObject *newGlobalObject(JSContext *cx) { - return NewGlobalObject(cx, NEW_COMPARTMENT); - } - }; - ShellWorkerHooks hooks; - if (!JS_AddNamedObjectRoot(cx, &gWorkers, "Workers") || - (gWorkerThreadPool = js::workers::init(cx, &hooks, glob, &gWorkers)) == NULL) { - return 1; - } -#endif - int result = ProcessArgs(cx, glob, argv, argc); -#ifdef JS_THREADSAFE - js::workers::finish(cx, gWorkerThreadPool); - JS_RemoveObjectRoot(cx, &gWorkers); - if (result == 0) - result = gExitCode; -#endif - #ifdef JSDEBUGGER if (jsdc) { #ifdef JSDEBUGGER_C_UI diff -ruN src/shell/jsworkers.cpp src.new/shell/jsworkers.cpp --- shell/jsworkers.cpp 2011-04-01 06:08:36.000000000 +1100 +++ shell/jsworkers.cpp 1970-01-01 10:00:00.000000000 +1000 @@ -1,1280 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is JavaScript shell workers. - * - * The Initial Developer of the Original Code is - * Mozilla Corporation. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Jason Orendorff - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifdef JS_THREADSAFE - -#include -#include -#include "prthread.h" -#include "prlock.h" -#include "prcvar.h" -#include "jsapi.h" -#include "jscntxt.h" -#include "jshashtable.h" -#include "jsstdint.h" -#include "jslock.h" -#include "jsvector.h" -#include "jsworkers.h" - -extern size_t gMaxStackSize; - -/* - * JavaScript shell workers. - * - * == Object lifetime rules == - * - * - The ThreadPool lasts from init() to finish(). - * - * - The ThreadPool owns the MainQueue and the WorkerQueue. Those live from - * the time the first Worker is created until finish(). - * - * - Each JS Worker object has the same lifetime as the corresponding C++ - * Worker object. A Worker is live if (a) the Worker JSObject is still - * live; (b) the Worker has an incoming event pending or running; (c) it - * has sent an outgoing event to its parent that is still pending; or (d) - * it has any live child Workers. - * - * - finish() continues to wait for events until all threads are idle. - * - * Event objects, however, are basically C++-only. The JS Event objects are - * just plain old JSObjects. They don't keep anything alive. - * - * == Locking scheme == - * - * When mixing mutexes and the JSAPI request model, there are two choices: - * - * - Always nest the mutexes in requests. Since threads in requests are not - * supposed to block, this means the mutexes must be only briefly held. - * - * - Never nest the mutexes in requests. Since this allows threads to race - * with the GC, trace() methods must go through the mutexes just like - * everyone else. - * - * This code uses the latter approach for all locks. - * - * In one case, a thread holding a Worker's mutex can acquire the mutex of one - * of its child Workers. See Worker::terminateSelf. (This can't deadlock because - * the parent-child relationship is a partial order.) - */ - -namespace js { -namespace workers { - -template -class Queue { - private: - typedef Vector Vec; - Vec v1; - Vec v2; - Vec *front; - Vec *back; - - // Queue is not copyable. - Queue(const Queue &); - Queue & operator=(const Queue &); - - public: - Queue() : front(&v1), back(&v2) {} - bool push(T t) { return back->append(t); } - bool empty() { return front->empty() && back->empty(); } - - T pop() { - if (front->empty()) { - std::reverse(back->begin(), back->end()); - Vec *tmp = front; - front = back; - back = tmp; - } - T item = front->back(); - front->popBack(); - return item; - } - - void clear() { - v1.clear(); - v2.clear(); - } - - void trace(JSTracer *trc) { - for (T *p = v1.begin(); p != v1.end(); p++) - (*p)->trace(trc); - for (T *p = v2.begin(); p != v2.end(); p++) - (*p)->trace(trc); - } -}; - -class Event; -class ThreadPool; -class Worker; - -class WorkerParent { - protected: - typedef HashSet, SystemAllocPolicy> ChildSet; - ChildSet children; - - bool initWorkerParent() { return children.init(8); } - - public: - virtual JSLock *getLock() = 0; - virtual ThreadPool *getThreadPool() = 0; - virtual bool post(Event *item) = 0; // false on OOM or queue closed - virtual void trace(JSTracer *trc) = 0; - - bool addChild(Worker *w) { - AutoLock hold(getLock()); - return children.put(w) != NULL; - } - - // This must be called only from GC or when all threads are shut down. It - // does not bother with locking. - void removeChild(Worker *w) { - ChildSet::Ptr p = children.lookup(w); - JS_ASSERT(p); - children.remove(p); - } - - void disposeChildren(); -}; - -template -class ThreadSafeQueue -{ - protected: - Queue queue; - JSLock *lock; - PRCondVar *condvar; - bool closed; - - private: - Vector busy; - - protected: - ThreadSafeQueue() : lock(NULL), condvar(NULL), closed(false) {} - - ~ThreadSafeQueue() { - if (condvar) - JS_DESTROY_CONDVAR(condvar); - if (lock) - JS_DESTROY_LOCK(lock); - } - - // Called by take() with the lock held. - virtual bool shouldStop() { return closed; } - - public: - bool initThreadSafeQueue() { - JS_ASSERT(!lock); - JS_ASSERT(!condvar); - return (lock = JS_NEW_LOCK()) && (condvar = JS_NEW_CONDVAR(lock)); - } - - bool post(T t) { - AutoLock hold(lock); - if (closed) - return false; - if (queue.empty()) - JS_NOTIFY_ALL_CONDVAR(condvar); - return queue.push(t); - } - - void close() { - AutoLock hold(lock); - closed = true; - queue.clear(); - JS_NOTIFY_ALL_CONDVAR(condvar); - } - - // The caller must hold the lock. - bool take(T *t) { - while (queue.empty()) { - if (shouldStop()) - return false; - JS_WAIT_CONDVAR(condvar, JS_NO_TIMEOUT); - } - *t = queue.pop(); - busy.append(*t); - return true; - } - - // The caller must hold the lock. - void drop(T item) { - for (T *p = busy.begin(); p != busy.end(); p++) { - if (*p == item) { - *p = busy.back(); - busy.popBack(); - return; - } - } - JS_NOT_REACHED("removeBusy"); - } - - bool lockedIsIdle() { return busy.empty() && queue.empty(); } - - bool isIdle() { - AutoLock hold(lock); - return lockedIsIdle(); - } - - void wake() { - AutoLock hold(lock); - JS_NOTIFY_ALL_CONDVAR(condvar); - } - - void trace(JSTracer *trc) { - AutoLock hold(lock); - for (T *p = busy.begin(); p != busy.end(); p++) - (*p)->trace(trc); - queue.trace(trc); - } -}; - -class MainQueue; - -class Event -{ - protected: - virtual ~Event() { JS_ASSERT(!data); } - - WorkerParent *recipient; - Worker *child; - uint64 *data; - size_t nbytes; - - public: - enum Result { fail = JS_FALSE, ok = JS_TRUE, forwardToParent }; - - virtual void destroy(JSContext *cx) { - JS_free(cx, data); -#ifdef DEBUG - data = NULL; -#endif - delete this; - } - - void setChildAndRecipient(Worker *aChild, WorkerParent *aRecipient) { - child = aChild; - recipient = aRecipient; - } - - bool deserializeData(JSContext *cx, jsval *vp) { - return !!JS_ReadStructuredClone(cx, data, nbytes, JS_STRUCTURED_CLONE_VERSION, vp, - NULL, NULL); - } - - virtual Result process(JSContext *cx) = 0; - - inline void trace(JSTracer *trc); - - template - static EventType *createEvent(JSContext *cx, WorkerParent *recipient, Worker *child, - jsval v) - { - uint64 *data; - size_t nbytes; - if (!JS_WriteStructuredClone(cx, v, &data, &nbytes, NULL, NULL)) - return NULL; - - EventType *event = new EventType; - if (!event) { - JS_ReportOutOfMemory(cx); - return NULL; - } - event->recipient = recipient; - event->child = child; - event->data = data; - event->nbytes = nbytes; - return event; - } - - Result dispatch(JSContext *cx, JSObject *thisobj, const char *dataPropName, - const char *methodName, Result noHandler) - { - if (!data) - return fail; - - JSBool found; - if (!JS_HasProperty(cx, thisobj, methodName, &found)) - return fail; - if (!found) - return noHandler; - - // Create event object. - jsval v; - if (!deserializeData(cx, &v)) - return fail; - JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL); - if (!obj || !JS_DefineProperty(cx, obj, dataPropName, v, NULL, NULL, 0)) - return fail; - - // Call event handler. - jsval argv[1] = { OBJECT_TO_JSVAL(obj) }; - jsval rval = JSVAL_VOID; - return Result(JS_CallFunctionName(cx, thisobj, methodName, 1, argv, &rval)); - } -}; - -typedef ThreadSafeQueue EventQueue; - -class MainQueue : public EventQueue, public WorkerParent -{ - private: - ThreadPool *threadPool; - - public: - explicit MainQueue(ThreadPool *tp) : threadPool(tp) {} - - ~MainQueue() { - JS_ASSERT(queue.empty()); - } - - bool init() { return initThreadSafeQueue() && initWorkerParent(); } - - void destroy(JSContext *cx) { - while (!queue.empty()) - queue.pop()->destroy(cx); - delete this; - } - - virtual JSLock *getLock() { return lock; } - virtual ThreadPool *getThreadPool() { return threadPool; } - - protected: - virtual bool shouldStop(); - - public: - virtual bool post(Event *event) { return EventQueue::post(event); } - - virtual void trace(JSTracer *trc); - - void traceChildren(JSTracer *trc) { EventQueue::trace(trc); } - - JSBool mainThreadWork(JSContext *cx, bool continueOnError) { - JSAutoSuspendRequest suspend(cx); - AutoLock hold(lock); - - Event *event; - while (take(&event)) { - JS_RELEASE_LOCK(lock); - Event::Result result; - { - JSAutoRequest req(cx); - result = event->process(cx); - if (result == Event::forwardToParent) { - // FIXME - pointlessly truncates the string to 8 bits - jsval data; - JSAutoByteString bytes; - if (event->deserializeData(cx, &data) && - JSVAL_IS_STRING(data) && - bytes.encode(cx, JSVAL_TO_STRING(data))) { - JS_ReportError(cx, "%s", bytes.ptr()); - } else { - JS_ReportOutOfMemory(cx); - } - result = Event::fail; - } - if (result == Event::fail && continueOnError) { - if (JS_IsExceptionPending(cx) && !JS_ReportPendingException(cx)) - JS_ClearPendingException(cx); - result = Event::ok; - } - } - JS_ACQUIRE_LOCK(lock); - drop(event); - event->destroy(cx); - if (result != Event::ok) - return false; - } - return true; - } -}; - -/* - * A queue of workers. - * - * We keep a queue of workers with pending events, rather than a queue of - * events, so that two threads won't try to run a Worker at the same time. - */ -class WorkerQueue : public ThreadSafeQueue -{ - private: - MainQueue *main; - - public: - explicit WorkerQueue(MainQueue *main) : main(main) {} - - void work(); -}; - -/* The top-level object that owns everything else. */ -class ThreadPool -{ - private: - enum { threadCount = 6 }; - - JSObject *obj; - WorkerHooks *hooks; - MainQueue *mq; - WorkerQueue *wq; - PRThread *threads[threadCount]; - int32_t terminating; - - static JSClass jsClass; - - static void start(void* arg) { - ((WorkerQueue *) arg)->work(); - } - - explicit ThreadPool(WorkerHooks *hooks) : hooks(hooks), mq(NULL), wq(NULL), terminating(0) { - for (int i = 0; i < threadCount; i++) - threads[i] = NULL; - } - - public: - ~ThreadPool() { - JS_ASSERT(!mq); - JS_ASSERT(!wq); - JS_ASSERT(!threads[0]); - } - - static ThreadPool *create(JSContext *cx, WorkerHooks *hooks) { - ThreadPool *tp = new ThreadPool(hooks); - if (!tp) { - JS_ReportOutOfMemory(cx); - return NULL; - } - - JSObject *obj = JS_NewObject(cx, &jsClass, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, tp)) { - delete tp; - return NULL; - } - tp->obj = obj; - return tp; - } - - JSObject *asObject() { return obj; } - WorkerHooks *getHooks() { return hooks; } - WorkerQueue *getWorkerQueue() { return wq; } - MainQueue *getMainQueue() { return mq; } - bool isTerminating() { return terminating != 0; } - - /* - * Main thread only. Requires request (to prevent GC, which could see the - * object in an inconsistent state). - */ - bool start(JSContext *cx) { - JS_ASSERT(!mq && !wq); - mq = new MainQueue(this); - if (!mq || !mq->init()) { - mq->destroy(cx); - mq = NULL; - return false; - } - wq = new WorkerQueue(mq); - if (!wq || !wq->initThreadSafeQueue()) { - delete wq; - wq = NULL; - mq->destroy(cx); - mq = NULL; - return false; - } - JSAutoSuspendRequest suspend(cx); - bool ok = true; - for (int i = 0; i < threadCount; i++) { - threads[i] = PR_CreateThread(PR_USER_THREAD, start, wq, PR_PRIORITY_NORMAL, - PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); - if (!threads[i]) { - shutdown(cx); - ok = false; - break; - } - } - return ok; - } - - void terminateAll(JSRuntime *rt) { - // See comment about JS_ATOMIC_SET in the implementation of - // JS_TriggerOperationCallback. - JS_ATOMIC_SET(&terminating, 1); - JS_TriggerAllOperationCallbacks(rt); - } - - /* This context is used only to free memory. */ - void shutdown(JSContext *cx) { - wq->close(); - for (int i = 0; i < threadCount; i++) { - if (threads[i]) { - PR_JoinThread(threads[i]); - threads[i] = NULL; - } - } - - delete wq; - wq = NULL; - - mq->disposeChildren(); - mq->destroy(cx); - mq = NULL; - terminating = 0; - } - - private: - static void jsTraceThreadPool(JSTracer *trc, JSObject *obj) { - ThreadPool *tp = unwrap(trc->context, obj); - if (tp->mq) { - tp->mq->traceChildren(trc); - tp->wq->trace(trc); - } - } - - - static void jsFinalize(JSContext *cx, JSObject *obj) { - if (ThreadPool *tp = unwrap(cx, obj)) - delete tp; - } - - public: - static ThreadPool *unwrap(JSContext *cx, JSObject *obj) { - JS_ASSERT(JS_GET_CLASS(cx, obj) == &jsClass); - return (ThreadPool *) JS_GetPrivate(cx, obj); - } -}; - -/* - * A Worker is always in one of 4 states, except when it is being initialized - * or destroyed, or its lock is held: - * - idle (!terminated && current == NULL && events.empty()) - * - enqueued (!terminated && current == NULL && !events.empty()) - * - busy (!terminated && current != NULL) - * - terminated (terminated && current == NULL && events.empty()) - * - * Separately, there is a terminateFlag that other threads can set - * asynchronously to tell the Worker to terminate. - */ -class Worker : public WorkerParent -{ - private: - ThreadPool *threadPool; - WorkerParent *parent; - JSObject *object; // Worker object exposed to parent - JSContext *context; - JSLock *lock; - Queue events; // owning pointers to pending events - Event *current; - bool terminated; - int32_t terminateFlag; - - static JSClass jsWorkerClass; - - Worker() - : threadPool(NULL), parent(NULL), object(NULL), - context(NULL), lock(NULL), current(NULL), terminated(false), terminateFlag(0) {} - - bool init(JSContext *parentcx, WorkerParent *parent, JSObject *obj) { - JS_ASSERT(!threadPool && !this->parent && !object && !lock); - - if (!initWorkerParent() || !parent->addChild(this)) - return false; - threadPool = parent->getThreadPool(); - this->parent = parent; - this->object = obj; - lock = JS_NEW_LOCK(); - return lock && - createContext(parentcx, parent) && - JS_SetPrivate(parentcx, obj, this); - } - - bool createContext(JSContext *parentcx, WorkerParent *parent) { - JSRuntime *rt = JS_GetRuntime(parentcx); - context = JS_NewContext(rt, 8192); - if (!context) - return false; - - // The Worker has a strong reference to the global; see jsTraceWorker. - // JSOPTION_UNROOTED_GLOBAL ensures that when the worker becomes - // unreachable, it and its global object can be collected. Otherwise - // the cx->globalObject root would keep them both alive forever. - JS_SetOptions(context, JS_GetOptions(parentcx) | JSOPTION_UNROOTED_GLOBAL | - JSOPTION_DONT_REPORT_UNCAUGHT); - JS_SetVersion(context, JS_GetVersion(parentcx)); - JS_SetContextPrivate(context, this); - JS_SetOperationCallback(context, jsOperationCallback); - JS_BeginRequest(context); - - JSObject *global = threadPool->getHooks()->newGlobalObject(context); - JSObject *post, *proto, *ctor; - if (!global) - goto bad; - JS_SetGlobalObject(context, global); - - // Because the Worker is completely isolated from the rest of the - // runtime, and because any pending events on a Worker keep the Worker - // alive, this postMessage function cannot be called after the Worker - // is collected. Therefore it's safe to stash a pointer (a weak - // reference) to the C++ Worker object in the reserved slot. - post = JS_GetFunctionObject(JS_DefineFunction(context, global, "postMessage", - (JSNative) jsPostMessageToParent, 1, 0)); - if (!post || !JS_SetReservedSlot(context, post, 0, PRIVATE_TO_JSVAL(this))) - goto bad; - - proto = JS_InitClass(context, global, NULL, &jsWorkerClass, jsConstruct, 1, - NULL, jsMethods, NULL, NULL); - if (!proto) - goto bad; - - ctor = JS_GetConstructor(context, proto); - if (!ctor || !JS_SetReservedSlot(context, ctor, 0, PRIVATE_TO_JSVAL(this))) - goto bad; - - JS_EndRequest(context); - JS_ClearContextThread(context); - return true; - - bad: - JS_EndRequest(context); - JS_DestroyContext(context); - context = NULL; - return false; - } - - static void jsTraceWorker(JSTracer *trc, JSObject *obj) { - JS_ASSERT(JS_GET_CLASS(trc->context, obj) == &jsWorkerClass); - if (Worker *w = (Worker *) JS_GetPrivate(trc->context, obj)) { - w->parent->trace(trc); - w->events.trace(trc); - if (w->current) - w->current->trace(trc); - JS_CALL_OBJECT_TRACER(trc, JS_GetGlobalObject(w->context), "Worker global"); - } - } - - static void jsFinalize(JSContext *cx, JSObject *obj) { - JS_ASSERT(JS_GET_CLASS(cx, obj) == &jsWorkerClass); - if (Worker *w = (Worker *) JS_GetPrivate(cx, obj)) - delete w; - } - - static JSBool jsOperationCallback(JSContext *cx) { - Worker *w = (Worker *) JS_GetContextPrivate(cx); - JSAutoSuspendRequest suspend(cx); // avoid nesting w->lock in a request - return !w->checkTermination(); - } - - static JSBool jsResolveGlobal(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp) - { - JSBool resolved; - - if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) - return false; - if (resolved) - *objp = obj; - - return true; - } - - static JSBool jsPostMessageToParent(JSContext *cx, uintN argc, jsval *vp); - static JSBool jsPostMessageToChild(JSContext *cx, uintN argc, jsval *vp); - static JSBool jsTerminate(JSContext *cx, uintN argc, jsval *vp); - - bool checkTermination() { - AutoLock hold(lock); - return lockedCheckTermination(); - } - - bool lockedCheckTermination() { - if (terminateFlag || threadPool->isTerminating()) { - terminateSelf(); - terminateFlag = 0; - } - return terminated; - } - - // Caller must hold the lock. - void terminateSelf() { - terminated = true; - while (!events.empty()) - events.pop()->destroy(context); - - // Tell the children to shut down too. An arbitrarily silly amount of - // processing could happen before the whole tree is terminated; but - // this way we don't have to worry about blowing the C stack. - for (ChildSet::Enum e(children); !e.empty(); e.popFront()) - e.front()->setTerminateFlag(); // note: nesting locks here - } - - public: - ~Worker() { - if (parent) - parent->removeChild(this); - dispose(); - } - - void dispose() { - JS_ASSERT(!current); - while (!events.empty()) - events.pop()->destroy(context); - if (lock) { - JS_DESTROY_LOCK(lock); - lock = NULL; - } - if (context) { - JS_SetContextThread(context); - JS_DestroyContextNoGC(context); - context = NULL; - } - object = NULL; - - // Do not call parent->removeChild(). This is called either from - // ~Worker, which calls it for us; or from parent->disposeChildren or - // Worker::create, which require that it not be called. - parent = NULL; - disposeChildren(); - } - - static Worker *create(JSContext *parentcx, WorkerParent *parent, - JSString *scriptName, JSObject *obj); - - JSObject *asObject() { return object; } - - JSObject *getGlobal() { return JS_GetGlobalObject(context); } - - WorkerParent *getParent() { return parent; } - - virtual JSLock *getLock() { return lock; } - - virtual ThreadPool *getThreadPool() { return threadPool; } - - bool post(Event *event) { - AutoLock hold(lock); - if (terminated) - return false; - if (!current && events.empty() && !threadPool->getWorkerQueue()->post(this)) - return false; - return events.push(event); - } - - void setTerminateFlag() { - AutoLock hold(lock); - terminateFlag = true; - if (current) - JS_TriggerOperationCallback(context); - } - - void processOneEvent(); - - /* Trace method to be called from C++. */ - void trace(JSTracer *trc) { - // Just mark the JSObject. If we haven't already been marked, - // jsTraceWorker will be called, at which point we'll trace referents. - JS_CALL_OBJECT_TRACER(trc, object, "queued Worker"); - } - - static bool getWorkerParentFromConstructor(JSContext *cx, JSObject *ctor, WorkerParent **p) { - jsval v; - if (!JS_GetReservedSlot(cx, ctor, 0, &v)) - return false; - if (JSVAL_IS_VOID(v)) { - // This means ctor is the root Worker constructor (created in - // Worker::initWorkers as opposed to Worker::createContext, which sets up - // Worker sandboxes) and nothing is initialized yet. - if (!JS_GetReservedSlot(cx, ctor, 1, &v)) - return false; - ThreadPool *threadPool = (ThreadPool *) JSVAL_TO_PRIVATE(v); - if (!threadPool->start(cx)) - return false; - WorkerParent *parent = threadPool->getMainQueue(); - if (!JS_SetReservedSlot(cx, ctor, 0, PRIVATE_TO_JSVAL(parent))) { - threadPool->shutdown(cx); - return false; - } - *p = parent; - return true; - } - *p = (WorkerParent *) JSVAL_TO_PRIVATE(v); - return true; - } - - static JSBool jsConstruct(JSContext *cx, uintN argc, jsval *vp) { - WorkerParent *parent; - if (!getWorkerParentFromConstructor(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), &parent)) - return false; - - - JSString *scriptName = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID); - if (!scriptName) - return false; - - JSObject *obj = JS_NewObject(cx, &jsWorkerClass, NULL, NULL); - if (!obj || !create(cx, parent, scriptName, obj)) - return false; - JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj)); - return true; - } - - static JSFunctionSpec jsMethods[3]; - static JSFunctionSpec jsStaticMethod[2]; - - static ThreadPool *initWorkers(JSContext *cx, WorkerHooks *hooks, JSObject *global, - JSObject **objp) { - // Create the ThreadPool object and its JSObject wrapper. - ThreadPool *threadPool = ThreadPool::create(cx, hooks); - if (!threadPool) - return NULL; - - // Root the ThreadPool JSObject early. - *objp = threadPool->asObject(); - - // Create the Worker constructor. - JSObject *proto = JS_InitClass(cx, global, NULL, &jsWorkerClass, - jsConstruct, 1, - NULL, jsMethods, NULL, NULL); - if (!proto) - return NULL; - - // Stash a pointer to the ThreadPool in constructor reserved slot 1. - // It will be used later when lazily creating the MainQueue. - JSObject *ctor = JS_GetConstructor(cx, proto); - if (!JS_SetReservedSlot(cx, ctor, 1, PRIVATE_TO_JSVAL(threadPool))) - return NULL; - - return threadPool; - } -}; - -class InitEvent : public Event -{ - public: - static InitEvent *create(JSContext *cx, Worker *worker, JSString *scriptName) { - return createEvent(cx, worker, worker, STRING_TO_JSVAL(scriptName)); - } - - Result process(JSContext *cx) { - jsval s; - if (!deserializeData(cx, &s)) - return fail; - JS_ASSERT(JSVAL_IS_STRING(s)); - JSAutoByteString filename(cx, JSVAL_TO_STRING(s)); - if (!filename) - return fail; - - JSObject *scriptObj = JS_CompileFile(cx, child->getGlobal(), filename.ptr()); - if (!scriptObj) - return fail; - - AutoValueRooter rval(cx); - JSBool ok = JS_ExecuteScript(cx, child->getGlobal(), scriptObj, Jsvalify(rval.addr())); - return Result(ok); - } -}; - -class DownMessageEvent : public Event -{ - public: - static DownMessageEvent *create(JSContext *cx, Worker *child, jsval data) { - return createEvent(cx, child, child, data); - } - - Result process(JSContext *cx) { - return dispatch(cx, child->getGlobal(), "data", "onmessage", ok); - } -}; - -class UpMessageEvent : public Event -{ - public: - static UpMessageEvent *create(JSContext *cx, Worker *child, jsval data) { - return createEvent(cx, child->getParent(), child, data); - } - - Result process(JSContext *cx) { - return dispatch(cx, child->asObject(), "data", "onmessage", ok); - } -}; - -class ErrorEvent : public Event -{ - public: - static ErrorEvent *create(JSContext *cx, Worker *child) { - JSString *data = NULL; - jsval exc; - if (JS_GetPendingException(cx, &exc)) { - AutoValueRooter tvr(cx, Valueify(exc)); - JS_ClearPendingException(cx); - - // Determine what error message to put in the error event. - // If exc.message is a string, use that; otherwise use String(exc). - // (This is a little different from what web workers do.) - if (JSVAL_IS_OBJECT(exc)) { - jsval msg; - if (!JS_GetProperty(cx, JSVAL_TO_OBJECT(exc), "message", &msg)) - JS_ClearPendingException(cx); - else if (JSVAL_IS_STRING(msg)) - data = JSVAL_TO_STRING(msg); - } - if (!data) { - data = JS_ValueToString(cx, exc); - if (!data) - return NULL; - } - } - return createEvent(cx, child->getParent(), child, - data ? STRING_TO_JSVAL(data) : JSVAL_VOID); - } - - Result process(JSContext *cx) { - return dispatch(cx, child->asObject(), "message", "onerror", forwardToParent); - } -}; - -} /* namespace workers */ -} /* namespace js */ - -using namespace js::workers; - -void -WorkerParent::disposeChildren() -{ - for (ChildSet::Enum e(children); !e.empty(); e.popFront()) { - e.front()->dispose(); - e.removeFront(); - } -} - -bool -MainQueue::shouldStop() -{ - // Note: This deliberately nests WorkerQueue::lock in MainQueue::lock. - // Releasing MainQueue::lock would risk a race -- isIdle() could return - // false, but the workers could become idle before we reacquire - // MainQueue::lock and go to sleep, and we would wait on the condvar - // forever. - return closed || threadPool->getWorkerQueue()->isIdle(); -} - -void -MainQueue::trace(JSTracer *trc) -{ - JS_CALL_OBJECT_TRACER(trc, threadPool->asObject(), "MainQueue"); -} - -void -WorkerQueue::work() { - AutoLock hold(lock); - - Worker *w; - while (take(&w)) { // can block outside the mutex - JS_RELEASE_LOCK(lock); - w->processOneEvent(); // enters request on w->context - JS_ACQUIRE_LOCK(lock); - drop(w); - - if (lockedIsIdle()) { - JS_RELEASE_LOCK(lock); - main->wake(); - JS_ACQUIRE_LOCK(lock); - } - } -} - -const bool mswin = -#ifdef XP_WIN - true -#else - false -#endif - ; - -template bool -IsAbsolute(const Ch *filename) -{ - return filename[0] == '/' || - (mswin && (filename[0] == '\\' || (filename[0] != '\0' && filename[1] == ':'))); -} - -// Note: base is a filename, not a directory name. -static JSString * -ResolveRelativePath(JSContext *cx, const char *base, JSString *filename) -{ - size_t fileLen = JS_GetStringLength(filename); - const jschar *fileChars = JS_GetStringCharsZ(cx, filename); - if (!fileChars) - return NULL; - - if (IsAbsolute(fileChars)) - return filename; - - // Strip off the filename part of base. - size_t dirLen = -1; - for (size_t i = 0; base[i]; i++) { - if (base[i] == '/' || (mswin && base[i] == '\\')) - dirLen = i; - } - - // If base is relative and contains no directories, use filename unchanged. - if (!IsAbsolute(base) && dirLen == (size_t) -1) - return filename; - - // Otherwise return base[:dirLen + 1] + filename. - js::Vector result(cx); - size_t nchars; - if (!JS_DecodeBytes(cx, base, dirLen + 1, NULL, &nchars)) - return NULL; - if (!result.reserve(dirLen + 1 + fileLen)) { - JS_ReportOutOfMemory(cx); - return NULL; - } - JS_ALWAYS_TRUE(result.resize(dirLen + 1)); - if (!JS_DecodeBytes(cx, base, dirLen + 1, result.begin(), &nchars)) - return NULL; - JS_ALWAYS_TRUE(result.append(fileChars, fileLen)); - return JS_NewUCStringCopyN(cx, result.begin(), result.length()); -} - -Worker * -Worker::create(JSContext *parentcx, WorkerParent *parent, JSString *scriptName, JSObject *obj) -{ - Worker *w = new Worker(); - if (!w || !w->init(parentcx, parent, obj)) { - delete w; - return NULL; - } - - JSStackFrame *frame = JS_GetScriptedCaller(parentcx, NULL); - const char *base = JS_GetScriptFilename(parentcx, JS_GetFrameScript(parentcx, frame)); - JSString *scriptPath = ResolveRelativePath(parentcx, base, scriptName); - if (!scriptPath) - return NULL; - - // Post an InitEvent to run the initialization script. - Event *event = InitEvent::create(parentcx, w, scriptPath); - if (!event) - return NULL; - if (!w->events.push(event) || !w->threadPool->getWorkerQueue()->post(w)) { - event->destroy(parentcx); - JS_ReportOutOfMemory(parentcx); - w->dispose(); - return NULL; - } - return w; -} - -void -Worker::processOneEvent() -{ - Event *event; - { - AutoLock hold1(lock); - if (lockedCheckTermination() || events.empty()) - return; - - event = current = events.pop(); - } - - JS_SetContextThread(context); - JS_SetNativeStackQuota(context, gMaxStackSize); - - Event::Result result; - { - JSAutoRequest req(context); - result = event->process(context); - } - - // Note: we have to leave the above request before calling parent->post or - // checkTermination, both of which acquire locks. - if (result == Event::forwardToParent) { - event->setChildAndRecipient(this, parent); - if (parent->post(event)) { - event = NULL; // to prevent it from being deleted below - } else { - JS_ReportOutOfMemory(context); - result = Event::fail; - } - } - if (result == Event::fail && !checkTermination()) { - JSAutoRequest req(context); - Event *err = ErrorEvent::create(context, this); - if (err && !parent->post(err)) { - JS_ReportOutOfMemory(context); - err->destroy(context); - err = NULL; - } - if (!err) { - // FIXME - out of memory, probably should panic - } - } - - if (event) - event->destroy(context); - JS_ClearContextThread(context); - - { - AutoLock hold2(lock); - current = NULL; - if (!lockedCheckTermination() && !events.empty()) { - // Re-enqueue this worker. OOM here effectively kills the worker. - if (!threadPool->getWorkerQueue()->post(this)) - JS_ReportOutOfMemory(context); - } - } -} - -JSBool -Worker::jsPostMessageToParent(JSContext *cx, uintN argc, jsval *vp) -{ - jsval workerval; - if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), 0, &workerval)) - return false; - Worker *w = (Worker *) JSVAL_TO_PRIVATE(workerval); - - { - JSAutoSuspendRequest suspend(cx); // avoid nesting w->lock in a request - if (w->checkTermination()) - return false; - } - - jsval data = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID; - Event *event = UpMessageEvent::create(cx, w, data); - if (!event) - return false; - if (!w->parent->post(event)) { - event->destroy(cx); - JS_ReportOutOfMemory(cx); - return false; - } - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return true; -} - -JSBool -Worker::jsPostMessageToChild(JSContext *cx, uintN argc, jsval *vp) -{ - JSObject *workerobj = JS_THIS_OBJECT(cx, vp); - if (!workerobj) - return false; - Worker *w = (Worker *) JS_GetInstancePrivate(cx, workerobj, &jsWorkerClass, JS_ARGV(cx, vp)); - if (!w) { - if (!JS_IsExceptionPending(cx)) - JS_ReportError(cx, "Worker was shut down"); - return false; - } - - jsval data = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID; - Event *event = DownMessageEvent::create(cx, w, data); - if (!event) - return false; - if (!w->post(event)) { - JS_ReportOutOfMemory(cx); - return false; - } - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return true; -} - -JSBool -Worker::jsTerminate(JSContext *cx, uintN argc, jsval *vp) -{ - JS_SET_RVAL(cx, vp, JSVAL_VOID); - - JSObject *workerobj = JS_THIS_OBJECT(cx, vp); - if (!workerobj) - return false; - Worker *w = (Worker *) JS_GetInstancePrivate(cx, workerobj, &jsWorkerClass, JS_ARGV(cx, vp)); - if (!w) - return !JS_IsExceptionPending(cx); // ok to terminate twice - - JSAutoSuspendRequest suspend(cx); - w->setTerminateFlag(); - return true; -} - -void -Event::trace(JSTracer *trc) -{ - if (recipient) - recipient->trace(trc); - if (child) - JS_CALL_OBJECT_TRACER(trc, child->asObject(), "worker"); -} - -JSClass ThreadPool::jsClass = { - "ThreadPool", JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, jsFinalize, - NULL, NULL, NULL, NULL, - NULL, NULL, JS_CLASS_TRACE(jsTraceThreadPool), NULL -}; - -JSClass Worker::jsWorkerClass = { - "Worker", JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, jsFinalize, - NULL, NULL, NULL, NULL, - NULL, NULL, JS_CLASS_TRACE(jsTraceWorker), NULL -}; - -JSFunctionSpec Worker::jsMethods[3] = { - JS_FN("postMessage", Worker::jsPostMessageToChild, 1, 0), - JS_FN("terminate", Worker::jsTerminate, 0, 0), - JS_FS_END -}; - -ThreadPool * -js::workers::init(JSContext *cx, WorkerHooks *hooks, JSObject *global, JSObject **rootp) -{ - return Worker::initWorkers(cx, hooks, global, rootp); -} - -void -js::workers::terminateAll(JSRuntime *rt, ThreadPool *tp) -{ - tp->terminateAll(rt); -} - -void -js::workers::finish(JSContext *cx, ThreadPool *tp) -{ - if (MainQueue *mq = tp->getMainQueue()) { - JS_ALWAYS_TRUE(mq->mainThreadWork(cx, true)); - tp->shutdown(cx); - } -} - -#endif /* JS_THREADSAFE */ diff -ruN src/shell/jsworkers.h src.new/shell/jsworkers.h --- shell/jsworkers.h 2011-04-01 06:08:36.000000000 +1100 +++ shell/jsworkers.h 1970-01-01 10:00:00.000000000 +1000 @@ -1,93 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is JavaScript shell workers. - * - * The Initial Developer of the Original Code is - * Mozilla Corporation. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Jason Orendorff - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsworkers_h___ -#define jsworkers_h___ - -#ifdef JS_THREADSAFE - -#include "jsapi.h" - -/* - * Workers for the JS shell. - * - * Note: The real implementation of DOM Workers is in dom/src/threads. - */ -namespace js { - namespace workers { - class ThreadPool; - - class WorkerHooks { - public: - virtual JSObject *newGlobalObject(JSContext *cx) = 0; - virtual ~WorkerHooks() {} - }; - - /* - * Initialize workers. This defines the Worker constructor on global. - * Requires request. rootp must point to a GC root. - * - * On success, *rootp receives a pointer to an object, and init returns - * a non-null value. The caller must keep the object rooted and must - * pass it to js::workers::finish later. - */ - ThreadPool *init(JSContext *cx, WorkerHooks *hooks, JSObject *global, JSObject **rootp); - - /* Asynchronously signal for all workers to terminate. - * - * Call this before calling finish() to shut down without waiting for - * all messages to be proceesed. - */ - void terminateAll(JSRuntime *rt, ThreadPool *tp); - - /* - * Finish running any workers, shut down the thread pool, and free all - * resources associated with workers. The application must call this - * before shutting down the runtime, and not during GC. - * - * Requires request. - */ - void finish(JSContext *cx, ThreadPool *tp); - } -} - -#endif /* JS_THREADSAFE */ - -#endif /* jsworkers_h___ */ diff -ruN src/tests/browser.js src.new/tests/browser.js --- tests/browser.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/browser.js 2013-03-23 22:03:56.716635673 +1100 @@ -767,17 +767,6 @@ document.write(s); } -var JSTest = { - waitForExplicitFinish: function () { - gDelayTestDriverEnd = true; - }, - - testFinished: function () { - gDelayTestDriverEnd = false; - jsTestDriverEnd(); - } -}; - function jsTestDriverEnd() { // gDelayTestDriverEnd is used to diff -ruN src/tests/js1_8_5/extensions/jstests.list src.new/tests/js1_8_5/extensions/jstests.list --- tests/js1_8_5/extensions/jstests.list 2011-04-01 06:08:36.000000000 +1100 +++ tests/js1_8_5/extensions/jstests.list 2013-03-23 22:29:16.973264001 +1100 @@ -1,13 +1,6 @@ url-prefix ../../jsreftest.html?test=js1_8_5/extensions/ script typedarray.js script typedarray-prototype.js -skip-if(!xulRuntime.shell) script worker-error.js # these tests sometimes hang in browser, bug 559954, bug 562333 -skip-if(!xulRuntime.shell) script worker-error-propagation.js -skip-if(!xulRuntime.shell) script worker-fib.js -skip-if(!xulRuntime.shell) script worker-init.js -skip-if(!xulRuntime.shell) script worker-simple.js -skip-if(!xulRuntime.shell) script worker-terminate.js -skip-if(!xulRuntime.shell) script worker-timeout.js script scripted-proxies.js script array-length-protochange.js script parseInt-octal.js diff -ruN src/tests/js1_8_5/extensions/worker-error-child.js src.new/tests/js1_8_5/extensions/worker-error-child.js --- tests/js1_8_5/extensions/worker-error-child.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/js1_8_5/extensions/worker-error-child.js 1970-01-01 10:00:00.000000000 +1000 @@ -1,9 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Jason Orendorff - */ -function onmessage(event) { - throw new Error("fail"); -} diff -ruN src/tests/js1_8_5/extensions/worker-error-propagation-child.js src.new/tests/js1_8_5/extensions/worker-error-propagation-child.js --- tests/js1_8_5/extensions/worker-error-propagation-child.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/js1_8_5/extensions/worker-error-propagation-child.js 1970-01-01 10:00:00.000000000 +1000 @@ -1,16 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Jason Orendorff - */ - -function onmessage(event) { - var n = +event.data; - if (n == 0) - throw new Error("boom"); - var w = new Worker(workerDir + "worker-error-propagation-child.js"); - w.onmessage = function (event) { postMessage(event.data); }; - // No w.onerror here. We are testing error propagation when it is absent. - w.postMessage(n - 1 + ""); -} diff -ruN src/tests/js1_8_5/extensions/worker-error-propagation.js src.new/tests/js1_8_5/extensions/worker-error-propagation.js --- tests/js1_8_5/extensions/worker-error-propagation.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/js1_8_5/extensions/worker-error-propagation.js 1970-01-01 10:00:00.000000000 +1000 @@ -1,20 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Jason Orendorff - */ - -if (typeof Worker != 'undefined') { - JSTest.waitForExplicitFinish(); - var w = Worker(workerDir + "worker-error-propagation-child.js"); - var errors = 0; - w.onmessage = function () { throw new Error("no reply expected"); }; - w.onerror = function (event) { - reportCompare("string", typeof event.message, "typeof event.message"); - JSTest.testFinished(); - }; - w.postMessage("5"); -} else { - reportCompare(0, 0, " PASSED! Test skipped. Shell workers required."); -} diff -ruN src/tests/js1_8_5/extensions/worker-error.js src.new/tests/js1_8_5/extensions/worker-error.js --- tests/js1_8_5/extensions/worker-error.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/js1_8_5/extensions/worker-error.js 1970-01-01 10:00:00.000000000 +1000 @@ -1,21 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Jason Orendorff - */ - -if (typeof Worker != 'undefined') { - JSTest.waitForExplicitFinish(); - - // The script throws new Error("fail") on first message. - var w = Worker(workerDir + "worker-error-child.js"); - var a = []; - w.onerror = function (event) { - reportCompare("fail", event.message, "worker-error"); - JSTest.testFinished(); - }; - w.postMessage("hello"); -} else { - reportCompare(0, 0, "Test skipped. Shell workers required."); -} diff -ruN src/tests/js1_8_5/extensions/worker-fib-child.js src.new/tests/js1_8_5/extensions/worker-fib-child.js --- tests/js1_8_5/extensions/worker-fib-child.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/js1_8_5/extensions/worker-fib-child.js 1970-01-01 10:00:00.000000000 +1000 @@ -1,27 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Jason Orendorff - */ - -function onmessage(event) { - var a = event.data.split(/\t/); - var n = Number(a[0]); - var workerDir = a[1]; - - if (n <= 1) { - postMessage("" + n); - } else { - var w1 = new Worker(workerDir + "worker-fib-child.js"), - w2 = new Worker(workerDir + "worker-fib-child.js"); - var a = []; - w1.onmessage = w2.onmessage = function(event) { - a.push(+event.data); - if (a.length == 2) - postMessage(a[0] + a[1] + ""); - }; - w1.postMessage(n - 1 + "\t" + workerDir); - w2.postMessage(n - 2 + "\t" + workerDir); - } -} diff -ruN src/tests/js1_8_5/extensions/worker-fib.js src.new/tests/js1_8_5/extensions/worker-fib.js --- tests/js1_8_5/extensions/worker-fib.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/js1_8_5/extensions/worker-fib.js 1970-01-01 10:00:00.000000000 +1000 @@ -1,18 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Jason Orendorff - */ - -if (typeof Worker != 'undefined') { - JSTest.waitForExplicitFinish(); - var w = Worker(workerDir + "worker-fib-child.js"); - w.onmessage = function (event) { - reportCompare("55", event.data, "worker-fib"); - JSTest.testFinished(); - }; - w.postMessage("10\t" + workerDir); // 0 1 1 2 3 5 8 13 21 34 55 -} else { - reportCompare(0, 0, "Test skipped. Shell workers required."); -} diff -ruN src/tests/js1_8_5/extensions/worker-init-child.js src.new/tests/js1_8_5/extensions/worker-init-child.js --- tests/js1_8_5/extensions/worker-init-child.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/js1_8_5/extensions/worker-init-child.js 1970-01-01 10:00:00.000000000 +1000 @@ -1,8 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Jason Orendorff - */ -postMessage('do your worst'); -for (;;) ; diff -ruN src/tests/js1_8_5/extensions/worker-init.js src.new/tests/js1_8_5/extensions/worker-init.js --- tests/js1_8_5/extensions/worker-init.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/js1_8_5/extensions/worker-init.js 1970-01-01 10:00:00.000000000 +1000 @@ -1,19 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Jason Orendorff - */ - -if (typeof Worker != 'undefined') { - JSTest.waitForExplicitFinish(); - // Messages sent during initialization are a corner case, but in any case - // they should be delivered (no waiting until initialization is complete). - var w = new Worker(workerDir + "worker-init-child.js"); // posts a message, then loops forever - w.onmessage = function (event) { - reportCompare(0, 0, "worker-init"); - JSTest.testFinished(); - }; -} else { - reportCompare(0, 0, "Test skipped. Shell workers required."); -} diff -ruN src/tests/js1_8_5/extensions/worker-simple-child.js src.new/tests/js1_8_5/extensions/worker-simple-child.js --- tests/js1_8_5/extensions/worker-simple-child.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/js1_8_5/extensions/worker-simple-child.js 1970-01-01 10:00:00.000000000 +1000 @@ -1,8 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Jason Orendorff - */ - -onmessage = function (event) { postMessage(event.data); }; diff -ruN src/tests/js1_8_5/extensions/worker-simple.js src.new/tests/js1_8_5/extensions/worker-simple.js --- tests/js1_8_5/extensions/worker-simple.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/js1_8_5/extensions/worker-simple.js 1970-01-01 10:00:00.000000000 +1000 @@ -1,20 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Jason Orendorff - */ - -if (typeof Worker != 'undefined') { - JSTest.waitForExplicitFinish(); - var w = new Worker(workerDir + "worker-simple-child.js"); - var a = []; - w.onmessage = function (event) { - a.push(event.data); - reportCompare(0, 0, "worker-simple"); - JSTest.testFinished(); - }; - w.postMessage("hello"); -} else { - reportCompare(0, 0, "Test skipped. Shell workers required."); -} diff -ruN src/tests/js1_8_5/extensions/worker-terminate-child.js src.new/tests/js1_8_5/extensions/worker-terminate-child.js --- tests/js1_8_5/extensions/worker-terminate-child.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/js1_8_5/extensions/worker-terminate-child.js 1970-01-01 10:00:00.000000000 +1000 @@ -1,13 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Jason Orendorff - */ - -onmessage = function (event) { - var workerDir = event.message; - var child = new Worker(workerDir + 'worker-terminate-iloop.js'); // loops forever - child.terminate(); - postMessage("killed"); -}; diff -ruN src/tests/js1_8_5/extensions/worker-terminate-iloop.js src.new/tests/js1_8_5/extensions/worker-terminate-iloop.js --- tests/js1_8_5/extensions/worker-terminate-iloop.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/js1_8_5/extensions/worker-terminate-iloop.js 1970-01-01 10:00:00.000000000 +1000 @@ -1,9 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Jason Orendorff - */ - -for (;;) - ; diff -ruN src/tests/js1_8_5/extensions/worker-terminate.js src.new/tests/js1_8_5/extensions/worker-terminate.js --- tests/js1_8_5/extensions/worker-terminate.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/js1_8_5/extensions/worker-terminate.js 1970-01-01 10:00:00.000000000 +1000 @@ -1,37 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Jason Orendorff - */ - -if (typeof Worker != 'undefined') { - JSTest.waitForExplicitFinish(); - - // This tests that a parent worker can terminate a child. We run the test - // several times serially. If terminate() doesn't work, the runaway Workers - // will soon outnumber the number of threads in the thread pool, and we - // will deadlock. - var i = 0; - - function next() { - if (++i == 10) { - reportCompare(0, 0, "worker-terminate"); - JSTest.testFinished(); - return; - } - - var w = new Worker(workerDir + "worker-terminate-child.js"); - w.onmessage = function (event) { - reportCompare("killed", event.data, "killed runaway worker #" + i); - next(); - }; - w.onerror = function (event) { - reportCompare(0, 1, "Got error: " + event.message); - }; - w.postMessage(workerDir); - } - next(); -} else { - reportCompare(0, 0, "Test skipped. Shell workers required."); -} diff -ruN src/tests/js1_8_5/extensions/worker-timeout-child.js src.new/tests/js1_8_5/extensions/worker-timeout-child.js --- tests/js1_8_5/extensions/worker-timeout-child.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/js1_8_5/extensions/worker-timeout-child.js 1970-01-01 10:00:00.000000000 +1000 @@ -1,9 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Jason Orendorff - */ - -for (;;) - ; diff -ruN src/tests/js1_8_5/extensions/worker-timeout.js src.new/tests/js1_8_5/extensions/worker-timeout.js --- tests/js1_8_5/extensions/worker-timeout.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/js1_8_5/extensions/worker-timeout.js 1970-01-01 10:00:00.000000000 +1000 @@ -1,18 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Jason Orendorff - */ - -if (typeof timeout == 'function' && typeof Worker != 'undefined') { - // We don't actually ever call JSTest.testFinished(); instead we - // time out and exit the shell with exit code 6. - JSTest.waitForExplicitFinish(); - expectExitCode(6); - timeout(1.0); - for (var i = 0; i < 5; i++) - new Worker(workerDir + "worker-timeout-child.js"); // just loops forever -} else { - reportCompare(0, 0, "Test skipped. Shell workers and timeout required."); -} diff -ruN src/tests/shell.js src.new/tests/shell.js --- tests/shell.js 2011-04-01 06:08:36.000000000 +1100 +++ tests/shell.js 2013-03-23 22:07:41.387249919 +1100 @@ -833,18 +833,6 @@ } } -var JSTest = { - waitForExplicitFinish: function () { - gDelayTestDriverEnd = true; - }, - - testFinished: function () { - gDelayTestDriverEnd = false; - jsTestDriverEnd(); - quit(); - } -}; - function jsTestDriverEnd() { // gDelayTestDriverEnd is used to