diff options
| author | Kubilay Kocak <koobs@FreeBSD.org> | 2013-06-08 17:13:09 +0000 | 
|---|---|---|
| committer | Kubilay Kocak <koobs@FreeBSD.org> | 2013-06-08 17:13:09 +0000 | 
| commit | 59a275ca574a48cd9c1382654805471b5af8808d (patch) | |
| tree | 12dfb82a9c6ac7f14bf2edfc5e9257154678b03a /lang/spidermonkey185/files/patch-bug771281 | |
| parent | Chase relocation of upstream source, and remove BROKEN=Does not fetch. (diff) | |
lang/spidermonkey185: Fix clang build, cherrypick upstream clang fixes
- patch: -fvisibility=hidden to fix clang build
- patch: Remove shell workers [1]
- patch: Silence clang alignment warnings jsscript.h [2]
- patch: Silence clang alignment warnings in jsstr.cpp [3]
- Fix pkg-plist - remove pkgconfig dirrm entry
- Tweak regression-test: target
[1] https://bugzilla.mozilla.org/show_bug.cgi?id=771281
[2] https://bugzilla.mozilla.org/show_bug.cgi?id=662962
[3] https://bugzilla.mozilla.org/show_bug.cgi?id=662961
Thanks to Guido Falsi (madpilot) for the visibility patch, kwm and miwi
for additional testing and the constant reminders.
Diffstat (limited to 'lang/spidermonkey185/files/patch-bug771281')
| -rw-r--r-- | lang/spidermonkey185/files/patch-bug771281 | 1839 | 
1 files changed, 1839 insertions, 0 deletions
| diff --git a/lang/spidermonkey185/files/patch-bug771281 b/lang/spidermonkey185/files/patch-bug771281 new file mode 100644 index 000000000000..1b59e5dfeafb --- /dev/null +++ b/lang/spidermonkey185/files/patch-bug771281 @@ -0,0 +1,1839 @@ +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 | 
