forked from Lainports/opnsense-ports
Taken from: https://github.com/freebsd/freebsd-ports.git Commit id: 5070672073b68be364139bc6b3a89100bd17d331
1839 lines
60 KiB
Text
1839 lines
60 KiB
Text
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 <jorendorff@mozilla.com>
|
|
- *
|
|
- * 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 <algorithm>
|
|
-#include <string.h>
|
|
-#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 T, class AllocPolicy>
|
|
-class Queue {
|
|
- private:
|
|
- typedef Vector<T, 4, AllocPolicy> 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<Worker *, DefaultHasher<Worker *>, 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 T>
|
|
-class ThreadSafeQueue
|
|
-{
|
|
- protected:
|
|
- Queue<T, SystemAllocPolicy> queue;
|
|
- JSLock *lock;
|
|
- PRCondVar *condvar;
|
|
- bool closed;
|
|
-
|
|
- private:
|
|
- Vector<T, 8, SystemAllocPolicy> 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 <class EventType>
|
|
- 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<Event *> 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<Worker *>
|
|
-{
|
|
- 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<Event *, SystemAllocPolicy> 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<InitEvent>(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<DownMessageEvent>(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<UpMessageEvent>(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<ErrorEvent>(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 <class Ch> 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<jschar, 0, js::ContextAllocPolicy> 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 <jorendorff@mozilla.com>
|
|
- *
|
|
- * 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 <jorendorff@mozilla.com>
|
|
- */
|
|
-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 <jorendorff@mozilla.com>
|
|
- */
|
|
-
|
|
-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 <jorendorff@mozilla.com>
|
|
- */
|
|
-
|
|
-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 <jorendorff@mozilla.com>
|
|
- */
|
|
-
|
|
-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 <jorendorff@mozilla.com>
|
|
- */
|
|
-
|
|
-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 <jorendorff@mozilla.com>
|
|
- */
|
|
-
|
|
-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 <jorendorff@mozilla.com>
|
|
- */
|
|
-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 <jorendorff@mozilla.com>
|
|
- */
|
|
-
|
|
-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 <jorendorff@mozilla.com>
|
|
- */
|
|
-
|
|
-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 <jorendorff@mozilla.com>
|
|
- */
|
|
-
|
|
-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 <jorendorff@mozilla.com>
|
|
- */
|
|
-
|
|
-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 <jorendorff@mozilla.com>
|
|
- */
|
|
-
|
|
-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 <jorendorff@mozilla.com>
|
|
- */
|
|
-
|
|
-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
|