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