Lines 84-1471
Link Here
|
84 |
#ifdef JSDEBUGGER |
84 |
#ifdef JSDEBUGGER |
85 |
if (jsdc) { |
85 |
if (jsdc) { |
86 |
#ifdef JSDEBUGGER_C_UI |
86 |
#ifdef JSDEBUGGER_C_UI |
87 |
diff -ruN deps.orig/mozilla/js/src/shell/jsworkers.cpp.rej deps/mozilla/js/src/shell/jsworkers.cpp.rej |
|
|
88 |
--- deps.orig/mozilla/js/src/shell/jsworkers.cpp.rej 1970-01-01 00:00:00 UTC |
89 |
+++ deps/mozilla/js/src/shell/jsworkers.cpp.rej |
90 |
@@ -0,0 +1,1281 @@ |
91 |
+@@ -1,1280 +0,0 @@ |
92 |
+-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
93 |
+- * vim: set ts=8 sw=4 et tw=99: |
94 |
+- * |
95 |
+- * ***** BEGIN LICENSE BLOCK ***** |
96 |
+- * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
97 |
+- * |
98 |
+- * The contents of this file are subject to the Mozilla Public License Version |
99 |
+- * 1.1 (the "License"); you may not use this file except in compliance with |
100 |
+- * the License. You may obtain a copy of the License at |
101 |
+- * http://www.mozilla.org/MPL/ |
102 |
+- * |
103 |
+- * Software distributed under the License is distributed on an "AS IS" basis, |
104 |
+- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
105 |
+- * for the specific language governing rights and limitations under the |
106 |
+- * License. |
107 |
+- * |
108 |
+- * The Original Code is JavaScript shell workers. |
109 |
+- * |
110 |
+- * The Initial Developer of the Original Code is |
111 |
+- * Mozilla Corporation. |
112 |
+- * Portions created by the Initial Developer are Copyright (C) 2010 |
113 |
+- * the Initial Developer. All Rights Reserved. |
114 |
+- * |
115 |
+- * Contributor(s): |
116 |
+- * Jason Orendorff <jorendorff@mozilla.com> |
117 |
+- * |
118 |
+- * Alternatively, the contents of this file may be used under the terms of |
119 |
+- * either of the GNU General Public License Version 2 or later (the "GPL"), |
120 |
+- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
121 |
+- * in which case the provisions of the GPL or the LGPL are applicable instead |
122 |
+- * of those above. If you wish to allow use of your version of this file only |
123 |
+- * under the terms of either the GPL or the LGPL, and not to allow others to |
124 |
+- * use your version of this file under the terms of the MPL, indicate your |
125 |
+- * decision by deleting the provisions above and replace them with the notice |
126 |
+- * and other provisions required by the GPL or the LGPL. If you do not delete |
127 |
+- * the provisions above, a recipient may use your version of this file under |
128 |
+- * the terms of any one of the MPL, the GPL or the LGPL. |
129 |
+- * |
130 |
+- * ***** END LICENSE BLOCK ***** */ |
131 |
+- |
132 |
+-#ifdef JS_THREADSAFE |
133 |
+- |
134 |
+-#include <algorithm> |
135 |
+-#include <string.h> |
136 |
+-#include "prthread.h" |
137 |
+-#include "prlock.h" |
138 |
+-#include "prcvar.h" |
139 |
+-#include "jsapi.h" |
140 |
+-#include "jscntxt.h" |
141 |
+-#include "jshashtable.h" |
142 |
+-#include "jsstdint.h" |
143 |
+-#include "jslock.h" |
144 |
+-#include "jsvector.h" |
145 |
+-#include "jsworkers.h" |
146 |
+- |
147 |
+-extern size_t gMaxStackSize; |
148 |
+- |
149 |
+-/* |
150 |
+- * JavaScript shell workers. |
151 |
+- * |
152 |
+- * == Object lifetime rules == |
153 |
+- * |
154 |
+- * - The ThreadPool lasts from init() to finish(). |
155 |
+- * |
156 |
+- * - The ThreadPool owns the MainQueue and the WorkerQueue. Those live from |
157 |
+- * the time the first Worker is created until finish(). |
158 |
+- * |
159 |
+- * - Each JS Worker object has the same lifetime as the corresponding C++ |
160 |
+- * Worker object. A Worker is live if (a) the Worker JSObject is still |
161 |
+- * live; (b) the Worker has an incoming event pending or running; (c) it |
162 |
+- * has sent an outgoing event to its parent that is still pending; or (d) |
163 |
+- * it has any live child Workers. |
164 |
+- * |
165 |
+- * - finish() continues to wait for events until all threads are idle. |
166 |
+- * |
167 |
+- * Event objects, however, are basically C++-only. The JS Event objects are |
168 |
+- * just plain old JSObjects. They don't keep anything alive. |
169 |
+- * |
170 |
+- * == Locking scheme == |
171 |
+- * |
172 |
+- * When mixing mutexes and the JSAPI request model, there are two choices: |
173 |
+- * |
174 |
+- * - Always nest the mutexes in requests. Since threads in requests are not |
175 |
+- * supposed to block, this means the mutexes must be only briefly held. |
176 |
+- * |
177 |
+- * - Never nest the mutexes in requests. Since this allows threads to race |
178 |
+- * with the GC, trace() methods must go through the mutexes just like |
179 |
+- * everyone else. |
180 |
+- * |
181 |
+- * This code uses the latter approach for all locks. |
182 |
+- * |
183 |
+- * In one case, a thread holding a Worker's mutex can acquire the mutex of one |
184 |
+- * of its child Workers. See Worker::terminateSelf. (This can't deadlock because |
185 |
+- * the parent-child relationship is a partial order.) |
186 |
+- */ |
187 |
+- |
188 |
+-namespace js { |
189 |
+-namespace workers { |
190 |
+- |
191 |
+-template <class T, class AllocPolicy> |
192 |
+-class Queue { |
193 |
+- private: |
194 |
+- typedef Vector<T, 4, AllocPolicy> Vec; |
195 |
+- Vec v1; |
196 |
+- Vec v2; |
197 |
+- Vec *front; |
198 |
+- Vec *back; |
199 |
+- |
200 |
+- // Queue is not copyable. |
201 |
+- Queue(const Queue &); |
202 |
+- Queue & operator=(const Queue &); |
203 |
+- |
204 |
+- public: |
205 |
+- Queue() : front(&v1), back(&v2) {} |
206 |
+- bool push(T t) { return back->append(t); } |
207 |
+- bool empty() { return front->empty() && back->empty(); } |
208 |
+- |
209 |
+- T pop() { |
210 |
+- if (front->empty()) { |
211 |
+- std::reverse(back->begin(), back->end()); |
212 |
+- Vec *tmp = front; |
213 |
+- front = back; |
214 |
+- back = tmp; |
215 |
+- } |
216 |
+- T item = front->back(); |
217 |
+- front->popBack(); |
218 |
+- return item; |
219 |
+- } |
220 |
+- |
221 |
+- void clear() { |
222 |
+- v1.clear(); |
223 |
+- v2.clear(); |
224 |
+- } |
225 |
+- |
226 |
+- void trace(JSTracer *trc) { |
227 |
+- for (T *p = v1.begin(); p != v1.end(); p++) |
228 |
+- (*p)->trace(trc); |
229 |
+- for (T *p = v2.begin(); p != v2.end(); p++) |
230 |
+- (*p)->trace(trc); |
231 |
+- } |
232 |
+-}; |
233 |
+- |
234 |
+-class Event; |
235 |
+-class ThreadPool; |
236 |
+-class Worker; |
237 |
+- |
238 |
+-class WorkerParent { |
239 |
+- protected: |
240 |
+- typedef HashSet<Worker *, DefaultHasher<Worker *>, SystemAllocPolicy> ChildSet; |
241 |
+- ChildSet children; |
242 |
+- |
243 |
+- bool initWorkerParent() { return children.init(8); } |
244 |
+- |
245 |
+- public: |
246 |
+- virtual JSLock *getLock() = 0; |
247 |
+- virtual ThreadPool *getThreadPool() = 0; |
248 |
+- virtual bool post(Event *item) = 0; // false on OOM or queue closed |
249 |
+- virtual void trace(JSTracer *trc) = 0; |
250 |
+- |
251 |
+- bool addChild(Worker *w) { |
252 |
+- AutoLock hold(getLock()); |
253 |
+- return children.put(w) != NULL; |
254 |
+- } |
255 |
+- |
256 |
+- // This must be called only from GC or when all threads are shut down. It |
257 |
+- // does not bother with locking. |
258 |
+- void removeChild(Worker *w) { |
259 |
+- ChildSet::Ptr p = children.lookup(w); |
260 |
+- JS_ASSERT(p); |
261 |
+- children.remove(p); |
262 |
+- } |
263 |
+- |
264 |
+- void disposeChildren(); |
265 |
+-}; |
266 |
+- |
267 |
+-template <class T> |
268 |
+-class ThreadSafeQueue |
269 |
+-{ |
270 |
+- protected: |
271 |
+- Queue<T, SystemAllocPolicy> queue; |
272 |
+- JSLock *lock; |
273 |
+- PRCondVar *condvar; |
274 |
+- bool closed; |
275 |
+- |
276 |
+- private: |
277 |
+- Vector<T, 8, SystemAllocPolicy> busy; |
278 |
+- |
279 |
+- protected: |
280 |
+- ThreadSafeQueue() : lock(NULL), condvar(NULL), closed(false) {} |
281 |
+- |
282 |
+- ~ThreadSafeQueue() { |
283 |
+- if (condvar) |
284 |
+- JS_DESTROY_CONDVAR(condvar); |
285 |
+- if (lock) |
286 |
+- JS_DESTROY_LOCK(lock); |
287 |
+- } |
288 |
+- |
289 |
+- // Called by take() with the lock held. |
290 |
+- virtual bool shouldStop() { return closed; } |
291 |
+- |
292 |
+- public: |
293 |
+- bool initThreadSafeQueue() { |
294 |
+- JS_ASSERT(!lock); |
295 |
+- JS_ASSERT(!condvar); |
296 |
+- return (lock = JS_NEW_LOCK()) && (condvar = JS_NEW_CONDVAR(lock)); |
297 |
+- } |
298 |
+- |
299 |
+- bool post(T t) { |
300 |
+- AutoLock hold(lock); |
301 |
+- if (closed) |
302 |
+- return false; |
303 |
+- if (queue.empty()) |
304 |
+- JS_NOTIFY_ALL_CONDVAR(condvar); |
305 |
+- return queue.push(t); |
306 |
+- } |
307 |
+- |
308 |
+- void close() { |
309 |
+- AutoLock hold(lock); |
310 |
+- closed = true; |
311 |
+- queue.clear(); |
312 |
+- JS_NOTIFY_ALL_CONDVAR(condvar); |
313 |
+- } |
314 |
+- |
315 |
+- // The caller must hold the lock. |
316 |
+- bool take(T *t) { |
317 |
+- while (queue.empty()) { |
318 |
+- if (shouldStop()) |
319 |
+- return false; |
320 |
+- JS_WAIT_CONDVAR(condvar, JS_NO_TIMEOUT); |
321 |
+- } |
322 |
+- *t = queue.pop(); |
323 |
+- busy.append(*t); |
324 |
+- return true; |
325 |
+- } |
326 |
+- |
327 |
+- // The caller must hold the lock. |
328 |
+- void drop(T item) { |
329 |
+- for (T *p = busy.begin(); p != busy.end(); p++) { |
330 |
+- if (*p == item) { |
331 |
+- *p = busy.back(); |
332 |
+- busy.popBack(); |
333 |
+- return; |
334 |
+- } |
335 |
+- } |
336 |
+- JS_NOT_REACHED("removeBusy"); |
337 |
+- } |
338 |
+- |
339 |
+- bool lockedIsIdle() { return busy.empty() && queue.empty(); } |
340 |
+- |
341 |
+- bool isIdle() { |
342 |
+- AutoLock hold(lock); |
343 |
+- return lockedIsIdle(); |
344 |
+- } |
345 |
+- |
346 |
+- void wake() { |
347 |
+- AutoLock hold(lock); |
348 |
+- JS_NOTIFY_ALL_CONDVAR(condvar); |
349 |
+- } |
350 |
+- |
351 |
+- void trace(JSTracer *trc) { |
352 |
+- AutoLock hold(lock); |
353 |
+- for (T *p = busy.begin(); p != busy.end(); p++) |
354 |
+- (*p)->trace(trc); |
355 |
+- queue.trace(trc); |
356 |
+- } |
357 |
+-}; |
358 |
+- |
359 |
+-class MainQueue; |
360 |
+- |
361 |
+-class Event |
362 |
+-{ |
363 |
+- protected: |
364 |
+- virtual ~Event() { JS_ASSERT(!data); } |
365 |
+- |
366 |
+- WorkerParent *recipient; |
367 |
+- Worker *child; |
368 |
+- uint64 *data; |
369 |
+- size_t nbytes; |
370 |
+- |
371 |
+- public: |
372 |
+- enum Result { fail = JS_FALSE, ok = JS_TRUE, forwardToParent }; |
373 |
+- |
374 |
+- virtual void destroy(JSContext *cx) { |
375 |
+- JS_free(cx, data); |
376 |
+-#ifdef DEBUG |
377 |
+- data = NULL; |
378 |
+-#endif |
379 |
+- delete this; |
380 |
+- } |
381 |
+- |
382 |
+- void setChildAndRecipient(Worker *aChild, WorkerParent *aRecipient) { |
383 |
+- child = aChild; |
384 |
+- recipient = aRecipient; |
385 |
+- } |
386 |
+- |
387 |
+- bool deserializeData(JSContext *cx, jsval *vp) { |
388 |
+- return !!JS_ReadStructuredClone(cx, data, nbytes, JS_STRUCTURED_CLONE_VERSION, vp, |
389 |
+- NULL, NULL); |
390 |
+- } |
391 |
+- |
392 |
+- virtual Result process(JSContext *cx) = 0; |
393 |
+- |
394 |
+- inline void trace(JSTracer *trc); |
395 |
+- |
396 |
+- template <class EventType> |
397 |
+- static EventType *createEvent(JSContext *cx, WorkerParent *recipient, Worker *child, |
398 |
+- jsval v) |
399 |
+- { |
400 |
+- uint64 *data; |
401 |
+- size_t nbytes; |
402 |
+- if (!JS_WriteStructuredClone(cx, v, &data, &nbytes, NULL, NULL)) |
403 |
+- return NULL; |
404 |
+- |
405 |
+- EventType *event = new EventType; |
406 |
+- if (!event) { |
407 |
+- JS_ReportOutOfMemory(cx); |
408 |
+- return NULL; |
409 |
+- } |
410 |
+- event->recipient = recipient; |
411 |
+- event->child = child; |
412 |
+- event->data = data; |
413 |
+- event->nbytes = nbytes; |
414 |
+- return event; |
415 |
+- } |
416 |
+- |
417 |
+- Result dispatch(JSContext *cx, JSObject *thisobj, const char *dataPropName, |
418 |
+- const char *methodName, Result noHandler) |
419 |
+- { |
420 |
+- if (!data) |
421 |
+- return fail; |
422 |
+- |
423 |
+- JSBool found; |
424 |
+- if (!JS_HasProperty(cx, thisobj, methodName, &found)) |
425 |
+- return fail; |
426 |
+- if (!found) |
427 |
+- return noHandler; |
428 |
+- |
429 |
+- // Create event object. |
430 |
+- jsval v; |
431 |
+- if (!deserializeData(cx, &v)) |
432 |
+- return fail; |
433 |
+- JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL); |
434 |
+- if (!obj || !JS_DefineProperty(cx, obj, dataPropName, v, NULL, NULL, 0)) |
435 |
+- return fail; |
436 |
+- |
437 |
+- // Call event handler. |
438 |
+- jsval argv[1] = { OBJECT_TO_JSVAL(obj) }; |
439 |
+- jsval rval = JSVAL_VOID; |
440 |
+- return Result(JS_CallFunctionName(cx, thisobj, methodName, 1, argv, &rval)); |
441 |
+- } |
442 |
+-}; |
443 |
+- |
444 |
+-typedef ThreadSafeQueue<Event *> EventQueue; |
445 |
+- |
446 |
+-class MainQueue : public EventQueue, public WorkerParent |
447 |
+-{ |
448 |
+- private: |
449 |
+- ThreadPool *threadPool; |
450 |
+- |
451 |
+- public: |
452 |
+- explicit MainQueue(ThreadPool *tp) : threadPool(tp) {} |
453 |
+- |
454 |
+- ~MainQueue() { |
455 |
+- JS_ASSERT(queue.empty()); |
456 |
+- } |
457 |
+- |
458 |
+- bool init() { return initThreadSafeQueue() && initWorkerParent(); } |
459 |
+- |
460 |
+- void destroy(JSContext *cx) { |
461 |
+- while (!queue.empty()) |
462 |
+- queue.pop()->destroy(cx); |
463 |
+- delete this; |
464 |
+- } |
465 |
+- |
466 |
+- virtual JSLock *getLock() { return lock; } |
467 |
+- virtual ThreadPool *getThreadPool() { return threadPool; } |
468 |
+- |
469 |
+- protected: |
470 |
+- virtual bool shouldStop(); |
471 |
+- |
472 |
+- public: |
473 |
+- virtual bool post(Event *event) { return EventQueue::post(event); } |
474 |
+- |
475 |
+- virtual void trace(JSTracer *trc); |
476 |
+- |
477 |
+- void traceChildren(JSTracer *trc) { EventQueue::trace(trc); } |
478 |
+- |
479 |
+- JSBool mainThreadWork(JSContext *cx, bool continueOnError) { |
480 |
+- JSAutoSuspendRequest suspend(cx); |
481 |
+- AutoLock hold(lock); |
482 |
+- |
483 |
+- Event *event; |
484 |
+- while (take(&event)) { |
485 |
+- JS_RELEASE_LOCK(lock); |
486 |
+- Event::Result result; |
487 |
+- { |
488 |
+- JSAutoRequest req(cx); |
489 |
+- result = event->process(cx); |
490 |
+- if (result == Event::forwardToParent) { |
491 |
+- // FIXME - pointlessly truncates the string to 8 bits |
492 |
+- jsval data; |
493 |
+- JSAutoByteString bytes; |
494 |
+- if (event->deserializeData(cx, &data) && |
495 |
+- JSVAL_IS_STRING(data) && |
496 |
+- bytes.encode(cx, JSVAL_TO_STRING(data))) { |
497 |
+- JS_ReportError(cx, "%s", bytes.ptr()); |
498 |
+- } else { |
499 |
+- JS_ReportOutOfMemory(cx); |
500 |
+- } |
501 |
+- result = Event::fail; |
502 |
+- } |
503 |
+- if (result == Event::fail && continueOnError) { |
504 |
+- if (JS_IsExceptionPending(cx) && !JS_ReportPendingException(cx)) |
505 |
+- JS_ClearPendingException(cx); |
506 |
+- result = Event::ok; |
507 |
+- } |
508 |
+- } |
509 |
+- JS_ACQUIRE_LOCK(lock); |
510 |
+- drop(event); |
511 |
+- event->destroy(cx); |
512 |
+- if (result != Event::ok) |
513 |
+- return false; |
514 |
+- } |
515 |
+- return true; |
516 |
+- } |
517 |
+-}; |
518 |
+- |
519 |
+-/* |
520 |
+- * A queue of workers. |
521 |
+- * |
522 |
+- * We keep a queue of workers with pending events, rather than a queue of |
523 |
+- * events, so that two threads won't try to run a Worker at the same time. |
524 |
+- */ |
525 |
+-class WorkerQueue : public ThreadSafeQueue<Worker *> |
526 |
+-{ |
527 |
+- private: |
528 |
+- MainQueue *main; |
529 |
+- |
530 |
+- public: |
531 |
+- explicit WorkerQueue(MainQueue *main) : main(main) {} |
532 |
+- |
533 |
+- void work(); |
534 |
+-}; |
535 |
+- |
536 |
+-/* The top-level object that owns everything else. */ |
537 |
+-class ThreadPool |
538 |
+-{ |
539 |
+- private: |
540 |
+- enum { threadCount = 6 }; |
541 |
+- |
542 |
+- JSObject *obj; |
543 |
+- WorkerHooks *hooks; |
544 |
+- MainQueue *mq; |
545 |
+- WorkerQueue *wq; |
546 |
+- PRThread *threads[threadCount]; |
547 |
+- int32_t terminating; |
548 |
+- |
549 |
+- static JSClass jsClass; |
550 |
+- |
551 |
+- static void start(void* arg) { |
552 |
+- ((WorkerQueue *) arg)->work(); |
553 |
+- } |
554 |
+- |
555 |
+- explicit ThreadPool(WorkerHooks *hooks) : hooks(hooks), mq(NULL), wq(NULL), terminating(0) { |
556 |
+- for (int i = 0; i < threadCount; i++) |
557 |
+- threads[i] = NULL; |
558 |
+- } |
559 |
+- |
560 |
+- public: |
561 |
+- ~ThreadPool() { |
562 |
+- JS_ASSERT(!mq); |
563 |
+- JS_ASSERT(!wq); |
564 |
+- JS_ASSERT(!threads[0]); |
565 |
+- } |
566 |
+- |
567 |
+- static ThreadPool *create(JSContext *cx, WorkerHooks *hooks) { |
568 |
+- ThreadPool *tp = new ThreadPool(hooks); |
569 |
+- if (!tp) { |
570 |
+- JS_ReportOutOfMemory(cx); |
571 |
+- return NULL; |
572 |
+- } |
573 |
+- |
574 |
+- JSObject *obj = JS_NewObject(cx, &jsClass, NULL, NULL); |
575 |
+- if (!obj || !JS_SetPrivate(cx, obj, tp)) { |
576 |
+- delete tp; |
577 |
+- return NULL; |
578 |
+- } |
579 |
+- tp->obj = obj; |
580 |
+- return tp; |
581 |
+- } |
582 |
+- |
583 |
+- JSObject *asObject() { return obj; } |
584 |
+- WorkerHooks *getHooks() { return hooks; } |
585 |
+- WorkerQueue *getWorkerQueue() { return wq; } |
586 |
+- MainQueue *getMainQueue() { return mq; } |
587 |
+- bool isTerminating() { return terminating != 0; } |
588 |
+- |
589 |
+- /* |
590 |
+- * Main thread only. Requires request (to prevent GC, which could see the |
591 |
+- * object in an inconsistent state). |
592 |
+- */ |
593 |
+- bool start(JSContext *cx) { |
594 |
+- JS_ASSERT(!mq && !wq); |
595 |
+- mq = new MainQueue(this); |
596 |
+- if (!mq || !mq->init()) { |
597 |
+- mq->destroy(cx); |
598 |
+- mq = NULL; |
599 |
+- return false; |
600 |
+- } |
601 |
+- wq = new WorkerQueue(mq); |
602 |
+- if (!wq || !wq->initThreadSafeQueue()) { |
603 |
+- delete wq; |
604 |
+- wq = NULL; |
605 |
+- mq->destroy(cx); |
606 |
+- mq = NULL; |
607 |
+- return false; |
608 |
+- } |
609 |
+- JSAutoSuspendRequest suspend(cx); |
610 |
+- bool ok = true; |
611 |
+- for (int i = 0; i < threadCount; i++) { |
612 |
+- threads[i] = PR_CreateThread(PR_USER_THREAD, start, wq, PR_PRIORITY_NORMAL, |
613 |
+- PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); |
614 |
+- if (!threads[i]) { |
615 |
+- shutdown(cx); |
616 |
+- ok = false; |
617 |
+- break; |
618 |
+- } |
619 |
+- } |
620 |
+- return ok; |
621 |
+- } |
622 |
+- |
623 |
+- void terminateAll(JSRuntime *rt) { |
624 |
+- // See comment about JS_ATOMIC_SET in the implementation of |
625 |
+- // JS_TriggerOperationCallback. |
626 |
+- JS_ATOMIC_SET(&terminating, 1); |
627 |
+- JS_TriggerAllOperationCallbacks(rt); |
628 |
+- } |
629 |
+- |
630 |
+- /* This context is used only to free memory. */ |
631 |
+- void shutdown(JSContext *cx) { |
632 |
+- wq->close(); |
633 |
+- for (int i = 0; i < threadCount; i++) { |
634 |
+- if (threads[i]) { |
635 |
+- PR_JoinThread(threads[i]); |
636 |
+- threads[i] = NULL; |
637 |
+- } |
638 |
+- } |
639 |
+- |
640 |
+- delete wq; |
641 |
+- wq = NULL; |
642 |
+- |
643 |
+- mq->disposeChildren(); |
644 |
+- mq->destroy(cx); |
645 |
+- mq = NULL; |
646 |
+- terminating = 0; |
647 |
+- } |
648 |
+- |
649 |
+- private: |
650 |
+- static void jsTraceThreadPool(JSTracer *trc, JSObject *obj) { |
651 |
+- ThreadPool *tp = unwrap(trc->context, obj); |
652 |
+- if (tp->mq) { |
653 |
+- tp->mq->traceChildren(trc); |
654 |
+- tp->wq->trace(trc); |
655 |
+- } |
656 |
+- } |
657 |
+- |
658 |
+- |
659 |
+- static void jsFinalize(JSContext *cx, JSObject *obj) { |
660 |
+- if (ThreadPool *tp = unwrap(cx, obj)) |
661 |
+- delete tp; |
662 |
+- } |
663 |
+- |
664 |
+- public: |
665 |
+- static ThreadPool *unwrap(JSContext *cx, JSObject *obj) { |
666 |
+- JS_ASSERT(JS_GET_CLASS(cx, obj) == &jsClass); |
667 |
+- return (ThreadPool *) JS_GetPrivate(cx, obj); |
668 |
+- } |
669 |
+-}; |
670 |
+- |
671 |
+-/* |
672 |
+- * A Worker is always in one of 4 states, except when it is being initialized |
673 |
+- * or destroyed, or its lock is held: |
674 |
+- * - idle (!terminated && current == NULL && events.empty()) |
675 |
+- * - enqueued (!terminated && current == NULL && !events.empty()) |
676 |
+- * - busy (!terminated && current != NULL) |
677 |
+- * - terminated (terminated && current == NULL && events.empty()) |
678 |
+- * |
679 |
+- * Separately, there is a terminateFlag that other threads can set |
680 |
+- * asynchronously to tell the Worker to terminate. |
681 |
+- */ |
682 |
+-class Worker : public WorkerParent |
683 |
+-{ |
684 |
+- private: |
685 |
+- ThreadPool *threadPool; |
686 |
+- WorkerParent *parent; |
687 |
+- JSObject *object; // Worker object exposed to parent |
688 |
+- JSContext *context; |
689 |
+- JSLock *lock; |
690 |
+- Queue<Event *, SystemAllocPolicy> events; // owning pointers to pending events |
691 |
+- Event *current; |
692 |
+- bool terminated; |
693 |
+- int32_t terminateFlag; |
694 |
+- |
695 |
+- static JSClass jsWorkerClass; |
696 |
+- |
697 |
+- Worker() |
698 |
+- : threadPool(NULL), parent(NULL), object(NULL), |
699 |
+- context(NULL), lock(NULL), current(NULL), terminated(false), terminateFlag(0) {} |
700 |
+- |
701 |
+- bool init(JSContext *parentcx, WorkerParent *parent, JSObject *obj) { |
702 |
+- JS_ASSERT(!threadPool && !this->parent && !object && !lock); |
703 |
+- |
704 |
+- if (!initWorkerParent() || !parent->addChild(this)) |
705 |
+- return false; |
706 |
+- threadPool = parent->getThreadPool(); |
707 |
+- this->parent = parent; |
708 |
+- this->object = obj; |
709 |
+- lock = JS_NEW_LOCK(); |
710 |
+- return lock && |
711 |
+- createContext(parentcx, parent) && |
712 |
+- JS_SetPrivate(parentcx, obj, this); |
713 |
+- } |
714 |
+- |
715 |
+- bool createContext(JSContext *parentcx, WorkerParent *parent) { |
716 |
+- JSRuntime *rt = JS_GetRuntime(parentcx); |
717 |
+- context = JS_NewContext(rt, 8192); |
718 |
+- if (!context) |
719 |
+- return false; |
720 |
+- |
721 |
+- // The Worker has a strong reference to the global; see jsTraceWorker. |
722 |
+- // JSOPTION_UNROOTED_GLOBAL ensures that when the worker becomes |
723 |
+- // unreachable, it and its global object can be collected. Otherwise |
724 |
+- // the cx->globalObject root would keep them both alive forever. |
725 |
+- JS_SetOptions(context, JS_GetOptions(parentcx) | JSOPTION_UNROOTED_GLOBAL | |
726 |
+- JSOPTION_DONT_REPORT_UNCAUGHT); |
727 |
+- JS_SetVersion(context, JS_GetVersion(parentcx)); |
728 |
+- JS_SetContextPrivate(context, this); |
729 |
+- JS_SetOperationCallback(context, jsOperationCallback); |
730 |
+- JS_BeginRequest(context); |
731 |
+- |
732 |
+- JSObject *global = threadPool->getHooks()->newGlobalObject(context); |
733 |
+- JSObject *post, *proto, *ctor; |
734 |
+- if (!global) |
735 |
+- goto bad; |
736 |
+- JS_SetGlobalObject(context, global); |
737 |
+- |
738 |
+- // Because the Worker is completely isolated from the rest of the |
739 |
+- // runtime, and because any pending events on a Worker keep the Worker |
740 |
+- // alive, this postMessage function cannot be called after the Worker |
741 |
+- // is collected. Therefore it's safe to stash a pointer (a weak |
742 |
+- // reference) to the C++ Worker object in the reserved slot. |
743 |
+- post = JS_GetFunctionObject(JS_DefineFunction(context, global, "postMessage", |
744 |
+- (JSNative) jsPostMessageToParent, 1, 0)); |
745 |
+- if (!post || !JS_SetReservedSlot(context, post, 0, PRIVATE_TO_JSVAL(this))) |
746 |
+- goto bad; |
747 |
+- |
748 |
+- proto = JS_InitClass(context, global, NULL, &jsWorkerClass, jsConstruct, 1, |
749 |
+- NULL, jsMethods, NULL, NULL); |
750 |
+- if (!proto) |
751 |
+- goto bad; |
752 |
+- |
753 |
+- ctor = JS_GetConstructor(context, proto); |
754 |
+- if (!ctor || !JS_SetReservedSlot(context, ctor, 0, PRIVATE_TO_JSVAL(this))) |
755 |
+- goto bad; |
756 |
+- |
757 |
+- JS_EndRequest(context); |
758 |
+- JS_ClearContextThread(context); |
759 |
+- return true; |
760 |
+- |
761 |
+- bad: |
762 |
+- JS_EndRequest(context); |
763 |
+- JS_DestroyContext(context); |
764 |
+- context = NULL; |
765 |
+- return false; |
766 |
+- } |
767 |
+- |
768 |
+- static void jsTraceWorker(JSTracer *trc, JSObject *obj) { |
769 |
+- JS_ASSERT(JS_GET_CLASS(trc->context, obj) == &jsWorkerClass); |
770 |
+- if (Worker *w = (Worker *) JS_GetPrivate(trc->context, obj)) { |
771 |
+- w->parent->trace(trc); |
772 |
+- w->events.trace(trc); |
773 |
+- if (w->current) |
774 |
+- w->current->trace(trc); |
775 |
+- JS_CALL_OBJECT_TRACER(trc, JS_GetGlobalObject(w->context), "Worker global"); |
776 |
+- } |
777 |
+- } |
778 |
+- |
779 |
+- static void jsFinalize(JSContext *cx, JSObject *obj) { |
780 |
+- JS_ASSERT(JS_GET_CLASS(cx, obj) == &jsWorkerClass); |
781 |
+- if (Worker *w = (Worker *) JS_GetPrivate(cx, obj)) |
782 |
+- delete w; |
783 |
+- } |
784 |
+- |
785 |
+- static JSBool jsOperationCallback(JSContext *cx) { |
786 |
+- Worker *w = (Worker *) JS_GetContextPrivate(cx); |
787 |
+- JSAutoSuspendRequest suspend(cx); // avoid nesting w->lock in a request |
788 |
+- return !w->checkTermination(); |
789 |
+- } |
790 |
+- |
791 |
+- static JSBool jsResolveGlobal(JSContext *cx, JSObject *obj, jsid id, uintN flags, |
792 |
+- JSObject **objp) |
793 |
+- { |
794 |
+- JSBool resolved; |
795 |
+- |
796 |
+- if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) |
797 |
+- return false; |
798 |
+- if (resolved) |
799 |
+- *objp = obj; |
800 |
+- |
801 |
+- return true; |
802 |
+- } |
803 |
+- |
804 |
+- static JSBool jsPostMessageToParent(JSContext *cx, uintN argc, jsval *vp); |
805 |
+- static JSBool jsPostMessageToChild(JSContext *cx, uintN argc, jsval *vp); |
806 |
+- static JSBool jsTerminate(JSContext *cx, uintN argc, jsval *vp); |
807 |
+- |
808 |
+- bool checkTermination() { |
809 |
+- AutoLock hold(lock); |
810 |
+- return lockedCheckTermination(); |
811 |
+- } |
812 |
+- |
813 |
+- bool lockedCheckTermination() { |
814 |
+- if (terminateFlag || threadPool->isTerminating()) { |
815 |
+- terminateSelf(); |
816 |
+- terminateFlag = 0; |
817 |
+- } |
818 |
+- return terminated; |
819 |
+- } |
820 |
+- |
821 |
+- // Caller must hold the lock. |
822 |
+- void terminateSelf() { |
823 |
+- terminated = true; |
824 |
+- while (!events.empty()) |
825 |
+- events.pop()->destroy(context); |
826 |
+- |
827 |
+- // Tell the children to shut down too. An arbitrarily silly amount of |
828 |
+- // processing could happen before the whole tree is terminated; but |
829 |
+- // this way we don't have to worry about blowing the C stack. |
830 |
+- for (ChildSet::Enum e(children); !e.empty(); e.popFront()) |
831 |
+- e.front()->setTerminateFlag(); // note: nesting locks here |
832 |
+- } |
833 |
+- |
834 |
+- public: |
835 |
+- ~Worker() { |
836 |
+- if (parent) |
837 |
+- parent->removeChild(this); |
838 |
+- dispose(); |
839 |
+- } |
840 |
+- |
841 |
+- void dispose() { |
842 |
+- JS_ASSERT(!current); |
843 |
+- while (!events.empty()) |
844 |
+- events.pop()->destroy(context); |
845 |
+- if (lock) { |
846 |
+- JS_DESTROY_LOCK(lock); |
847 |
+- lock = NULL; |
848 |
+- } |
849 |
+- if (context) { |
850 |
+- JS_SetContextThread(context); |
851 |
+- JS_DestroyContextNoGC(context); |
852 |
+- context = NULL; |
853 |
+- } |
854 |
+- object = NULL; |
855 |
+- |
856 |
+- // Do not call parent->removeChild(). This is called either from |
857 |
+- // ~Worker, which calls it for us; or from parent->disposeChildren or |
858 |
+- // Worker::create, which require that it not be called. |
859 |
+- parent = NULL; |
860 |
+- disposeChildren(); |
861 |
+- } |
862 |
+- |
863 |
+- static Worker *create(JSContext *parentcx, WorkerParent *parent, |
864 |
+- JSString *scriptName, JSObject *obj); |
865 |
+- |
866 |
+- JSObject *asObject() { return object; } |
867 |
+- |
868 |
+- JSObject *getGlobal() { return JS_GetGlobalObject(context); } |
869 |
+- |
870 |
+- WorkerParent *getParent() { return parent; } |
871 |
+- |
872 |
+- virtual JSLock *getLock() { return lock; } |
873 |
+- |
874 |
+- virtual ThreadPool *getThreadPool() { return threadPool; } |
875 |
+- |
876 |
+- bool post(Event *event) { |
877 |
+- AutoLock hold(lock); |
878 |
+- if (terminated) |
879 |
+- return false; |
880 |
+- if (!current && events.empty() && !threadPool->getWorkerQueue()->post(this)) |
881 |
+- return false; |
882 |
+- return events.push(event); |
883 |
+- } |
884 |
+- |
885 |
+- void setTerminateFlag() { |
886 |
+- AutoLock hold(lock); |
887 |
+- terminateFlag = true; |
888 |
+- if (current) |
889 |
+- JS_TriggerOperationCallback(context); |
890 |
+- } |
891 |
+- |
892 |
+- void processOneEvent(); |
893 |
+- |
894 |
+- /* Trace method to be called from C++. */ |
895 |
+- void trace(JSTracer *trc) { |
896 |
+- // Just mark the JSObject. If we haven't already been marked, |
897 |
+- // jsTraceWorker will be called, at which point we'll trace referents. |
898 |
+- JS_CALL_OBJECT_TRACER(trc, object, "queued Worker"); |
899 |
+- } |
900 |
+- |
901 |
+- static bool getWorkerParentFromConstructor(JSContext *cx, JSObject *ctor, WorkerParent **p) { |
902 |
+- jsval v; |
903 |
+- if (!JS_GetReservedSlot(cx, ctor, 0, &v)) |
904 |
+- return false; |
905 |
+- if (JSVAL_IS_VOID(v)) { |
906 |
+- // This means ctor is the root Worker constructor (created in |
907 |
+- // Worker::initWorkers as opposed to Worker::createContext, which sets up |
908 |
+- // Worker sandboxes) and nothing is initialized yet. |
909 |
+- if (!JS_GetReservedSlot(cx, ctor, 1, &v)) |
910 |
+- return false; |
911 |
+- ThreadPool *threadPool = (ThreadPool *) JSVAL_TO_PRIVATE(v); |
912 |
+- if (!threadPool->start(cx)) |
913 |
+- return false; |
914 |
+- WorkerParent *parent = threadPool->getMainQueue(); |
915 |
+- if (!JS_SetReservedSlot(cx, ctor, 0, PRIVATE_TO_JSVAL(parent))) { |
916 |
+- threadPool->shutdown(cx); |
917 |
+- return false; |
918 |
+- } |
919 |
+- *p = parent; |
920 |
+- return true; |
921 |
+- } |
922 |
+- *p = (WorkerParent *) JSVAL_TO_PRIVATE(v); |
923 |
+- return true; |
924 |
+- } |
925 |
+- |
926 |
+- static JSBool jsConstruct(JSContext *cx, uintN argc, jsval *vp) { |
927 |
+- WorkerParent *parent; |
928 |
+- if (!getWorkerParentFromConstructor(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), &parent)) |
929 |
+- return false; |
930 |
+- |
931 |
+- |
932 |
+- JSString *scriptName = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID); |
933 |
+- if (!scriptName) |
934 |
+- return false; |
935 |
+- |
936 |
+- JSObject *obj = JS_NewObject(cx, &jsWorkerClass, NULL, NULL); |
937 |
+- if (!obj || !create(cx, parent, scriptName, obj)) |
938 |
+- return false; |
939 |
+- JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj)); |
940 |
+- return true; |
941 |
+- } |
942 |
+- |
943 |
+- static JSFunctionSpec jsMethods[3]; |
944 |
+- static JSFunctionSpec jsStaticMethod[2]; |
945 |
+- |
946 |
+- static ThreadPool *initWorkers(JSContext *cx, WorkerHooks *hooks, JSObject *global, |
947 |
+- JSObject **objp) { |
948 |
+- // Create the ThreadPool object and its JSObject wrapper. |
949 |
+- ThreadPool *threadPool = ThreadPool::create(cx, hooks); |
950 |
+- if (!threadPool) |
951 |
+- return NULL; |
952 |
+- |
953 |
+- // Root the ThreadPool JSObject early. |
954 |
+- *objp = threadPool->asObject(); |
955 |
+- |
956 |
+- // Create the Worker constructor. |
957 |
+- JSObject *proto = JS_InitClass(cx, global, NULL, &jsWorkerClass, |
958 |
+- jsConstruct, 1, |
959 |
+- NULL, jsMethods, NULL, NULL); |
960 |
+- if (!proto) |
961 |
+- return NULL; |
962 |
+- |
963 |
+- // Stash a pointer to the ThreadPool in constructor reserved slot 1. |
964 |
+- // It will be used later when lazily creating the MainQueue. |
965 |
+- JSObject *ctor = JS_GetConstructor(cx, proto); |
966 |
+- if (!JS_SetReservedSlot(cx, ctor, 1, PRIVATE_TO_JSVAL(threadPool))) |
967 |
+- return NULL; |
968 |
+- |
969 |
+- return threadPool; |
970 |
+- } |
971 |
+-}; |
972 |
+- |
973 |
+-class InitEvent : public Event |
974 |
+-{ |
975 |
+- public: |
976 |
+- static InitEvent *create(JSContext *cx, Worker *worker, JSString *scriptName) { |
977 |
+- return createEvent<InitEvent>(cx, worker, worker, STRING_TO_JSVAL(scriptName)); |
978 |
+- } |
979 |
+- |
980 |
+- Result process(JSContext *cx) { |
981 |
+- jsval s; |
982 |
+- if (!deserializeData(cx, &s)) |
983 |
+- return fail; |
984 |
+- JS_ASSERT(JSVAL_IS_STRING(s)); |
985 |
+- JSAutoByteString filename(cx, JSVAL_TO_STRING(s)); |
986 |
+- if (!filename) |
987 |
+- return fail; |
988 |
+- |
989 |
+- JSObject *scriptObj = JS_CompileFile(cx, child->getGlobal(), filename.ptr()); |
990 |
+- if (!scriptObj) |
991 |
+- return fail; |
992 |
+- |
993 |
+- AutoValueRooter rval(cx); |
994 |
+- JSBool ok = JS_ExecuteScript(cx, child->getGlobal(), scriptObj, Jsvalify(rval.addr())); |
995 |
+- return Result(ok); |
996 |
+- } |
997 |
+-}; |
998 |
+- |
999 |
+-class DownMessageEvent : public Event |
1000 |
+-{ |
1001 |
+- public: |
1002 |
+- static DownMessageEvent *create(JSContext *cx, Worker *child, jsval data) { |
1003 |
+- return createEvent<DownMessageEvent>(cx, child, child, data); |
1004 |
+- } |
1005 |
+- |
1006 |
+- Result process(JSContext *cx) { |
1007 |
+- return dispatch(cx, child->getGlobal(), "data", "onmessage", ok); |
1008 |
+- } |
1009 |
+-}; |
1010 |
+- |
1011 |
+-class UpMessageEvent : public Event |
1012 |
+-{ |
1013 |
+- public: |
1014 |
+- static UpMessageEvent *create(JSContext *cx, Worker *child, jsval data) { |
1015 |
+- return createEvent<UpMessageEvent>(cx, child->getParent(), child, data); |
1016 |
+- } |
1017 |
+- |
1018 |
+- Result process(JSContext *cx) { |
1019 |
+- return dispatch(cx, child->asObject(), "data", "onmessage", ok); |
1020 |
+- } |
1021 |
+-}; |
1022 |
+- |
1023 |
+-class ErrorEvent : public Event |
1024 |
+-{ |
1025 |
+- public: |
1026 |
+- static ErrorEvent *create(JSContext *cx, Worker *child) { |
1027 |
+- JSString *data = NULL; |
1028 |
+- jsval exc; |
1029 |
+- if (JS_GetPendingException(cx, &exc)) { |
1030 |
+- AutoValueRooter tvr(cx, Valueify(exc)); |
1031 |
+- JS_ClearPendingException(cx); |
1032 |
+- |
1033 |
+- // Determine what error message to put in the error event. |
1034 |
+- // If exc.message is a string, use that; otherwise use String(exc). |
1035 |
+- // (This is a little different from what web workers do.) |
1036 |
+- if (JSVAL_IS_OBJECT(exc)) { |
1037 |
+- jsval msg; |
1038 |
+- if (!JS_GetProperty(cx, JSVAL_TO_OBJECT(exc), "message", &msg)) |
1039 |
+- JS_ClearPendingException(cx); |
1040 |
+- else if (JSVAL_IS_STRING(msg)) |
1041 |
+- data = JSVAL_TO_STRING(msg); |
1042 |
+- } |
1043 |
+- if (!data) { |
1044 |
+- data = JS_ValueToString(cx, exc); |
1045 |
+- if (!data) |
1046 |
+- return NULL; |
1047 |
+- } |
1048 |
+- } |
1049 |
+- return createEvent<ErrorEvent>(cx, child->getParent(), child, |
1050 |
+- data ? STRING_TO_JSVAL(data) : JSVAL_VOID); |
1051 |
+- } |
1052 |
+- |
1053 |
+- Result process(JSContext *cx) { |
1054 |
+- return dispatch(cx, child->asObject(), "message", "onerror", forwardToParent); |
1055 |
+- } |
1056 |
+-}; |
1057 |
+- |
1058 |
+-} /* namespace workers */ |
1059 |
+-} /* namespace js */ |
1060 |
+- |
1061 |
+-using namespace js::workers; |
1062 |
+- |
1063 |
+-void |
1064 |
+-WorkerParent::disposeChildren() |
1065 |
+-{ |
1066 |
+- for (ChildSet::Enum e(children); !e.empty(); e.popFront()) { |
1067 |
+- e.front()->dispose(); |
1068 |
+- e.removeFront(); |
1069 |
+- } |
1070 |
+-} |
1071 |
+- |
1072 |
+-bool |
1073 |
+-MainQueue::shouldStop() |
1074 |
+-{ |
1075 |
+- // Note: This deliberately nests WorkerQueue::lock in MainQueue::lock. |
1076 |
+- // Releasing MainQueue::lock would risk a race -- isIdle() could return |
1077 |
+- // false, but the workers could become idle before we reacquire |
1078 |
+- // MainQueue::lock and go to sleep, and we would wait on the condvar |
1079 |
+- // forever. |
1080 |
+- return closed || threadPool->getWorkerQueue()->isIdle(); |
1081 |
+-} |
1082 |
+- |
1083 |
+-void |
1084 |
+-MainQueue::trace(JSTracer *trc) |
1085 |
+-{ |
1086 |
+- JS_CALL_OBJECT_TRACER(trc, threadPool->asObject(), "MainQueue"); |
1087 |
+-} |
1088 |
+- |
1089 |
+-void |
1090 |
+-WorkerQueue::work() { |
1091 |
+- AutoLock hold(lock); |
1092 |
+- |
1093 |
+- Worker *w; |
1094 |
+- while (take(&w)) { // can block outside the mutex |
1095 |
+- JS_RELEASE_LOCK(lock); |
1096 |
+- w->processOneEvent(); // enters request on w->context |
1097 |
+- JS_ACQUIRE_LOCK(lock); |
1098 |
+- drop(w); |
1099 |
+- |
1100 |
+- if (lockedIsIdle()) { |
1101 |
+- JS_RELEASE_LOCK(lock); |
1102 |
+- main->wake(); |
1103 |
+- JS_ACQUIRE_LOCK(lock); |
1104 |
+- } |
1105 |
+- } |
1106 |
+-} |
1107 |
+- |
1108 |
+-const bool mswin = |
1109 |
+-#ifdef XP_WIN |
1110 |
+- true |
1111 |
+-#else |
1112 |
+- false |
1113 |
+-#endif |
1114 |
+- ; |
1115 |
+- |
1116 |
+-template <class Ch> bool |
1117 |
+-IsAbsolute(const Ch *filename) |
1118 |
+-{ |
1119 |
+- return filename[0] == '/' || |
1120 |
+- (mswin && (filename[0] == '\\' || (filename[0] != '\0' && filename[1] == ':'))); |
1121 |
+-} |
1122 |
+- |
1123 |
+-// Note: base is a filename, not a directory name. |
1124 |
+-static JSString * |
1125 |
+-ResolveRelativePath(JSContext *cx, const char *base, JSString *filename) |
1126 |
+-{ |
1127 |
+- size_t fileLen = JS_GetStringLength(filename); |
1128 |
+- const jschar *fileChars = JS_GetStringCharsZ(cx, filename); |
1129 |
+- if (!fileChars) |
1130 |
+- return NULL; |
1131 |
+- |
1132 |
+- if (IsAbsolute(fileChars)) |
1133 |
+- return filename; |
1134 |
+- |
1135 |
+- // Strip off the filename part of base. |
1136 |
+- size_t dirLen = -1; |
1137 |
+- for (size_t i = 0; base[i]; i++) { |
1138 |
+- if (base[i] == '/' || (mswin && base[i] == '\\')) |
1139 |
+- dirLen = i; |
1140 |
+- } |
1141 |
+- |
1142 |
+- // If base is relative and contains no directories, use filename unchanged. |
1143 |
+- if (!IsAbsolute(base) && dirLen == (size_t) -1) |
1144 |
+- return filename; |
1145 |
+- |
1146 |
+- // Otherwise return base[:dirLen + 1] + filename. |
1147 |
+- js::Vector<jschar, 0, js::ContextAllocPolicy> result(cx); |
1148 |
+- size_t nchars; |
1149 |
+- if (!JS_DecodeBytes(cx, base, dirLen + 1, NULL, &nchars)) |
1150 |
+- return NULL; |
1151 |
+- if (!result.reserve(dirLen + 1 + fileLen)) { |
1152 |
+- JS_ReportOutOfMemory(cx); |
1153 |
+- return NULL; |
1154 |
+- } |
1155 |
+- JS_ALWAYS_TRUE(result.resize(dirLen + 1)); |
1156 |
+- if (!JS_DecodeBytes(cx, base, dirLen + 1, result.begin(), &nchars)) |
1157 |
+- return NULL; |
1158 |
+- JS_ALWAYS_TRUE(result.append(fileChars, fileLen)); |
1159 |
+- return JS_NewUCStringCopyN(cx, result.begin(), result.length()); |
1160 |
+-} |
1161 |
+- |
1162 |
+-Worker * |
1163 |
+-Worker::create(JSContext *parentcx, WorkerParent *parent, JSString *scriptName, JSObject *obj) |
1164 |
+-{ |
1165 |
+- Worker *w = new Worker(); |
1166 |
+- if (!w || !w->init(parentcx, parent, obj)) { |
1167 |
+- delete w; |
1168 |
+- return NULL; |
1169 |
+- } |
1170 |
+- |
1171 |
+- JSStackFrame *frame = JS_GetScriptedCaller(parentcx, NULL); |
1172 |
+- const char *base = JS_GetScriptFilename(parentcx, JS_GetFrameScript(parentcx, frame)); |
1173 |
+- JSString *scriptPath = ResolveRelativePath(parentcx, base, scriptName); |
1174 |
+- if (!scriptPath) |
1175 |
+- return NULL; |
1176 |
+- |
1177 |
+- // Post an InitEvent to run the initialization script. |
1178 |
+- Event *event = InitEvent::create(parentcx, w, scriptPath); |
1179 |
+- if (!event) |
1180 |
+- return NULL; |
1181 |
+- if (!w->events.push(event) || !w->threadPool->getWorkerQueue()->post(w)) { |
1182 |
+- event->destroy(parentcx); |
1183 |
+- JS_ReportOutOfMemory(parentcx); |
1184 |
+- w->dispose(); |
1185 |
+- return NULL; |
1186 |
+- } |
1187 |
+- return w; |
1188 |
+-} |
1189 |
+- |
1190 |
+-void |
1191 |
+-Worker::processOneEvent() |
1192 |
+-{ |
1193 |
+- Event *event; |
1194 |
+- { |
1195 |
+- AutoLock hold1(lock); |
1196 |
+- if (lockedCheckTermination() || events.empty()) |
1197 |
+- return; |
1198 |
+- |
1199 |
+- event = current = events.pop(); |
1200 |
+- } |
1201 |
+- |
1202 |
+- JS_SetContextThread(context); |
1203 |
+- JS_SetNativeStackQuota(context, gMaxStackSize); |
1204 |
+- |
1205 |
+- Event::Result result; |
1206 |
+- { |
1207 |
+- JSAutoRequest req(context); |
1208 |
+- result = event->process(context); |
1209 |
+- } |
1210 |
+- |
1211 |
+- // Note: we have to leave the above request before calling parent->post or |
1212 |
+- // checkTermination, both of which acquire locks. |
1213 |
+- if (result == Event::forwardToParent) { |
1214 |
+- event->setChildAndRecipient(this, parent); |
1215 |
+- if (parent->post(event)) { |
1216 |
+- event = NULL; // to prevent it from being deleted below |
1217 |
+- } else { |
1218 |
+- JS_ReportOutOfMemory(context); |
1219 |
+- result = Event::fail; |
1220 |
+- } |
1221 |
+- } |
1222 |
+- if (result == Event::fail && !checkTermination()) { |
1223 |
+- JSAutoRequest req(context); |
1224 |
+- Event *err = ErrorEvent::create(context, this); |
1225 |
+- if (err && !parent->post(err)) { |
1226 |
+- JS_ReportOutOfMemory(context); |
1227 |
+- err->destroy(context); |
1228 |
+- err = NULL; |
1229 |
+- } |
1230 |
+- if (!err) { |
1231 |
+- // FIXME - out of memory, probably should panic |
1232 |
+- } |
1233 |
+- } |
1234 |
+- |
1235 |
+- if (event) |
1236 |
+- event->destroy(context); |
1237 |
+- JS_ClearContextThread(context); |
1238 |
+- |
1239 |
+- { |
1240 |
+- AutoLock hold2(lock); |
1241 |
+- current = NULL; |
1242 |
+- if (!lockedCheckTermination() && !events.empty()) { |
1243 |
+- // Re-enqueue this worker. OOM here effectively kills the worker. |
1244 |
+- if (!threadPool->getWorkerQueue()->post(this)) |
1245 |
+- JS_ReportOutOfMemory(context); |
1246 |
+- } |
1247 |
+- } |
1248 |
+-} |
1249 |
+- |
1250 |
+-JSBool |
1251 |
+-Worker::jsPostMessageToParent(JSContext *cx, uintN argc, jsval *vp) |
1252 |
+-{ |
1253 |
+- jsval workerval; |
1254 |
+- if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), 0, &workerval)) |
1255 |
+- return false; |
1256 |
+- Worker *w = (Worker *) JSVAL_TO_PRIVATE(workerval); |
1257 |
+- |
1258 |
+- { |
1259 |
+- JSAutoSuspendRequest suspend(cx); // avoid nesting w->lock in a request |
1260 |
+- if (w->checkTermination()) |
1261 |
+- return false; |
1262 |
+- } |
1263 |
+- |
1264 |
+- jsval data = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID; |
1265 |
+- Event *event = UpMessageEvent::create(cx, w, data); |
1266 |
+- if (!event) |
1267 |
+- return false; |
1268 |
+- if (!w->parent->post(event)) { |
1269 |
+- event->destroy(cx); |
1270 |
+- JS_ReportOutOfMemory(cx); |
1271 |
+- return false; |
1272 |
+- } |
1273 |
+- JS_SET_RVAL(cx, vp, JSVAL_VOID); |
1274 |
+- return true; |
1275 |
+-} |
1276 |
+- |
1277 |
+-JSBool |
1278 |
+-Worker::jsPostMessageToChild(JSContext *cx, uintN argc, jsval *vp) |
1279 |
+-{ |
1280 |
+- JSObject *workerobj = JS_THIS_OBJECT(cx, vp); |
1281 |
+- if (!workerobj) |
1282 |
+- return false; |
1283 |
+- Worker *w = (Worker *) JS_GetInstancePrivate(cx, workerobj, &jsWorkerClass, JS_ARGV(cx, vp)); |
1284 |
+- if (!w) { |
1285 |
+- if (!JS_IsExceptionPending(cx)) |
1286 |
+- JS_ReportError(cx, "Worker was shut down"); |
1287 |
+- return false; |
1288 |
+- } |
1289 |
+- |
1290 |
+- jsval data = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID; |
1291 |
+- Event *event = DownMessageEvent::create(cx, w, data); |
1292 |
+- if (!event) |
1293 |
+- return false; |
1294 |
+- if (!w->post(event)) { |
1295 |
+- JS_ReportOutOfMemory(cx); |
1296 |
+- return false; |
1297 |
+- } |
1298 |
+- JS_SET_RVAL(cx, vp, JSVAL_VOID); |
1299 |
+- return true; |
1300 |
+-} |
1301 |
+- |
1302 |
+-JSBool |
1303 |
+-Worker::jsTerminate(JSContext *cx, uintN argc, jsval *vp) |
1304 |
+-{ |
1305 |
+- JS_SET_RVAL(cx, vp, JSVAL_VOID); |
1306 |
+- |
1307 |
+- JSObject *workerobj = JS_THIS_OBJECT(cx, vp); |
1308 |
+- if (!workerobj) |
1309 |
+- return false; |
1310 |
+- Worker *w = (Worker *) JS_GetInstancePrivate(cx, workerobj, &jsWorkerClass, JS_ARGV(cx, vp)); |
1311 |
+- if (!w) |
1312 |
+- return !JS_IsExceptionPending(cx); // ok to terminate twice |
1313 |
+- |
1314 |
+- JSAutoSuspendRequest suspend(cx); |
1315 |
+- w->setTerminateFlag(); |
1316 |
+- return true; |
1317 |
+-} |
1318 |
+- |
1319 |
+-void |
1320 |
+-Event::trace(JSTracer *trc) |
1321 |
+-{ |
1322 |
+- if (recipient) |
1323 |
+- recipient->trace(trc); |
1324 |
+- if (child) |
1325 |
+- JS_CALL_OBJECT_TRACER(trc, child->asObject(), "worker"); |
1326 |
+-} |
1327 |
+- |
1328 |
+-JSClass ThreadPool::jsClass = { |
1329 |
+- "ThreadPool", JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE, |
1330 |
+- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
1331 |
+- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, jsFinalize, |
1332 |
+- NULL, NULL, NULL, NULL, |
1333 |
+- NULL, NULL, JS_CLASS_TRACE(jsTraceThreadPool), NULL |
1334 |
+-}; |
1335 |
+- |
1336 |
+-JSClass Worker::jsWorkerClass = { |
1337 |
+- "Worker", JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE, |
1338 |
+- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
1339 |
+- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, jsFinalize, |
1340 |
+- NULL, NULL, NULL, NULL, |
1341 |
+- NULL, NULL, JS_CLASS_TRACE(jsTraceWorker), NULL |
1342 |
+-}; |
1343 |
+- |
1344 |
+-JSFunctionSpec Worker::jsMethods[3] = { |
1345 |
+- JS_FN("postMessage", Worker::jsPostMessageToChild, 1, 0), |
1346 |
+- JS_FN("terminate", Worker::jsTerminate, 0, 0), |
1347 |
+- JS_FS_END |
1348 |
+-}; |
1349 |
+- |
1350 |
+-ThreadPool * |
1351 |
+-js::workers::init(JSContext *cx, WorkerHooks *hooks, JSObject *global, JSObject **rootp) |
1352 |
+-{ |
1353 |
+- return Worker::initWorkers(cx, hooks, global, rootp); |
1354 |
+-} |
1355 |
+- |
1356 |
+-void |
1357 |
+-js::workers::terminateAll(JSRuntime *rt, ThreadPool *tp) |
1358 |
+-{ |
1359 |
+- tp->terminateAll(rt); |
1360 |
+-} |
1361 |
+- |
1362 |
+-void |
1363 |
+-js::workers::finish(JSContext *cx, ThreadPool *tp) |
1364 |
+-{ |
1365 |
+- if (MainQueue *mq = tp->getMainQueue()) { |
1366 |
+- JS_ALWAYS_TRUE(mq->mainThreadWork(cx, true)); |
1367 |
+- tp->shutdown(cx); |
1368 |
+- } |
1369 |
+-} |
1370 |
+- |
1371 |
+-#endif /* JS_THREADSAFE */ |
1372 |
diff -ruN deps.orig/mozilla/js/src/shell/jsworkers.h deps/mozilla/js/src/shell/jsworkers.h |
1373 |
--- deps.orig/mozilla/js/src/shell/jsworkers.h 2014-06-30 08:54:39 UTC |
1374 |
+++ deps/mozilla/js/src/shell/jsworkers.h |
1375 |
@@ -1,93 +0,0 @@ |
1376 |
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
1377 |
- * vim: set ts=8 sw=4 et tw=99: |
1378 |
- * |
1379 |
- * ***** BEGIN LICENSE BLOCK ***** |
1380 |
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
1381 |
- * |
1382 |
- * The contents of this file are subject to the Mozilla Public License Version |
1383 |
- * 1.1 (the "License"); you may not use this file except in compliance with |
1384 |
- * the License. You may obtain a copy of the License at |
1385 |
- * http://www.mozilla.org/MPL/ |
1386 |
- * |
1387 |
- * Software distributed under the License is distributed on an "AS IS" basis, |
1388 |
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
1389 |
- * for the specific language governing rights and limitations under the |
1390 |
- * License. |
1391 |
- * |
1392 |
- * The Original Code is JavaScript shell workers. |
1393 |
- * |
1394 |
- * The Initial Developer of the Original Code is |
1395 |
- * Mozilla Corporation. |
1396 |
- * Portions created by the Initial Developer are Copyright (C) 2010 |
1397 |
- * the Initial Developer. All Rights Reserved. |
1398 |
- * |
1399 |
- * Contributor(s): |
1400 |
- * Jason Orendorff <jorendorff@mozilla.com> |
1401 |
- * |
1402 |
- * Alternatively, the contents of this file may be used under the terms of |
1403 |
- * either of the GNU General Public License Version 2 or later (the "GPL"), |
1404 |
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
1405 |
- * in which case the provisions of the GPL or the LGPL are applicable instead |
1406 |
- * of those above. If you wish to allow use of your version of this file only |
1407 |
- * under the terms of either the GPL or the LGPL, and not to allow others to |
1408 |
- * use your version of this file under the terms of the MPL, indicate your |
1409 |
- * decision by deleting the provisions above and replace them with the notice |
1410 |
- * and other provisions required by the GPL or the LGPL. If you do not delete |
1411 |
- * the provisions above, a recipient may use your version of this file under |
1412 |
- * the terms of any one of the MPL, the GPL or the LGPL. |
1413 |
- * |
1414 |
- * ***** END LICENSE BLOCK ***** */ |
1415 |
- |
1416 |
-#ifndef jsworkers_h___ |
1417 |
-#define jsworkers_h___ |
1418 |
- |
1419 |
-#ifdef JS_THREADSAFE |
1420 |
- |
1421 |
-#include "jsapi.h" |
1422 |
- |
1423 |
-/* |
1424 |
- * Workers for the JS shell. |
1425 |
- * |
1426 |
- * Note: The real implementation of DOM Workers is in dom/src/threads. |
1427 |
- */ |
1428 |
-namespace js { |
1429 |
- namespace workers { |
1430 |
- class ThreadPool; |
1431 |
- |
1432 |
- class WorkerHooks { |
1433 |
- public: |
1434 |
- virtual JSObject *newGlobalObject(JSContext *cx) = 0; |
1435 |
- virtual ~WorkerHooks() {} |
1436 |
- }; |
1437 |
- |
1438 |
- /* |
1439 |
- * Initialize workers. This defines the Worker constructor on global. |
1440 |
- * Requires request. rootp must point to a GC root. |
1441 |
- * |
1442 |
- * On success, *rootp receives a pointer to an object, and init returns |
1443 |
- * a non-null value. The caller must keep the object rooted and must |
1444 |
- * pass it to js::workers::finish later. |
1445 |
- */ |
1446 |
- ThreadPool *init(JSContext *cx, WorkerHooks *hooks, JSObject *global, JSObject **rootp); |
1447 |
- |
1448 |
- /* Asynchronously signal for all workers to terminate. |
1449 |
- * |
1450 |
- * Call this before calling finish() to shut down without waiting for |
1451 |
- * all messages to be proceesed. |
1452 |
- */ |
1453 |
- void terminateAll(JSRuntime *rt, ThreadPool *tp); |
1454 |
- |
1455 |
- /* |
1456 |
- * Finish running any workers, shut down the thread pool, and free all |
1457 |
- * resources associated with workers. The application must call this |
1458 |
- * before shutting down the runtime, and not during GC. |
1459 |
- * |
1460 |
- * Requires request. |
1461 |
- */ |
1462 |
- void finish(JSContext *cx, ThreadPool *tp); |
1463 |
- } |
1464 |
-} |
1465 |
- |
1466 |
-#endif /* JS_THREADSAFE */ |
1467 |
- |
1468 |
-#endif /* jsworkers_h___ */ |
1469 |
diff -ruN deps.orig/mozilla/js/src/tests/browser.js deps/mozilla/js/src/tests/browser.js |
87 |
diff -ruN deps.orig/mozilla/js/src/tests/browser.js deps/mozilla/js/src/tests/browser.js |
1470 |
--- deps.orig/mozilla/js/src/tests/browser.js 2014-06-30 08:54:39 UTC |
88 |
--- deps.orig/mozilla/js/src/tests/browser.js 2014-06-30 08:54:39 UTC |
1471 |
+++ deps/mozilla/js/src/tests/browser.js |
89 |
+++ deps/mozilla/js/src/tests/browser.js |