Bug 580128 - Some more compartment fixes. r=gal

This commit is contained in:
Jason Orendorff 2010-10-10 15:35:46 -07:00
Родитель 1bea2a0e1e
Коммит 01554ba3dc
3 изменённых файлов: 99 добавлений и 82 удалений

Просмотреть файл

@ -191,6 +191,7 @@ FILE *gErrFile = NULL;
FILE *gOutFile = NULL; FILE *gOutFile = NULL;
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
JSObject *gWorkers = NULL; JSObject *gWorkers = NULL;
js::workers::ThreadPool *gWorkerThreadPool = NULL;
#endif #endif
static JSBool reportWarnings = JS_TRUE; static JSBool reportWarnings = JS_TRUE;
@ -1142,8 +1143,8 @@ Quit(JSContext *cx, uintN argc, jsval *vp)
gQuitting = JS_TRUE; gQuitting = JS_TRUE;
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
if (gWorkers) if (gWorkerThreadPool)
js::workers::terminateAll(cx, gWorkers); js::workers::terminateAll(JS_GetRuntime(cx), gWorkerThreadPool);
#endif #endif
return JS_FALSE; return JS_FALSE;
} }
@ -3809,13 +3810,8 @@ CancelExecution(JSRuntime *rt)
if (gExitCode == 0) if (gExitCode == 0)
gExitCode = EXITCODE_TIMEOUT; gExitCode = EXITCODE_TIMEOUT;
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
if (gWorkers) { if (gWorkerThreadPool)
JSContext *cx = JS_NewContext(rt, 8192); js::workers::terminateAll(rt, gWorkerThreadPool);
if (cx) {
js::workers::terminateAll(cx, gWorkers);
JS_DestroyContextNoGC(cx);
}
}
#endif #endif
JS_TriggerAllOperationCallbacks(rt); JS_TriggerAllOperationCallbacks(rt);
@ -5305,7 +5301,7 @@ shell(JSContext *cx, int argc, char **argv, char **envp)
}; };
ShellWorkerHooks hooks; ShellWorkerHooks hooks;
if (!JS_AddNamedObjectRoot(cx, &gWorkers, "Workers") || if (!JS_AddNamedObjectRoot(cx, &gWorkers, "Workers") ||
!js::workers::init(cx, &hooks, glob, &gWorkers)) { (gWorkerThreadPool = js::workers::init(cx, &hooks, glob, &gWorkers)) == NULL) {
return 1; return 1;
} }
#endif #endif
@ -5313,7 +5309,7 @@ shell(JSContext *cx, int argc, char **argv, char **envp)
int result = ProcessArgs(cx, glob, argv, argc); int result = ProcessArgs(cx, glob, argv, argc);
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
js::workers::finish(cx, gWorkers); js::workers::finish(cx, gWorkerThreadPool);
JS_RemoveObjectRoot(cx, &gWorkers); JS_RemoveObjectRoot(cx, &gWorkers);
if (result == 0) if (result == 0)
result = gExitCode; result = gExitCode;

Просмотреть файл

@ -95,9 +95,7 @@ extern size_t gMaxStackSize;
*/ */
namespace js { namespace js {
namespace detail { namespace workers {
using js::workers::WorkerHooks;
template <class T, class AllocPolicy> template <class T, class AllocPolicy>
class Queue { class Queue {
@ -272,31 +270,44 @@ class MainQueue;
class Event class Event
{ {
protected: protected:
virtual ~Event() { JS_ASSERT(!data); }
WorkerParent *recipient; WorkerParent *recipient;
Worker *child; Worker *child;
JSString *data; uint64 *data;
size_t nbytes;
public: public:
enum Result { fail = JS_FALSE, ok = JS_TRUE, forwardToParent }; enum Result { fail = JS_FALSE, ok = JS_TRUE, forwardToParent };
virtual ~Event() {} virtual void destroy(JSContext *cx) {
JS_free(cx, data);
JSString *getData() const { return data; } #ifdef DEBUG
data = NULL;
#endif
delete this;
}
void setChildAndRecipient(Worker *aChild, WorkerParent *aRecipient) { void setChildAndRecipient(Worker *aChild, WorkerParent *aRecipient) {
child = aChild; child = aChild;
recipient = aRecipient; recipient = aRecipient;
} }
bool deserializeData(JSContext *cx, jsval *vp) {
return !!JS_ReadStructuredClone(cx, data, nbytes, vp);
}
virtual Result process(JSContext *cx) = 0; virtual Result process(JSContext *cx) = 0;
inline void trace(JSTracer *trc); inline void trace(JSTracer *trc);
template <class EventType> template <class EventType>
static EventType *createEvent(JSContext *cx, WorkerParent *recipient, Worker *child, static EventType *createEvent(JSContext *cx, WorkerParent *recipient, Worker *child,
JSString *data) jsval v)
{ {
if (data && !JS_MakeStringImmutable(cx, data)) uint64 *data;
size_t nbytes;
if (!JS_WriteStructuredClone(cx, v, &data, &nbytes))
return NULL; return NULL;
EventType *event = new EventType; EventType *event = new EventType;
@ -307,6 +318,7 @@ class Event
event->recipient = recipient; event->recipient = recipient;
event->child = child; event->child = child;
event->data = data; event->data = data;
event->nbytes = nbytes;
return event; return event;
} }
@ -323,8 +335,11 @@ class Event
return noHandler; return noHandler;
// Create event object. // Create event object.
jsval v;
if (!deserializeData(cx, &v))
return fail;
JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL); JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
if (!obj || !JS_DefineProperty(cx, obj, dataPropName, STRING_TO_JSVAL(data), NULL, NULL, 0)) if (!obj || !JS_DefineProperty(cx, obj, dataPropName, v, NULL, NULL, 0))
return fail; return fail;
// Call event handler. // Call event handler.
@ -345,12 +360,17 @@ class MainQueue : public EventQueue, public WorkerParent
explicit MainQueue(ThreadPool *tp) : threadPool(tp) {} explicit MainQueue(ThreadPool *tp) : threadPool(tp) {}
~MainQueue() { ~MainQueue() {
while (!queue.empty()) JS_ASSERT(queue.empty());
delete queue.pop();
} }
bool init() { return initThreadSafeQueue() && initWorkerParent(); } bool init() { return initThreadSafeQueue() && initWorkerParent(); }
void destroy(JSContext *cx) {
while (!queue.empty())
queue.pop()->destroy(cx);
delete this;
}
virtual JSLock *getLock() { return lock; } virtual JSLock *getLock() { return lock; }
virtual ThreadPool *getThreadPool() { return threadPool; } virtual ThreadPool *getThreadPool() { return threadPool; }
@ -377,11 +397,15 @@ class MainQueue : public EventQueue, public WorkerParent
result = event->process(cx); result = event->process(cx);
if (result == Event::forwardToParent) { if (result == Event::forwardToParent) {
// FIXME - pointlessly truncates the string to 8 bits // FIXME - pointlessly truncates the string to 8 bits
JSString *data = event->getData(); jsval data;
if (const char *s = data ? JS_GetStringBytesZ(cx, data) : NULL) const char *s;
if (event->deserializeData(cx, &data) &&
JSVAL_IS_STRING(data) &&
(s = JS_GetStringBytesZ(cx, JSVAL_TO_STRING(data)))) {
JS_ReportError(cx, "%s", s); JS_ReportError(cx, "%s", s);
else } else {
JS_ReportOutOfMemory(cx); JS_ReportOutOfMemory(cx);
}
result = Event::fail; result = Event::fail;
} }
if (result == Event::fail && continueOnError) { if (result == Event::fail && continueOnError) {
@ -392,7 +416,7 @@ class MainQueue : public EventQueue, public WorkerParent
} }
JS_ACQUIRE_LOCK(lock); JS_ACQUIRE_LOCK(lock);
drop(event); drop(event);
delete event; event->destroy(cx);
if (result != Event::ok) if (result != Event::ok)
return false; return false;
} }
@ -478,7 +502,7 @@ class ThreadPool
JS_ASSERT(!mq && !wq); JS_ASSERT(!mq && !wq);
mq = new MainQueue(this); mq = new MainQueue(this);
if (!mq || !mq->init()) { if (!mq || !mq->init()) {
delete mq; mq->destroy(cx);
mq = NULL; mq = NULL;
return false; return false;
} }
@ -486,7 +510,7 @@ class ThreadPool
if (!wq || !wq->initThreadSafeQueue()) { if (!wq || !wq->initThreadSafeQueue()) {
delete wq; delete wq;
wq = NULL; wq = NULL;
delete mq; mq->destroy(cx);
mq = NULL; mq = NULL;
return false; return false;
} }
@ -496,7 +520,7 @@ class ThreadPool
threads[i] = PR_CreateThread(PR_USER_THREAD, start, wq, PR_PRIORITY_NORMAL, threads[i] = PR_CreateThread(PR_USER_THREAD, start, wq, PR_PRIORITY_NORMAL,
PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
if (!threads[i]) { if (!threads[i]) {
shutdown(); shutdown(cx);
ok = false; ok = false;
break; break;
} }
@ -504,14 +528,15 @@ class ThreadPool
return ok; return ok;
} }
void terminateAll(JSContext *cx) { void terminateAll(JSRuntime *rt) {
// See comment about JS_ATOMIC_SET in the implementation of // See comment about JS_ATOMIC_SET in the implementation of
// JS_TriggerOperationCallback. // JS_TriggerOperationCallback.
JS_ATOMIC_SET(&terminating, 1); JS_ATOMIC_SET(&terminating, 1);
JS_TriggerAllOperationCallbacks(JS_GetRuntime(cx)); JS_TriggerAllOperationCallbacks(rt);
} }
void shutdown() { /* This context is used only to free memory. */
void shutdown(JSContext *cx) {
wq->close(); wq->close();
for (int i = 0; i < threadCount; i++) { for (int i = 0; i < threadCount; i++) {
if (threads[i]) { if (threads[i]) {
@ -524,7 +549,7 @@ class ThreadPool
wq = NULL; wq = NULL;
mq->disposeChildren(); mq->disposeChildren();
delete mq; mq->destroy(cx);
mq = NULL; mq = NULL;
terminating = 0; terminating = 0;
} }
@ -705,7 +730,7 @@ class Worker : public WorkerParent
void terminateSelf() { void terminateSelf() {
terminated = true; terminated = true;
while (!events.empty()) while (!events.empty())
delete events.pop(); events.pop()->destroy(context);
// Tell the children to shut down too. An arbitrarily silly amount of // Tell the children to shut down too. An arbitrarily silly amount of
// processing could happen before the whole tree is terminated; but // processing could happen before the whole tree is terminated; but
@ -724,7 +749,7 @@ class Worker : public WorkerParent
void dispose() { void dispose() {
JS_ASSERT(!current); JS_ASSERT(!current);
while (!events.empty()) while (!events.empty())
delete events.pop(); events.pop()->destroy(context);
if (lock) { if (lock) {
JS_DESTROY_LOCK(lock); JS_DESTROY_LOCK(lock);
lock = NULL; lock = NULL;
@ -796,7 +821,7 @@ class Worker : public WorkerParent
return false; return false;
WorkerParent *parent = threadPool->getMainQueue(); WorkerParent *parent = threadPool->getMainQueue();
if (!JS_SetReservedSlot(cx, ctor, 0, PRIVATE_TO_JSVAL(parent))) { if (!JS_SetReservedSlot(cx, ctor, 0, PRIVATE_TO_JSVAL(parent))) {
threadPool->shutdown(); threadPool->shutdown(cx);
return false; return false;
} }
*p = parent; *p = parent;
@ -826,11 +851,12 @@ class Worker : public WorkerParent
static JSFunctionSpec jsMethods[3]; static JSFunctionSpec jsMethods[3];
static JSFunctionSpec jsStaticMethod[2]; static JSFunctionSpec jsStaticMethod[2];
static JSBool initWorkers(JSContext *cx, WorkerHooks *hooks, JSObject *global, JSObject **objp) { static ThreadPool *initWorkers(JSContext *cx, WorkerHooks *hooks, JSObject *global,
JSObject **objp) {
// Create the ThreadPool object and its JSObject wrapper. // Create the ThreadPool object and its JSObject wrapper.
ThreadPool *threadPool = ThreadPool::create(cx, hooks); ThreadPool *threadPool = ThreadPool::create(cx, hooks);
if (!threadPool) if (!threadPool)
return false; return NULL;
// Root the ThreadPool JSObject early. // Root the ThreadPool JSObject early.
*objp = threadPool->asObject(); *objp = threadPool->asObject();
@ -840,15 +866,15 @@ class Worker : public WorkerParent
jsConstruct, 1, jsConstruct, 1,
NULL, jsMethods, NULL, NULL); NULL, jsMethods, NULL, NULL);
if (!proto) if (!proto)
return false; return NULL;
// Stash a pointer to the ThreadPool in constructor reserved slot 1. // Stash a pointer to the ThreadPool in constructor reserved slot 1.
// It will be used later when lazily creating the MainQueue. // It will be used later when lazily creating the MainQueue.
JSObject *ctor = JS_GetConstructor(cx, proto); JSObject *ctor = JS_GetConstructor(cx, proto);
if (!JS_SetReservedSlot(cx, ctor, 1, PRIVATE_TO_JSVAL(threadPool))) if (!JS_SetReservedSlot(cx, ctor, 1, PRIVATE_TO_JSVAL(threadPool)))
return false; return NULL;
return true; return threadPool;
} }
}; };
@ -856,11 +882,15 @@ class InitEvent : public Event
{ {
public: public:
static InitEvent *create(JSContext *cx, Worker *worker, JSString *scriptName) { static InitEvent *create(JSContext *cx, Worker *worker, JSString *scriptName) {
return createEvent<InitEvent>(cx, worker, worker, scriptName); return createEvent<InitEvent>(cx, worker, worker, STRING_TO_JSVAL(scriptName));
} }
Result process(JSContext *cx) { Result process(JSContext *cx) {
const char *filename = JS_GetStringBytesZ(cx, data); jsval s;
if (!deserializeData(cx, &s))
return fail;
JS_ASSERT(JSVAL_IS_STRING(s));
const char *filename = JS_GetStringBytesZ(cx, JSVAL_TO_STRING(s));
if (!filename) if (!filename)
return fail; return fail;
@ -878,7 +908,7 @@ class InitEvent : public Event
class DownMessageEvent : public Event class DownMessageEvent : public Event
{ {
public: public:
static DownMessageEvent *create(JSContext *cx, Worker *child, JSString *data) { static DownMessageEvent *create(JSContext *cx, Worker *child, jsval data) {
return createEvent<DownMessageEvent>(cx, child, child, data); return createEvent<DownMessageEvent>(cx, child, child, data);
} }
@ -890,7 +920,7 @@ class DownMessageEvent : public Event
class UpMessageEvent : public Event class UpMessageEvent : public Event
{ {
public: public:
static UpMessageEvent *create(JSContext *cx, Worker *child, JSString *data) { static UpMessageEvent *create(JSContext *cx, Worker *child, jsval data) {
return createEvent<UpMessageEvent>(cx, child->getParent(), child, data); return createEvent<UpMessageEvent>(cx, child->getParent(), child, data);
} }
@ -925,7 +955,8 @@ class ErrorEvent : public Event
return NULL; return NULL;
} }
} }
return createEvent<ErrorEvent>(cx, child->getParent(), child, data); return createEvent<ErrorEvent>(cx, child->getParent(), child,
data ? STRING_TO_JSVAL(data) : JSVAL_VOID);
} }
Result process(JSContext *cx) { Result process(JSContext *cx) {
@ -933,10 +964,10 @@ class ErrorEvent : public Event
} }
}; };
} /* namespace detail */ } /* namespace workers */
} /* namespace js */ } /* namespace js */
using namespace js::detail; using namespace js::workers;
void void
WorkerParent::disposeChildren() WorkerParent::disposeChildren()
@ -1057,7 +1088,7 @@ Worker::create(JSContext *parentcx, WorkerParent *parent, JSString *scriptName,
if (!event) if (!event)
return NULL; return NULL;
if (!w->events.push(event) || !w->threadPool->getWorkerQueue()->post(w)) { if (!w->events.push(event) || !w->threadPool->getWorkerQueue()->post(w)) {
delete event; event->destroy(parentcx);
JS_ReportOutOfMemory(parentcx); JS_ReportOutOfMemory(parentcx);
w->dispose(); w->dispose();
return NULL; return NULL;
@ -1102,7 +1133,7 @@ Worker::processOneEvent()
Event *err = ErrorEvent::create(context, this); Event *err = ErrorEvent::create(context, this);
if (err && !parent->post(err)) { if (err && !parent->post(err)) {
JS_ReportOutOfMemory(context); JS_ReportOutOfMemory(context);
delete err; err->destroy(context);
err = NULL; err = NULL;
} }
if (!err) { if (!err) {
@ -1110,6 +1141,7 @@ Worker::processOneEvent()
} }
} }
event->destroy(context);
JS_ClearContextThread(context); JS_ClearContextThread(context);
{ {
@ -1121,7 +1153,6 @@ Worker::processOneEvent()
JS_ReportOutOfMemory(context); JS_ReportOutOfMemory(context);
} }
} }
delete event;
} }
JSBool JSBool
@ -1138,16 +1169,14 @@ Worker::jsPostMessageToParent(JSContext *cx, uintN argc, jsval *vp)
return false; return false;
} }
JSString *message = JS_ValueToString(cx, argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID); jsval data = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
if (!message) Event *event = UpMessageEvent::create(cx, w, data);
return false;
Event *event = UpMessageEvent::create(cx, w, message);
if (!event) if (!event)
return false; return false;
if (!w->parent->post(event)) { if (!w->parent->post(event)) {
delete event; event->destroy(cx);
JS_ReportOutOfMemory(cx); JS_ReportOutOfMemory(cx);
return false;
} }
JS_SET_RVAL(cx, vp, JSVAL_VOID); JS_SET_RVAL(cx, vp, JSVAL_VOID);
return true; return true;
@ -1166,11 +1195,8 @@ Worker::jsPostMessageToChild(JSContext *cx, uintN argc, jsval *vp)
return false; return false;
} }
JSString *message = JS_ValueToString(cx, argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID); jsval data = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
if (!message) Event *event = DownMessageEvent::create(cx, w, data);
return false;
Event *event = DownMessageEvent::create(cx, w, message);
if (!event) if (!event)
return false; return false;
if (!w->post(event)) { if (!w->post(event)) {
@ -1205,8 +1231,6 @@ Event::trace(JSTracer *trc)
recipient->trace(trc); recipient->trace(trc);
if (child) if (child)
JS_CALL_OBJECT_TRACER(trc, child->asObject(), "worker"); JS_CALL_OBJECT_TRACER(trc, child->asObject(), "worker");
if (data)
JS_CALL_STRING_TRACER(trc, data, "worker event data");
} }
JSClass ThreadPool::jsClass = { JSClass ThreadPool::jsClass = {
@ -1231,29 +1255,24 @@ JSFunctionSpec Worker::jsMethods[3] = {
JS_FS_END JS_FS_END
}; };
JSBool ThreadPool *
js::workers::init(JSContext *cx, WorkerHooks *hooks, JSObject *global, JSObject **rootp) js::workers::init(JSContext *cx, WorkerHooks *hooks, JSObject *global, JSObject **rootp)
{ {
return Worker::initWorkers(cx, hooks, global, rootp); return Worker::initWorkers(cx, hooks, global, rootp);
} }
void void
js::workers::terminateAll(JSContext *cx, JSObject *workersobj) js::workers::terminateAll(JSRuntime *rt, ThreadPool *tp)
{ {
ThreadPool::unwrap(cx, workersobj)->terminateAll(cx); tp->terminateAll(rt);
} }
void void
js::workers::finish(JSContext *cx, JSObject *workersobj) js::workers::finish(JSContext *cx, ThreadPool *tp)
{ {
ThreadPool *threadPool = ThreadPool::unwrap(cx, workersobj); if (MainQueue *mq = tp->getMainQueue()) {
if (MainQueue *mq = threadPool->getMainQueue()) { JS_ALWAYS_TRUE(mq->mainThreadWork(cx, true));
#ifdef DEBUG tp->shutdown(cx);
JSBool ok =
#endif
mq->mainThreadWork(cx, true);
JS_ASSERT(ok);
threadPool->shutdown();
} }
} }

Просмотреть файл

@ -52,6 +52,8 @@
*/ */
namespace js { namespace js {
namespace workers { namespace workers {
class ThreadPool;
class WorkerHooks { class WorkerHooks {
public: public:
virtual JSObject *newGlobalObject(JSContext *cx) = 0; virtual JSObject *newGlobalObject(JSContext *cx) = 0;
@ -63,17 +65,17 @@ namespace js {
* Requires request. rootp must point to a GC root. * Requires request. rootp must point to a GC root.
* *
* On success, *rootp receives a pointer to an object, and init returns * On success, *rootp receives a pointer to an object, and init returns
* true. The caller must keep the object rooted and must pass it to * a non-null value. The caller must keep the object rooted and must
* js::workers::finish later. * pass it to js::workers::finish later.
*/ */
JSBool init(JSContext *cx, WorkerHooks *hooks, JSObject *global, JSObject **rootp); ThreadPool *init(JSContext *cx, WorkerHooks *hooks, JSObject *global, JSObject **rootp);
/* Asynchronously signal for all workers to terminate. /* Asynchronously signal for all workers to terminate.
* *
* Call this before calling finish() to shut down without waiting for * Call this before calling finish() to shut down without waiting for
* all messages to be proceesed. * all messages to be proceesed.
*/ */
void terminateAll(JSContext *cx, JSObject *workersobj); void terminateAll(JSRuntime *rt, ThreadPool *tp);
/* /*
* Finish running any workers, shut down the thread pool, and free all * Finish running any workers, shut down the thread pool, and free all
@ -82,7 +84,7 @@ namespace js {
* *
* Requires request. * Requires request.
*/ */
void finish(JSContext *cx, JSObject *workersobj); void finish(JSContext *cx, ThreadPool *tp);
} }
} }