From owner-svn-ports-head@FreeBSD.ORG Thu Oct 23 11:52:13 2014 Return-Path: Delivered-To: svn-ports-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 23737450; Thu, 23 Oct 2014 11:52:13 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 0D865A45; Thu, 23 Oct 2014 11:52:13 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id s9NBqDts097363; Thu, 23 Oct 2014 11:52:13 GMT (envelope-from madpilot@FreeBSD.org) Received: (from madpilot@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id s9NBqCiU097358; Thu, 23 Oct 2014 11:52:12 GMT (envelope-from madpilot@FreeBSD.org) Message-Id: <201410231152.s9NBqCiU097358@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: madpilot set sender to madpilot@FreeBSD.org using -f From: Guido Falsi Date: Thu, 23 Oct 2014 11:52:12 +0000 (UTC) To: ports-committers@freebsd.org, svn-ports-all@freebsd.org, svn-ports-head@freebsd.org Subject: svn commit: r371389 - in head/games/oolite: . 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.18-1 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: Thu, 23 Oct 2014 11:52:13 -0000 Author: madpilot Date: Thu Oct 23 11:52:11 2014 New Revision: 371389 URL: https://svnweb.freebsd.org/changeset/ports/371389 QAT: https://qat.redports.org/buildarchive/r371389/ Log: - Update to 1.80 - Add LICENSE information - Convert to using PLIST_FILES, PORTDOCS, PORTDATA - Use external minizip - Force space optimization (in place of -O2) when compiling with clang 3.4.1 to avoid build problems PR: 193886 Submitted by: lightside Approved by: avg@ (maintainer) Added: head/games/oolite/files/oolite.in (contents, props changed) head/games/oolite/files/patch-deps_mozilla-bug771281 (contents, props changed) head/games/oolite/files/patch-deps_mozilla_js_src_configure (contents, props changed) head/games/oolite/files/patch-deps_mozilla_js_src_configure.in (contents, props changed) head/games/oolite/files/patch-deps_mozilla_js_src_jsscript.h (contents, props changed) head/games/oolite/files/patch-deps_mozilla_js_src_jsstr.cpp (contents, props changed) head/games/oolite/files/patch-src_Core_OOMaths.h (contents, props changed) head/games/oolite/files/patch-src_Core_OOOXZManager.m (contents, props changed) head/games/oolite/files/patch-src_Core_OOPointMaths.h (contents, props changed) Deleted: head/games/oolite/files/patch-GNUmakefile head/games/oolite/files/patch-deps_Cross-platform-deps_mozilla_js_src_config_system-headers head/games/oolite/files/patch-src_Core_NSFileManagerOOExtensions.h head/games/oolite/files/pkg-message.in head/games/oolite/pkg-plist Modified: head/games/oolite/Makefile head/games/oolite/distinfo head/games/oolite/pkg-descr Modified: head/games/oolite/Makefile ============================================================================== --- head/games/oolite/Makefile Thu Oct 23 11:43:23 2014 (r371388) +++ head/games/oolite/Makefile Thu Oct 23 11:52:11 2014 (r371389) @@ -2,27 +2,32 @@ # $FreeBSD$ PORTNAME= oolite -PORTVERSION= 1.76 -PORTREVISION= 4 +PORTVERSION= 1.80 CATEGORIES= games gnustep -MASTER_SITES= ${MASTER_SITE_BERLIOS} http://jens.ayton.se/oolite/deps/:js -MASTER_SITE_SUBDIR= oolite-linux -DISTFILES= ${PORTNAME}-source-${PORTVERSION}${EXTRACT_SUFX} \ - firefox-4.0.source.js-only.tbz:js +MASTER_SITES= http://github.com/OoliteProject/oolite/releases/download/1.80b/ +DISTNAME= ${PORTNAME}-source-${PORTVERSION} DIST_SUBDIR= oolite -EXTRACT_ONLY= ${PORTNAME}-source-${PORTVERSION}${EXTRACT_SUFX} MAINTAINER= avg@icyb.net.ua COMMENT= Trade and combat space simulator, clone of Elite +LICENSE= CCBYNCSAv3 GPLv2 ZLIB +LICENSE_COMB= multi +LICENSE_NAME_CCBYNCSAv3= Creative Commons Attribution-NonCommercial-ShareAlike License version 3.0 +LICENSE_FILE_CCBYNCSAv3= ${WRKSRC}/Doc/LICENSE.TXT +LICENSE_PERMS_CCBYNCSAv3= dist-mirror pkg-mirror auto-accept + BUILD_DEPENDS= zip:${PORTSDIR}/archivers/zip \ ${LOCALBASE}/bin/unzip:${PORTSDIR}/archivers/unzip LIB_DEPENDS= libespeak.so:${PORTSDIR}/audio/espeak \ - libnspr4.so:${PORTSDIR}/devel/nspr + libnspr4.so:${PORTSDIR}/devel/nspr \ + libvorbisfile.so:${PORTSDIR}/audio/libvorbis \ + libpng15.so:${PORTSDIR}/graphics/png \ + libminizip.so:${PORTSDIR}/archivers/minizip -USES= gmake perl5 python:build tar:bzip2 +USES= gmake openal:al perl5 python:build tar:bzip2 USE_GL= yes -USE_SDL= mixer image gfx +USE_SDL= sdl USE_GNUSTEP= yes USE_GNUSTEP_BASE= yes USE_GNUSTEP_BUILD= yes @@ -30,23 +35,64 @@ USE_MAKEFILE= yes ALL_TARGET= release-deployment USE_PERL5= build -SUB_FILES= pkg-message -PKGMESSAGE= ${WRKDIR}/pkg-message - -WRKSRC= ${WRKDIR}/${PORTNAME}-source-${PORTVERSION} +SUB_FILES= oolite +SUB_LIST= GNUSTEP_SYSTEM_TOOLS="${GNUSTEP_SYSTEM_TOOLS}" -post-extract: - @(cd ${WRKSRC}/deps/Cross-platform-deps && \ - ${BZIP2_CMD} -dc ${_DISTDIR}/firefox-4.0.source.js-only.tbz | ${TAR} -xf - && \ - ${MV} mozilla-2.0 mozilla && \ - ${ECHO_CMD} 'http://jens.ayton.se/oolite/deps/firefox-4.0.source.js-only.tbz' > mozilla/current.url \ - ) +WRKSRC= ${WRKDIR}/${DISTNAME} +RELEASEDIR= ${WRKSRC}/oolite.app +DATADIR= ${GNUSTEP_LOCAL_APPS}/oolite.app +PORTDATA= Resources +PORTDOCS= *.pdf CHANGELOG.TXT contributors.txt + +PLIST_FILES+= bin/oolite %%DATADIR%%/oolite \ + share/applications/oolite.desktop \ + share/pixmaps/oolite-icon.png + +OPTIONS_DEFINE= DOCS + +.include + +.if ${OSVERSION} < 900014 +BUILD_DEPENDS+= clang34:${PORTSDIR}/lang/clang34 +CC= ${LOCALBASE}/bin/clang34 +CXX= ${LOCALBASE}/bin/clang++34 +CPP= ${LOCALBASE}/bin/clang-cpp34 +.elif exists(${CC}) +CCVERSION!= ${CC} --version +COMPILER_VERSION= ${CCVERSION:M[0-9].[0-9]*:C/([0-9]).?/\1/g} +COMPILER_IS_CLANG= ${CCVERSION:Mclang} + +# Check for LLVM/Clang v3.4.1 +.if ${COMPILER_IS_CLANG} && ${COMPILER_VERSION} == 341 +# Force to use optimization for size, because of speed optimization error(s) +# for ${WRKSRC}/src/Core/Scripting/OOJavaScriptEngine.m file +ADD_OBJCFLAGS= s|-std=c99|-std=c99 -Os| +.endif +.endif + +post-patch: .SILENT + ${REINPLACE_CMD} -e 's/GNUSTEP_USER_ROOT/GNUSTEP_LOCAL_ROOT/ ; \ + s/sdl-config/$${SDL_CONFIG}/ ; \ + s|-lstdc++|`$${SDL_CONFIG} --libs` -lstdc++| ; \ + s|:src/Core/MiniZip|| ; \ + s|-Isrc/Core/MiniZip|-I$${LOCALBASE}/include/minizip| ; \ + s|-lz|-lminizip| ; \ + /ioapi.c/d ; /unzip.c/d ; \ + s|/usr/X11R6|$${LOCALBASE}| ; ${ADD_OBJCFLAGS}' \ + ${WRKSRC}/GNUmakefile +# Conversion needed for unsigned int type for using isfinite function + ${REINPLACE_CMD} -e 's|isfinite(uValue)|isfinite((long double)uValue)|' \ + ${WRKSRC}/src/Core/Scripting/OOJSPlayerShip.m +# Change value of the SAVEDIR define + ${REINPLACE_CMD} -e 's|oolite-saves|\.oolite-saves|' \ + ${WRKSRC}/src/Core/NSFileManagerOOExtensions.h do-install: - ${MKDIR} ${STAGEDIR}${PREFIX}/GNUstep/Local/Applications - ${CP} -R ${WRKSRC}/oolite.app ${STAGEDIR}${PREFIX}/GNUstep/Local/Applications - ${STRIP_CMD} ${STAGEDIR}${PREFIX}/GNUstep/Local/Applications/oolite.app/oolite + cd ${RELEASEDIR} && ${COPYTREE_SHARE} "${PORTDATA}" ${STAGEDIR}${DATADIR} + ${INSTALL_SCRIPT} ${WRKDIR}/oolite ${STAGEDIR}${PREFIX}/bin + ${INSTALL_PROGRAM} ${RELEASEDIR}/oolite ${STAGEDIR}${DATADIR} ${INSTALL_DATA} ${WRKSRC}/installers/FreeDesktop/oolite.desktop ${STAGEDIR}${PREFIX}/share/applications ${INSTALL_DATA} ${WRKSRC}/installers/FreeDesktop/oolite-icon.png ${STAGEDIR}${PREFIX}/share/pixmaps + cd ${WRKSRC}/Doc && ${COPYTREE_SHARE} "${PORTDOCS}" ${STAGEDIR}${DOCSDIR} -.include +.include Modified: head/games/oolite/distinfo ============================================================================== --- head/games/oolite/distinfo Thu Oct 23 11:43:23 2014 (r371388) +++ head/games/oolite/distinfo Thu Oct 23 11:52:11 2014 (r371389) @@ -1,4 +1,2 @@ -SHA256 (oolite/oolite-source-1.76.tar.bz2) = cc5054432b4e1ad316ed5f52c88036cb8fc9732d42969225993f79ae3e93ff11 -SIZE (oolite/oolite-source-1.76.tar.bz2) = 44739650 -SHA256 (oolite/firefox-4.0.source.js-only.tbz) = 173b8798043612e50fa7618858c3a92d06f1f439d6d336a5700548cc5d75580a -SIZE (oolite/firefox-4.0.source.js-only.tbz) = 6198554 +SHA256 (oolite/oolite-source-1.80.tar.bz2) = a9bea03435cd7fd05b4519a44345e4ff4857a799aef79913733ddc5d7a537bdf +SIZE (oolite/oolite-source-1.80.tar.bz2) = 139397592 Added: head/games/oolite/files/oolite.in ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/games/oolite/files/oolite.in Thu Oct 23 11:52:11 2014 (r371389) @@ -0,0 +1,3 @@ +#!/bin/sh + +%%GNUSTEP_SYSTEM_TOOLS%%/openapp oolite "$@" Added: head/games/oolite/files/patch-deps_mozilla-bug771281 ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/games/oolite/files/patch-deps_mozilla-bug771281 Thu Oct 23 11:52:11 2014 (r371389) @@ -0,0 +1,1840 @@ +diff -ruN deps.orig/mozilla/js/src/shell/Makefile.in deps/mozilla/js/src/shell/Makefile.in +--- deps.orig/mozilla/js/src/shell/Makefile.in 2014-06-30 12:54:39.000000000 +0400 ++++ deps/mozilla/js/src/shell/Makefile.in 2014-09-23 07:59:03.000000000 +0400 +@@ -47,7 +47,6 @@ + PROGRAM = js$(BIN_SUFFIX) + CPPSRCS = \ + js.cpp \ +- jsworkers.cpp \ + $(NULL) + + DEFINES += -DEXPORT_JS_API +diff -ruN deps.orig/mozilla/js/src/shell/js.cpp deps/mozilla/js/src/shell/js.cpp +--- deps.orig/mozilla/js/src/shell/js.cpp 2014-06-30 12:54:39.000000000 +0400 ++++ deps/mozilla/js/src/shell/js.cpp 2014-09-23 07:59:03.000000000 +0400 +@@ -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; +@@ -1324,10 +1318,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; + } + +@@ -4164,10 +4154,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"; +@@ -5695,29 +5681,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 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 03:00:00.000000000 +0300 ++++ deps/mozilla/js/src/shell/jsworkers.cpp.rej 2014-09-23 07:59:03.000000000 +0400 +@@ -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. *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***