svn commit: r320274 - in head/lang/spidermonkey185: . files

Kubilay Kocak koobs at FreeBSD.org
Sat Jun 8 17:13:10 UTC 2013


Author: koobs
Date: Sat Jun  8 17:13:09 2013
New Revision: 320274
URL: http://svnweb.freebsd.org/changeset/ports/320274

Log:
  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.

Added:
  head/lang/spidermonkey185/files/
  head/lang/spidermonkey185/files/patch-bug771281   (contents, props changed)
  head/lang/spidermonkey185/files/patch-configure.in   (contents, props changed)
  head/lang/spidermonkey185/files/patch-jsscript.h   (contents, props changed)
  head/lang/spidermonkey185/files/patch-jsstr.cpp   (contents, props changed)
Modified:
  head/lang/spidermonkey185/Makefile
  head/lang/spidermonkey185/pkg-plist

Modified: head/lang/spidermonkey185/Makefile
==============================================================================
--- head/lang/spidermonkey185/Makefile	Sat Jun  8 17:02:58 2013	(r320273)
+++ head/lang/spidermonkey185/Makefile	Sat Jun  8 17:13:09 2013	(r320274)
@@ -3,7 +3,7 @@
 
 PORTNAME=		spidermonkey185
 PORTVERSION=		1.8.5
-PORTREVISION=		1
+PORTREVISION=		2
 CATEGORIES=		lang
 MASTER_SITES=		${MASTER_SITE_MOZILLA}
 MASTER_SITE_SUBDIR=	js
@@ -17,6 +17,8 @@ LIB_DEPENDS=		nspr4:${PORTSDIR}/devel/ns
 
 CONFLICTS=		njs-[0-9]*
 
+#USES=			pkgconfig:build
+USE_AUTOTOOLS=		autoconf213:env
 GNU_CONFIGURE=		yes
 USE_GMAKE=		yes
 USE_GNOME=		gnomehack
@@ -151,10 +153,13 @@ PLIST_SUB+=	SPARC=""
 PLIST_SUB+=	SPARC="@comment "
 .endif
 
+pre-configure:
+	(cd ${WRKSRC} && ${AUTOCONF})
+
 regression-test: build
 	@${ECHO_MSG} -n "===> Running jstests.py: "
 	@cd ${WRKSRC} && ${SETENV} TZ=PST8PDT ${PYTHON_CMD} tests/jstests.py \
-	--no-progress ./js
+	--no-progress --worker-count=${MAKE_JOBS_NUMBER} ./js
 .if ${PORT_OPTIONS:MMETHODJIT} || ${PORT_OPTIONS:MTRACEJIT}
 	@${ECHO_MSG} -n "===> Running jit_test.py: "
 	@cd ${WRKSRC} && ${SETENV} TZ=PST8PDT ${PYTHON_CMD} jit-test/jit_test.py \

Added: head/lang/spidermonkey185/files/patch-bug771281
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lang/spidermonkey185/files/patch-bug771281	Sat Jun  8 17:13:09 2013	(r320274)
@@ -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 at 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.

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-ports-all mailing list