From owner-svn-ports-head@freebsd.org Wed Dec 26 20:36:59 2018 Return-Path: Delivered-To: svn-ports-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 5CD47135BE15; Wed, 26 Dec 2018 20:36:59 +0000 (UTC) (envelope-from swills@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 101376A992; Wed, 26 Dec 2018 20:36:59 +0000 (UTC) (envelope-from swills@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 02D9DBEE; Wed, 26 Dec 2018 20:36:59 +0000 (UTC) (envelope-from swills@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id wBQKawET055787; Wed, 26 Dec 2018 20:36:58 GMT (envelope-from swills@FreeBSD.org) Received: (from swills@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id wBQKawYj055784; Wed, 26 Dec 2018 20:36:58 GMT (envelope-from swills@FreeBSD.org) Message-Id: <201812262036.wBQKawYj055784@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: swills set sender to swills@FreeBSD.org using -f From: Steve Wills Date: Wed, 26 Dec 2018 20:36:58 +0000 (UTC) To: ports-committers@freebsd.org, svn-ports-all@freebsd.org, svn-ports-head@freebsd.org Subject: svn commit: r488434 - in head/games/oolite: . files X-SVN-Group: ports-head X-SVN-Commit-Author: swills X-SVN-Commit-Paths: in head/games/oolite: . files X-SVN-Commit-Revision: 488434 X-SVN-Commit-Repository: ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: 101376A992 X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org X-Spamd-Result: default: False [-2.97 / 15.00]; local_wl_from(0.00)[FreeBSD.org]; NEURAL_HAM_MEDIUM(-1.00)[-0.999,0]; NEURAL_HAM_SHORT(-0.98)[-0.977,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US]; NEURAL_HAM_LONG(-1.00)[-0.999,0] X-BeenThere: svn-ports-head@freebsd.org X-Mailman-Version: 2.1.29 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: Wed, 26 Dec 2018 20:36:59 -0000 Author: swills Date: Wed Dec 26 20:36:58 2018 New Revision: 488434 URL: https://svnweb.freebsd.org/changeset/ports/488434 Log: games/oolite: Update to 1.88 PR: 234408 Submitted by: lightside Modified: head/games/oolite/Makefile (contents, props changed) head/games/oolite/distinfo (contents, props changed) head/games/oolite/files/patch-deps_mozilla-bug771281 (contents, props changed) Modified: head/games/oolite/Makefile ============================================================================== --- head/games/oolite/Makefile Wed Dec 26 20:34:47 2018 (r488433) +++ head/games/oolite/Makefile Wed Dec 26 20:36:58 2018 (r488434) @@ -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 @@ LIB_DEPENDS= libespeak.so:audio/espeak \ 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 @@ DATADIR= ${GNUSTEP_LOCAL_APPS}/oolite.app 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 Modified: head/games/oolite/distinfo ============================================================================== --- head/games/oolite/distinfo Wed Dec 26 20:34:47 2018 (r488433) +++ head/games/oolite/distinfo Wed Dec 26 20:36:58 2018 (r488434) @@ -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 Modified: head/games/oolite/files/patch-deps_mozilla-bug771281 ============================================================================== --- head/games/oolite/files/patch-deps_mozilla-bug771281 Wed Dec 26 20:34:47 2018 (r488433) +++ head/games/oolite/files/patch-deps_mozilla-bug771281 Wed Dec 26 20:36:58 2018 (r488434) @@ -84,1291 +84,6 @@ diff -ruN deps.orig/mozilla/js/src/shell/js.cpp deps/m #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 -+- * -+- * 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. -+- // 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(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(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(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)) *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***