FreeBSD Bugzilla – Attachment 200520 Details for
Bug 234408
games/oolite: Update to 1.88
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Proposed patch (since 480741 revision)
oolite.diff (text/plain), 43.53 KB, created by
lightside
on 2018-12-26 06:45:20 UTC
(
hide
)
Description:
Proposed patch (since 480741 revision)
Filename:
MIME Type:
Creator:
lightside
Created:
2018-12-26 06:45:20 UTC
Size:
43.53 KB
patch
obsolete
>Index: Makefile >=================================================================== >--- Makefile (revision 480741) >+++ Makefile (working copy) >@@ -2,10 +2,9 @@ > # $FreeBSD$ > > PORTNAME= oolite >-PORTVERSION= 1.86 >-PORTREVISION= 1 >+PORTVERSION= 1.88 > CATEGORIES= games gnustep >-MASTER_SITES= https://github.com/OoliteProject/oolite/releases/download/1.86/ >+MASTER_SITES= https://github.com/OoliteProject/oolite/releases/download/1.88/ > DISTNAME= ${PORTNAME}-source-${PORTVERSION} > DIST_SUBDIR= oolite > >@@ -24,7 +23,7 @@ > libpng.so:graphics/png \ > libminizip.so:archivers/minizip > >-USES= gnustep openal:al perl5 python:build tar:bzip2 >+USES= gl gnustep openal:al perl5 python:build tar:bzip2 > USE_CXXSTD= gnu++98 > USE_GL= gl glu > USE_SDL= sdl >@@ -47,7 +46,7 @@ > PORTDATA= Resources > PORTDOCS= *.pdf CHANGELOG.TXT contributors.txt > >-PLIST_FILES+= bin/oolite %%DATADIR%%/oolite \ >+PLIST_FILES+= bin/oolite ${DATADIR}/oolite \ > share/applications/oolite.desktop \ > share/pixmaps/oolite-icon.png > >Index: distinfo >=================================================================== >--- distinfo (revision 480741) >+++ distinfo (working copy) >@@ -1,3 +1,3 @@ >-TIMESTAMP = 1508936871 >-SHA256 (oolite/oolite-source-1.86.tar.bz2) = 9f99c72f433fbbad972abdac5104775b29994d73c0b35f05130b31522b70ec9a >-SIZE (oolite/oolite-source-1.86.tar.bz2) = 144561828 >+TIMESTAMP = 1540654199 >+SHA256 (oolite/oolite-source-1.88.tar.bz2) = 298abec5f9192b121003bf8d84fbf22eb137e5ce66f1a1469ca24fd582ab97e9 >+SIZE (oolite/oolite-source-1.88.tar.bz2) = 145146957 >Index: files/patch-deps_mozilla-bug771281 >=================================================================== >--- files/patch-deps_mozilla-bug771281 (revision 480741) >+++ files/patch-deps_mozilla-bug771281 (working copy) >@@ -84,1291 +84,6 @@ > #ifdef JSDEBUGGER > if (jsdc) { > #ifdef JSDEBUGGER_C_UI >-diff -ruN deps.orig/mozilla/js/src/shell/jsworkers.cpp.rej deps/mozilla/js/src/shell/jsworkers.cpp.rej >---- deps.orig/mozilla/js/src/shell/jsworkers.cpp.rej 1970-01-01 00:00:00 UTC >-+++ deps/mozilla/js/src/shell/jsworkers.cpp.rej >-@@ -0,0 +1,1281 @@ >-+@@ -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 deps.orig/mozilla/js/src/shell/jsworkers.h deps/mozilla/js/src/shell/jsworkers.h > --- deps.orig/mozilla/js/src/shell/jsworkers.h 2014-06-30 08:54:39 UTC > +++ deps/mozilla/js/src/shell/jsworkers.h
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 234408
:
200519
| 200520