From owner-svn-ports-head@FreeBSD.ORG Sat Jun 8 17:13:10 2013 Return-Path: Delivered-To: svn-ports-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by hub.freebsd.org (Postfix) with ESMTP id 108CC4B5; Sat, 8 Jun 2013 17:13:10 +0000 (UTC) (envelope-from koobs@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id 01DFD1741; Sat, 8 Jun 2013 17:13:10 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r58HD99a037385; Sat, 8 Jun 2013 17:13:09 GMT (envelope-from koobs@svn.freebsd.org) Received: (from koobs@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r58HD9WX037382; Sat, 8 Jun 2013 17:13:09 GMT (envelope-from koobs@svn.freebsd.org) Message-Id: <201306081713.r58HD9WX037382@svn.freebsd.org> From: Kubilay Kocak Date: Sat, 8 Jun 2013 17:13:09 +0000 (UTC) To: ports-committers@freebsd.org, svn-ports-all@freebsd.org, svn-ports-head@freebsd.org Subject: svn commit: r320274 - in head/lang/spidermonkey185: . files X-SVN-Group: ports-head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-ports-head@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: SVN commit messages for the ports tree for head List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 08 Jun 2013 17:13:10 -0000 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 +- * +- * Alternatively, the contents of this file may be used under the terms of +- * either of the GNU General Public License Version 2 or later (the "GPL"), +- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +- * in which case the provisions of the GPL or the LGPL are applicable instead +- * of those above. If you wish to allow use of your version of this file only +- * under the terms of either the GPL or the LGPL, and not to allow others to +- * use your version of this file under the terms of the MPL, indicate your +- * decision by deleting the provisions above and replace them with the notice +- * and other provisions required by the GPL or the LGPL. If you do not delete +- * the provisions above, a recipient may use your version of this file under +- * the terms of any one of the MPL, the GPL or the LGPL. +- * +- * ***** END LICENSE BLOCK ***** */ +- +-#ifdef JS_THREADSAFE +- +-#include +-#include +-#include "prthread.h" +-#include "prlock.h" +-#include "prcvar.h" +-#include "jsapi.h" +-#include "jscntxt.h" +-#include "jshashtable.h" +-#include "jsstdint.h" +-#include "jslock.h" +-#include "jsvector.h" +-#include "jsworkers.h" +- +-extern size_t gMaxStackSize; +- +-/* +- * JavaScript shell workers. +- * +- * == Object lifetime rules == +- * +- * - The ThreadPool lasts from init() to finish(). +- * +- * - The ThreadPool owns the MainQueue and the WorkerQueue. Those live from +- * the time the first Worker is created until finish(). +- * +- * - Each JS Worker object has the same lifetime as the corresponding C++ +- * Worker object. A Worker is live if (a) the Worker JSObject is still +- * live; (b) the Worker has an incoming event pending or running; (c) it +- * has sent an outgoing event to its parent that is still pending; or (d) +- * it has any live child Workers. +- * +- * - finish() continues to wait for events until all threads are idle. +- * +- * Event objects, however, are basically C++-only. The JS Event objects are +- * just plain old JSObjects. They don't keep anything alive. +- * +- * == Locking scheme == +- * +- * When mixing mutexes and the JSAPI request model, there are two choices: +- * +- * - Always nest the mutexes in requests. Since threads in requests are not +- * supposed to block, this means the mutexes must be only briefly held. +- * +- * - Never nest the mutexes in requests. Since this allows threads to race +- * with the GC, trace() methods must go through the mutexes just like +- * everyone else. +- * +- * This code uses the latter approach for all locks. +- * +- * In one case, a thread holding a Worker's mutex can acquire the mutex of one +- * of its child Workers. See Worker::terminateSelf. (This can't deadlock because +- * the parent-child relationship is a partial order.) +- */ +- +-namespace js { +-namespace workers { +- +-template +-class Queue { +- private: +- typedef Vector Vec; +- Vec v1; +- Vec v2; +- Vec *front; +- Vec *back; +- +- // Queue is not copyable. +- Queue(const Queue &); +- Queue & operator=(const Queue &); +- +- public: +- Queue() : front(&v1), back(&v2) {} +- bool push(T t) { return back->append(t); } +- bool empty() { return front->empty() && back->empty(); } +- +- T pop() { +- if (front->empty()) { +- std::reverse(back->begin(), back->end()); +- Vec *tmp = front; +- front = back; +- back = tmp; +- } +- T item = front->back(); +- front->popBack(); +- return item; +- } +- +- void clear() { +- v1.clear(); +- v2.clear(); +- } +- +- void trace(JSTracer *trc) { +- for (T *p = v1.begin(); p != v1.end(); p++) +- (*p)->trace(trc); +- for (T *p = v2.begin(); p != v2.end(); p++) +- (*p)->trace(trc); +- } +-}; +- +-class Event; +-class ThreadPool; +-class Worker; +- +-class WorkerParent { +- protected: +- typedef HashSet, SystemAllocPolicy> ChildSet; +- ChildSet children; +- +- bool initWorkerParent() { return children.init(8); } +- +- public: +- virtual JSLock *getLock() = 0; +- virtual ThreadPool *getThreadPool() = 0; +- virtual bool post(Event *item) = 0; // false on OOM or queue closed +- virtual void trace(JSTracer *trc) = 0; +- +- bool addChild(Worker *w) { +- AutoLock hold(getLock()); +- return children.put(w) != NULL; +- } +- +- // This must be called only from GC or when all threads are shut down. It +- // does not bother with locking. +- void removeChild(Worker *w) { +- ChildSet::Ptr p = children.lookup(w); +- JS_ASSERT(p); +- children.remove(p); +- } +- +- void disposeChildren(); +-}; +- +-template +-class ThreadSafeQueue +-{ +- protected: +- Queue queue; +- JSLock *lock; +- PRCondVar *condvar; +- bool closed; +- +- private: +- Vector busy; +- +- protected: +- ThreadSafeQueue() : lock(NULL), condvar(NULL), closed(false) {} +- +- ~ThreadSafeQueue() { +- if (condvar) +- JS_DESTROY_CONDVAR(condvar); +- if (lock) +- JS_DESTROY_LOCK(lock); +- } +- +- // Called by take() with the lock held. +- virtual bool shouldStop() { return closed; } +- +- public: +- bool initThreadSafeQueue() { +- JS_ASSERT(!lock); +- JS_ASSERT(!condvar); +- return (lock = JS_NEW_LOCK()) && (condvar = JS_NEW_CONDVAR(lock)); +- } +- +- bool post(T t) { +- AutoLock hold(lock); +- if (closed) +- return false; +- if (queue.empty()) +- JS_NOTIFY_ALL_CONDVAR(condvar); +- return queue.push(t); +- } +- +- void close() { +- AutoLock hold(lock); +- closed = true; +- queue.clear(); +- JS_NOTIFY_ALL_CONDVAR(condvar); +- } +- +- // The caller must hold the lock. +- bool take(T *t) { +- while (queue.empty()) { +- if (shouldStop()) +- return false; +- JS_WAIT_CONDVAR(condvar, JS_NO_TIMEOUT); +- } +- *t = queue.pop(); +- busy.append(*t); +- return true; +- } +- +- // The caller must hold the lock. +- void drop(T item) { +- for (T *p = busy.begin(); p != busy.end(); p++) { +- if (*p == item) { +- *p = busy.back(); +- busy.popBack(); +- return; +- } +- } +- JS_NOT_REACHED("removeBusy"); +- } +- +- bool lockedIsIdle() { return busy.empty() && queue.empty(); } +- +- bool isIdle() { +- AutoLock hold(lock); +- return lockedIsIdle(); +- } +- +- void wake() { +- AutoLock hold(lock); +- JS_NOTIFY_ALL_CONDVAR(condvar); +- } +- +- void trace(JSTracer *trc) { +- AutoLock hold(lock); +- for (T *p = busy.begin(); p != busy.end(); p++) +- (*p)->trace(trc); +- queue.trace(trc); +- } +-}; +- +-class MainQueue; +- +-class Event +-{ +- protected: +- virtual ~Event() { JS_ASSERT(!data); } +- +- WorkerParent *recipient; +- Worker *child; +- uint64 *data; +- size_t nbytes; +- +- public: +- enum Result { fail = JS_FALSE, ok = JS_TRUE, forwardToParent }; +- +- virtual void destroy(JSContext *cx) { +- JS_free(cx, data); +-#ifdef DEBUG +- data = NULL; +-#endif +- delete this; +- } +- +- void setChildAndRecipient(Worker *aChild, WorkerParent *aRecipient) { +- child = aChild; +- recipient = aRecipient; +- } +- +- bool deserializeData(JSContext *cx, jsval *vp) { +- return !!JS_ReadStructuredClone(cx, data, nbytes, JS_STRUCTURED_CLONE_VERSION, vp, +- NULL, NULL); +- } +- +- virtual Result process(JSContext *cx) = 0; +- +- inline void trace(JSTracer *trc); +- +- template +- static EventType *createEvent(JSContext *cx, WorkerParent *recipient, Worker *child, +- jsval v) +- { +- uint64 *data; +- size_t nbytes; +- if (!JS_WriteStructuredClone(cx, v, &data, &nbytes, NULL, NULL)) +- return NULL; +- +- EventType *event = new EventType; +- if (!event) { +- JS_ReportOutOfMemory(cx); +- return NULL; +- } +- event->recipient = recipient; +- event->child = child; +- event->data = data; +- event->nbytes = nbytes; +- return event; +- } +- +- Result dispatch(JSContext *cx, JSObject *thisobj, const char *dataPropName, +- const char *methodName, Result noHandler) +- { +- if (!data) +- return fail; +- +- JSBool found; +- if (!JS_HasProperty(cx, thisobj, methodName, &found)) +- return fail; +- if (!found) +- return noHandler; +- +- // Create event object. +- jsval v; +- if (!deserializeData(cx, &v)) +- return fail; +- JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL); +- if (!obj || !JS_DefineProperty(cx, obj, dataPropName, v, NULL, NULL, 0)) +- return fail; +- +- // Call event handler. +- jsval argv[1] = { OBJECT_TO_JSVAL(obj) }; +- jsval rval = JSVAL_VOID; +- return Result(JS_CallFunctionName(cx, thisobj, methodName, 1, argv, &rval)); +- } +-}; +- +-typedef ThreadSafeQueue EventQueue; +- +-class MainQueue : public EventQueue, public WorkerParent +-{ +- private: +- ThreadPool *threadPool; +- +- public: +- explicit MainQueue(ThreadPool *tp) : threadPool(tp) {} +- +- ~MainQueue() { +- JS_ASSERT(queue.empty()); +- } +- +- bool init() { return initThreadSafeQueue() && initWorkerParent(); } +- +- void destroy(JSContext *cx) { +- while (!queue.empty()) +- queue.pop()->destroy(cx); +- delete this; +- } +- +- virtual JSLock *getLock() { return lock; } +- virtual ThreadPool *getThreadPool() { return threadPool; } +- +- protected: +- virtual bool shouldStop(); +- +- public: +- virtual bool post(Event *event) { return EventQueue::post(event); } +- +- virtual void trace(JSTracer *trc); +- +- void traceChildren(JSTracer *trc) { EventQueue::trace(trc); } +- +- JSBool mainThreadWork(JSContext *cx, bool continueOnError) { +- JSAutoSuspendRequest suspend(cx); +- AutoLock hold(lock); +- +- Event *event; +- while (take(&event)) { +- JS_RELEASE_LOCK(lock); +- Event::Result result; +- { +- JSAutoRequest req(cx); +- result = event->process(cx); +- if (result == Event::forwardToParent) { +- // FIXME - pointlessly truncates the string to 8 bits +- jsval data; +- JSAutoByteString bytes; +- if (event->deserializeData(cx, &data) && +- JSVAL_IS_STRING(data) && +- bytes.encode(cx, JSVAL_TO_STRING(data))) { +- JS_ReportError(cx, "%s", bytes.ptr()); +- } else { +- JS_ReportOutOfMemory(cx); +- } +- result = Event::fail; +- } +- if (result == Event::fail && continueOnError) { +- if (JS_IsExceptionPending(cx) && !JS_ReportPendingException(cx)) +- JS_ClearPendingException(cx); +- result = Event::ok; +- } +- } +- JS_ACQUIRE_LOCK(lock); +- drop(event); +- event->destroy(cx); +- if (result != Event::ok) +- return false; +- } +- return true; +- } +-}; +- +-/* +- * A queue of workers. +- * +- * We keep a queue of workers with pending events, rather than a queue of +- * events, so that two threads won't try to run a Worker at the same time. +- */ +-class WorkerQueue : public ThreadSafeQueue +-{ +- private: +- MainQueue *main; +- +- public: +- explicit WorkerQueue(MainQueue *main) : main(main) {} +- +- void work(); +-}; +- +-/* The top-level object that owns everything else. */ +-class ThreadPool +-{ +- private: +- enum { threadCount = 6 }; +- +- JSObject *obj; +- WorkerHooks *hooks; +- MainQueue *mq; +- WorkerQueue *wq; +- PRThread *threads[threadCount]; +- int32_t terminating; +- +- static JSClass jsClass; +- +- static void start(void* arg) { +- ((WorkerQueue *) arg)->work(); +- } +- +- explicit ThreadPool(WorkerHooks *hooks) : hooks(hooks), mq(NULL), wq(NULL), terminating(0) { +- for (int i = 0; i < threadCount; i++) +- threads[i] = NULL; +- } +- +- public: +- ~ThreadPool() { +- JS_ASSERT(!mq); +- JS_ASSERT(!wq); +- JS_ASSERT(!threads[0]); +- } +- +- static ThreadPool *create(JSContext *cx, WorkerHooks *hooks) { +- ThreadPool *tp = new ThreadPool(hooks); +- if (!tp) { +- JS_ReportOutOfMemory(cx); +- return NULL; +- } +- +- JSObject *obj = JS_NewObject(cx, &jsClass, NULL, NULL); +- if (!obj || !JS_SetPrivate(cx, obj, tp)) { +- delete tp; +- return NULL; +- } +- tp->obj = obj; +- return tp; +- } +- +- JSObject *asObject() { return obj; } +- WorkerHooks *getHooks() { return hooks; } +- WorkerQueue *getWorkerQueue() { return wq; } +- MainQueue *getMainQueue() { return mq; } +- bool isTerminating() { return terminating != 0; } +- +- /* +- * Main thread only. Requires request (to prevent GC, which could see the +- * object in an inconsistent state). +- */ +- bool start(JSContext *cx) { +- JS_ASSERT(!mq && !wq); +- mq = new MainQueue(this); +- if (!mq || !mq->init()) { +- mq->destroy(cx); +- mq = NULL; +- return false; +- } +- wq = new WorkerQueue(mq); +- if (!wq || !wq->initThreadSafeQueue()) { +- delete wq; +- wq = NULL; +- mq->destroy(cx); +- mq = NULL; +- return false; +- } +- JSAutoSuspendRequest suspend(cx); +- bool ok = true; +- for (int i = 0; i < threadCount; i++) { +- threads[i] = PR_CreateThread(PR_USER_THREAD, start, wq, PR_PRIORITY_NORMAL, +- PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); +- if (!threads[i]) { +- shutdown(cx); +- ok = false; +- break; +- } +- } +- return ok; +- } +- +- void terminateAll(JSRuntime *rt) { +- // See comment about JS_ATOMIC_SET in the implementation of +- // JS_TriggerOperationCallback. +- JS_ATOMIC_SET(&terminating, 1); +- JS_TriggerAllOperationCallbacks(rt); +- } +- +- /* This context is used only to free memory. */ +- void shutdown(JSContext *cx) { +- wq->close(); +- for (int i = 0; i < threadCount; i++) { +- if (threads[i]) { +- PR_JoinThread(threads[i]); +- threads[i] = NULL; +- } +- } +- +- delete wq; +- wq = NULL; +- +- mq->disposeChildren(); +- mq->destroy(cx); +- mq = NULL; +- terminating = 0; +- } +- +- private: +- static void jsTraceThreadPool(JSTracer *trc, JSObject *obj) { +- ThreadPool *tp = unwrap(trc->context, obj); +- if (tp->mq) { +- tp->mq->traceChildren(trc); +- tp->wq->trace(trc); +- } +- } +- +- +- static void jsFinalize(JSContext *cx, JSObject *obj) { +- if (ThreadPool *tp = unwrap(cx, obj)) +- delete tp; +- } +- +- public: +- static ThreadPool *unwrap(JSContext *cx, JSObject *obj) { +- JS_ASSERT(JS_GET_CLASS(cx, obj) == &jsClass); +- return (ThreadPool *) JS_GetPrivate(cx, obj); +- } +-}; +- +-/* +- * A Worker is always in one of 4 states, except when it is being initialized +- * or destroyed, or its lock is held: +- * - idle (!terminated && current == NULL && events.empty()) +- * - enqueued (!terminated && current == NULL && !events.empty()) +- * - busy (!terminated && current != NULL) +- * - terminated (terminated && current == NULL && events.empty()) +- * +- * Separately, there is a terminateFlag that other threads can set +- * asynchronously to tell the Worker to terminate. +- */ +-class Worker : public WorkerParent +-{ +- private: +- ThreadPool *threadPool; +- WorkerParent *parent; +- JSObject *object; // Worker object exposed to parent +- JSContext *context; +- JSLock *lock; +- Queue events; // owning pointers to pending events +- Event *current; +- bool terminated; +- int32_t terminateFlag; +- +- static JSClass jsWorkerClass; +- +- Worker() +- : threadPool(NULL), parent(NULL), object(NULL), +- context(NULL), lock(NULL), current(NULL), terminated(false), terminateFlag(0) {} +- +- bool init(JSContext *parentcx, WorkerParent *parent, JSObject *obj) { +- JS_ASSERT(!threadPool && !this->parent && !object && !lock); +- +- if (!initWorkerParent() || !parent->addChild(this)) +- return false; +- threadPool = parent->getThreadPool(); +- this->parent = parent; +- this->object = obj; +- lock = JS_NEW_LOCK(); +- return lock && +- createContext(parentcx, parent) && +- JS_SetPrivate(parentcx, obj, this); +- } +- +- bool createContext(JSContext *parentcx, WorkerParent *parent) { +- JSRuntime *rt = JS_GetRuntime(parentcx); +- context = JS_NewContext(rt, 8192); +- if (!context) +- return false; +- +- // The Worker has a strong reference to the global; see jsTraceWorker. +- // JSOPTION_UNROOTED_GLOBAL ensures that when the worker becomes +- // unreachable, it and its global object can be collected. Otherwise +- // the cx->globalObject root would keep them both alive forever. +- JS_SetOptions(context, JS_GetOptions(parentcx) | JSOPTION_UNROOTED_GLOBAL | +- JSOPTION_DONT_REPORT_UNCAUGHT); +- JS_SetVersion(context, JS_GetVersion(parentcx)); +- JS_SetContextPrivate(context, this); +- JS_SetOperationCallback(context, jsOperationCallback); +- JS_BeginRequest(context); +- +- JSObject *global = threadPool->getHooks()->newGlobalObject(context); +- JSObject *post, *proto, *ctor; +- if (!global) +- goto bad; +- JS_SetGlobalObject(context, global); +- +- // Because the Worker is completely isolated from the rest of the +- // runtime, and because any pending events on a Worker keep the Worker +- // alive, this postMessage function cannot be called after the Worker +- // is collected. Therefore it's safe to stash a pointer (a weak +- // reference) to the C++ Worker object in the reserved slot. +- post = JS_GetFunctionObject(JS_DefineFunction(context, global, "postMessage", +- (JSNative) jsPostMessageToParent, 1, 0)); +- if (!post || !JS_SetReservedSlot(context, post, 0, PRIVATE_TO_JSVAL(this))) +- goto bad; +- +- proto = JS_InitClass(context, global, NULL, &jsWorkerClass, jsConstruct, 1, +- NULL, jsMethods, NULL, NULL); +- if (!proto) +- goto bad; +- +- ctor = JS_GetConstructor(context, proto); +- if (!ctor || !JS_SetReservedSlot(context, ctor, 0, PRIVATE_TO_JSVAL(this))) +- goto bad; +- +- JS_EndRequest(context); +- JS_ClearContextThread(context); +- return true; +- +- bad: +- JS_EndRequest(context); +- JS_DestroyContext(context); +- context = NULL; +- return false; +- } +- +- static void jsTraceWorker(JSTracer *trc, JSObject *obj) { +- JS_ASSERT(JS_GET_CLASS(trc->context, obj) == &jsWorkerClass); +- if (Worker *w = (Worker *) JS_GetPrivate(trc->context, obj)) { +- w->parent->trace(trc); +- w->events.trace(trc); +- if (w->current) +- w->current->trace(trc); +- JS_CALL_OBJECT_TRACER(trc, JS_GetGlobalObject(w->context), "Worker global"); +- } +- } +- +- static void jsFinalize(JSContext *cx, JSObject *obj) { +- JS_ASSERT(JS_GET_CLASS(cx, obj) == &jsWorkerClass); +- if (Worker *w = (Worker *) JS_GetPrivate(cx, obj)) +- delete w; +- } +- +- static JSBool jsOperationCallback(JSContext *cx) { +- Worker *w = (Worker *) JS_GetContextPrivate(cx); +- JSAutoSuspendRequest suspend(cx); // avoid nesting w->lock in a request +- return !w->checkTermination(); +- } +- +- static JSBool jsResolveGlobal(JSContext *cx, JSObject *obj, jsid id, uintN flags, +- JSObject **objp) +- { +- JSBool resolved; +- +- if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) +- return false; +- if (resolved) +- *objp = obj; +- +- return true; +- } +- +- static JSBool jsPostMessageToParent(JSContext *cx, uintN argc, jsval *vp); +- static JSBool jsPostMessageToChild(JSContext *cx, uintN argc, jsval *vp); +- static JSBool jsTerminate(JSContext *cx, uintN argc, jsval *vp); +- +- bool checkTermination() { +- AutoLock hold(lock); +- return lockedCheckTermination(); +- } +- +- bool lockedCheckTermination() { +- if (terminateFlag || threadPool->isTerminating()) { +- terminateSelf(); +- terminateFlag = 0; +- } +- return terminated; +- } +- +- // Caller must hold the lock. +- void terminateSelf() { +- terminated = true; +- while (!events.empty()) +- events.pop()->destroy(context); +- +- // Tell the children to shut down too. An arbitrarily silly amount of +- // processing could happen before the whole tree is terminated; but +- // this way we don't have to worry about blowing the C stack. +- for (ChildSet::Enum e(children); !e.empty(); e.popFront()) +- e.front()->setTerminateFlag(); // note: nesting locks here +- } +- +- public: +- ~Worker() { +- if (parent) +- parent->removeChild(this); +- dispose(); +- } +- +- void dispose() { +- JS_ASSERT(!current); +- while (!events.empty()) +- events.pop()->destroy(context); +- if (lock) { +- JS_DESTROY_LOCK(lock); +- lock = NULL; +- } +- if (context) { +- JS_SetContextThread(context); +- JS_DestroyContextNoGC(context); +- context = NULL; +- } +- object = NULL; +- +- // Do not call parent->removeChild(). This is called either from +- // ~Worker, which calls it for us; or from parent->disposeChildren or +- // Worker::create, which require that it not be called. +- parent = NULL; +- disposeChildren(); +- } +- +- static Worker *create(JSContext *parentcx, WorkerParent *parent, +- JSString *scriptName, JSObject *obj); +- +- JSObject *asObject() { return object; } +- +- JSObject *getGlobal() { return JS_GetGlobalObject(context); } +- +- WorkerParent *getParent() { return parent; } +- +- virtual JSLock *getLock() { return lock; } +- +- virtual ThreadPool *getThreadPool() { return threadPool; } +- +- bool post(Event *event) { +- AutoLock hold(lock); +- if (terminated) +- return false; +- if (!current && events.empty() && !threadPool->getWorkerQueue()->post(this)) +- return false; +- return events.push(event); +- } +- +- void setTerminateFlag() { +- AutoLock hold(lock); +- terminateFlag = true; +- if (current) +- JS_TriggerOperationCallback(context); +- } +- +- void processOneEvent(); +- +- /* Trace method to be called from C++. */ +- void trace(JSTracer *trc) { +- // Just mark the JSObject. If we haven't already been marked, +- // jsTraceWorker will be called, at which point we'll trace referents. +- JS_CALL_OBJECT_TRACER(trc, object, "queued Worker"); +- } +- +- static bool getWorkerParentFromConstructor(JSContext *cx, JSObject *ctor, WorkerParent **p) { +- jsval v; +- if (!JS_GetReservedSlot(cx, ctor, 0, &v)) +- return false; +- if (JSVAL_IS_VOID(v)) { +- // This means ctor is the root Worker constructor (created in +- // Worker::initWorkers as opposed to Worker::createContext, which sets up +- // Worker sandboxes) and nothing is initialized yet. +- if (!JS_GetReservedSlot(cx, ctor, 1, &v)) +- return false; +- ThreadPool *threadPool = (ThreadPool *) JSVAL_TO_PRIVATE(v); +- if (!threadPool->start(cx)) +- return false; +- WorkerParent *parent = threadPool->getMainQueue(); +- if (!JS_SetReservedSlot(cx, ctor, 0, PRIVATE_TO_JSVAL(parent))) { +- threadPool->shutdown(cx); +- return false; +- } +- *p = parent; +- return true; +- } +- *p = (WorkerParent *) JSVAL_TO_PRIVATE(v); +- return true; +- } +- +- static JSBool jsConstruct(JSContext *cx, uintN argc, jsval *vp) { +- WorkerParent *parent; +- if (!getWorkerParentFromConstructor(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), &parent)) +- return false; +- +- +- JSString *scriptName = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID); +- if (!scriptName) +- return false; +- +- JSObject *obj = JS_NewObject(cx, &jsWorkerClass, NULL, NULL); +- if (!obj || !create(cx, parent, scriptName, obj)) +- return false; +- JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj)); +- return true; +- } +- +- static JSFunctionSpec jsMethods[3]; +- static JSFunctionSpec jsStaticMethod[2]; +- +- static ThreadPool *initWorkers(JSContext *cx, WorkerHooks *hooks, JSObject *global, +- JSObject **objp) { +- // Create the ThreadPool object and its JSObject wrapper. +- ThreadPool *threadPool = ThreadPool::create(cx, hooks); +- if (!threadPool) +- return NULL; +- +- // Root the ThreadPool JSObject early. +- *objp = threadPool->asObject(); +- +- // Create the Worker constructor. +- JSObject *proto = JS_InitClass(cx, global, NULL, &jsWorkerClass, +- jsConstruct, 1, +- NULL, jsMethods, NULL, NULL); +- if (!proto) +- return NULL; +- +- // Stash a pointer to the ThreadPool in constructor reserved slot 1. *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***