69 |
JS_END_EXTERN_C |
JS_END_EXTERN_C |
70 |
#pragma intrinsic(_InterlockedCompareExchange) |
#pragma intrinsic(_InterlockedCompareExchange) |
71 |
|
|
72 |
|
JS_STATIC_ASSERT(sizeof(jsword) == sizeof(long)); |
73 |
|
|
74 |
static JS_ALWAYS_INLINE int |
static JS_ALWAYS_INLINE int |
75 |
NativeCompareAndSwapHelper(jsword *w, jsword ov, jsword nv) |
NativeCompareAndSwapHelper(jsword *w, jsword ov, jsword nv) |
76 |
{ |
{ |
77 |
_InterlockedCompareExchange(w, nv, ov); |
_InterlockedCompareExchange((long*) w, nv, ov); |
78 |
__asm { |
__asm { |
79 |
sete al |
sete al |
80 |
} |
} |
94 |
NativeCompareAndSwap(jsword *w, jsword ov, jsword nv) |
NativeCompareAndSwap(jsword *w, jsword ov, jsword nv) |
95 |
{ |
{ |
96 |
/* Details on these functions available in the manpage for atomic */ |
/* Details on these functions available in the manpage for atomic */ |
97 |
#if JS_BYTES_PER_WORD == 8 && JS_BYTES_PER_LONG != 8 |
return OSAtomicCompareAndSwapPtrBarrier(ov, nv, w); |
|
return OSAtomicCompareAndSwap64Barrier(ov, nv, (int64_t*) w); |
|
|
#else |
|
|
return OSAtomicCompareAndSwap32Barrier(ov, nv, (int32_t*) w); |
|
|
#endif |
|
98 |
} |
} |
99 |
|
|
100 |
#elif defined(__GNUC__) && defined(__i386__) |
#elif defined(__GNUC__) && defined(__i386__) |
233 |
|
|
234 |
#endif |
#endif |
235 |
|
|
236 |
|
void |
237 |
|
js_AtomicSetMask(jsword *w, jsword mask) |
238 |
|
{ |
239 |
|
jsword ov, nv; |
240 |
|
|
241 |
|
do { |
242 |
|
ov = *w; |
243 |
|
nv = ov | mask; |
244 |
|
} while (!js_CompareAndSwap(w, ov, nv)); |
245 |
|
} |
246 |
|
|
247 |
#ifndef NSPR_LOCK |
#ifndef NSPR_LOCK |
248 |
|
|
249 |
struct JSFatLock { |
struct JSFatLock { |
314 |
#include <stdio.h> |
#include <stdio.h> |
315 |
#include "jsdhash.h" |
#include "jsdhash.h" |
316 |
|
|
317 |
static FILE *logfp; |
static FILE *logfp = NULL; |
318 |
static JSDHashTable logtbl; |
static JSDHashTable logtbl; |
319 |
|
|
320 |
typedef struct logentry { |
typedef struct logentry { |
325 |
} logentry; |
} logentry; |
326 |
|
|
327 |
static void |
static void |
328 |
logit(JSScope *scope, char op, const char *file, int line) |
logit(JSTitle *title, char op, const char *file, int line) |
329 |
{ |
{ |
330 |
logentry *entry; |
logentry *entry; |
331 |
|
|
335 |
return; |
return; |
336 |
setvbuf(logfp, NULL, _IONBF, 0); |
setvbuf(logfp, NULL, _IONBF, 0); |
337 |
} |
} |
338 |
fprintf(logfp, "%p %c %s %d\n", scope, op, file, line); |
fprintf(logfp, "%p %d %c %s %d\n", title, title->u.count, op, file, line); |
339 |
|
|
340 |
if (!logtbl.entryStore && |
if (!logtbl.entryStore && |
341 |
!JS_DHashTableInit(&logtbl, JS_DHashGetStubOps(), NULL, |
!JS_DHashTableInit(&logtbl, JS_DHashGetStubOps(), NULL, |
342 |
sizeof(logentry), 100)) { |
sizeof(logentry), 100)) { |
343 |
return; |
return; |
344 |
} |
} |
345 |
entry = (logentry *) JS_DHashTableOperate(&logtbl, scope, JS_DHASH_ADD); |
entry = (logentry *) JS_DHashTableOperate(&logtbl, title, JS_DHASH_ADD); |
346 |
if (!entry) |
if (!entry) |
347 |
return; |
return; |
348 |
entry->stub.key = scope; |
entry->stub.key = title; |
349 |
entry->op = op; |
entry->op = op; |
350 |
entry->file = file; |
entry->file = file; |
351 |
entry->line = line; |
entry->line = line; |
352 |
} |
} |
353 |
|
|
354 |
void |
void |
355 |
js_unlog_scope(JSScope *scope) |
js_unlog_title(JSTitle *title) |
356 |
{ |
{ |
357 |
if (!logtbl.entryStore) |
if (!logtbl.entryStore) |
358 |
return; |
return; |
359 |
(void) JS_DHashTableOperate(&logtbl, scope, JS_DHASH_REMOVE); |
(void) JS_DHashTableOperate(&logtbl, title, JS_DHASH_REMOVE); |
360 |
} |
} |
361 |
|
|
362 |
# define LOGIT(scope,op) logit(scope, op, __FILE__, __LINE__) |
# define LOGIT(title,op) logit(title, op, __FILE__, __LINE__) |
363 |
|
|
364 |
#else |
#else |
365 |
|
|
366 |
# define LOGIT(scope,op) /* nothing */ |
# define LOGIT(title, op) /* nothing */ |
367 |
|
|
368 |
#endif /* DEBUG_SCOPE_COUNT */ |
#endif /* DEBUG_SCOPE_COUNT */ |
369 |
|
|
370 |
/* |
/* |
371 |
* Return true if scope's ownercx, or the ownercx of a single-threaded scope |
* Return true if we would deadlock waiting in ClaimTitle on |
372 |
* for which ownercx is waiting to become multi-threaded and shared, is cx. |
* rt->titleSharingDone until ownercx finishes its request and shares a title. |
|
* That condition implies deadlock in ClaimScope if cx's thread were to wait |
|
|
* to share scope. |
|
373 |
* |
* |
374 |
* (i) rt->gcLock held |
* (i) rt->gcLock held |
375 |
*/ |
*/ |
376 |
static JSBool |
static bool |
377 |
WillDeadlock(JSTitle *title, JSContext *cx) |
WillDeadlock(JSContext *ownercx, JSThread *thread) |
378 |
{ |
{ |
379 |
JSContext *ownercx; |
JS_ASSERT(CURRENT_THREAD_IS_ME(thread)); |
380 |
|
JS_ASSERT(ownercx->thread != thread); |
381 |
|
|
382 |
do { |
for (;;) { |
383 |
ownercx = title->ownercx; |
JS_ASSERT(ownercx->thread); |
384 |
if (ownercx == cx) { |
JS_ASSERT(ownercx->requestDepth > 0); |
385 |
JS_RUNTIME_METER(cx->runtime, deadlocksAvoided); |
JSTitle *title = ownercx->thread->titleToShare; |
386 |
return JS_TRUE; |
if (!title || !title->ownercx) { |
387 |
|
/* |
388 |
|
* ownercx->thread doesn't wait or has just been notified that the |
389 |
|
* title became shared. |
390 |
|
*/ |
391 |
|
return false; |
392 |
} |
} |
393 |
} while (ownercx && (title = ownercx->titleToShare) != NULL); |
|
394 |
return JS_FALSE; |
/* |
395 |
|
* ownercx->thread is waiting in ClaimTitle for a context from some |
396 |
|
* thread to finish its request. If that thread is the current thread, |
397 |
|
* we would deadlock. Otherwise we must recursively check if that |
398 |
|
* thread waits for the current thread. |
399 |
|
*/ |
400 |
|
if (title->ownercx->thread == thread) { |
401 |
|
JS_RUNTIME_METER(ownercx->runtime, deadlocksAvoided); |
402 |
|
return true; |
403 |
|
} |
404 |
|
ownercx = title->ownercx; |
405 |
|
} |
406 |
} |
} |
407 |
|
|
408 |
|
static void |
409 |
|
FinishSharingTitle(JSContext *cx, JSTitle *title); |
410 |
|
|
411 |
/* |
/* |
412 |
* Make title multi-threaded, i.e. share its ownership among contexts in rt |
* Make title multi-threaded, i.e. share its ownership among contexts in rt |
413 |
* using a "thin" or (if necessary due to contention) "fat" lock. Called only |
* using a "thin" or (if necessary due to contention) "fat" lock. Called only |
433 |
title->u.link = NULL; /* null u.link for sanity ASAP */ |
title->u.link = NULL; /* null u.link for sanity ASAP */ |
434 |
JS_NOTIFY_ALL_CONDVAR(rt->titleSharingDone); |
JS_NOTIFY_ALL_CONDVAR(rt->titleSharingDone); |
435 |
} |
} |
436 |
js_InitLock(&title->lock); |
FinishSharingTitle(cx, title); |
|
title->u.count = 0; |
|
|
js_FinishSharingTitle(cx, title); |
|
437 |
} |
} |
438 |
|
|
439 |
/* |
/* |
440 |
* js_FinishSharingTitle is the tail part of ShareTitle, split out to become a |
* FinishSharingTitle is the tail part of ShareTitle, split out to become a |
441 |
* subroutine of JS_EndRequest too. The bulk of the work here involves making |
* subroutine of js_ShareWaitingTitles too. The bulk of the work here involves |
442 |
* mutable strings in the title's object's slots be immutable. We have to do |
* making mutable strings in the title's object's slots be immutable. We have |
443 |
* this because such strings will soon be available to multiple threads, so |
* to do this because such strings will soon be available to multiple threads, |
444 |
* their buffers can't be realloc'd any longer in js_ConcatStrings, and their |
* so their buffers can't be realloc'd any longer in js_ConcatStrings, and |
445 |
* members can't be modified by js_ConcatStrings, js_MinimizeDependentStrings, |
* their members can't be modified by js_ConcatStrings, js_UndependString or |
446 |
* or js_UndependString. |
* js_MinimizeDependentStrings. |
447 |
* |
* |
448 |
* The last bit of work done by js_FinishSharingTitle nulls title->ownercx and |
* The last bit of work done by this function nulls title->ownercx and updates |
449 |
* updates rt->sharedTitles. |
* rt->sharedTitles. |
450 |
*/ |
*/ |
451 |
|
static void |
452 |
void |
FinishSharingTitle(JSContext *cx, JSTitle *title) |
|
js_FinishSharingTitle(JSContext *cx, JSTitle *title) |
|
453 |
{ |
{ |
|
JSObjectMap *map; |
|
454 |
JSScope *scope; |
JSScope *scope; |
455 |
JSObject *obj; |
JSObject *obj; |
456 |
uint32 nslots, i; |
uint32 nslots, i; |
457 |
jsval v; |
jsval v; |
458 |
|
|
459 |
map = TITLE_TO_MAP(title); |
js_InitLock(&title->lock); |
460 |
if (!MAP_IS_NATIVE(map)) |
title->u.count = 0; /* NULL may not pun as 0 */ |
461 |
return; |
scope = TITLE_TO_SCOPE(title); |
|
scope = (JSScope *)map; |
|
|
|
|
462 |
obj = scope->object; |
obj = scope->object; |
463 |
if (obj) { |
if (obj) { |
464 |
nslots = scope->map.freeslot; |
nslots = scope->freeslot; |
465 |
for (i = 0; i != nslots; ++i) { |
for (i = 0; i != nslots; ++i) { |
466 |
v = STOBJ_GET_SLOT(obj, i); |
v = STOBJ_GET_SLOT(obj, i); |
467 |
if (JSVAL_IS_STRING(v) && |
if (JSVAL_IS_STRING(v) && |
482 |
} |
} |
483 |
|
|
484 |
/* |
/* |
485 |
|
* Notify all contexts that are currently in a request, which will give them a |
486 |
|
* chance to yield their current request. |
487 |
|
*/ |
488 |
|
void |
489 |
|
js_NudgeOtherContexts(JSContext *cx) |
490 |
|
{ |
491 |
|
JSRuntime *rt = cx->runtime; |
492 |
|
JSContext *acx = NULL; |
493 |
|
|
494 |
|
while ((acx = js_NextActiveContext(rt, acx)) != NULL) { |
495 |
|
if (cx != acx) |
496 |
|
JS_TriggerOperationCallback(acx); |
497 |
|
} |
498 |
|
} |
499 |
|
|
500 |
|
/* |
501 |
|
* Notify all contexts that are currently in a request and execute on this |
502 |
|
* specific thread. |
503 |
|
*/ |
504 |
|
static void |
505 |
|
NudgeThread(JSThread *thread) |
506 |
|
{ |
507 |
|
JSCList *link; |
508 |
|
JSContext *acx; |
509 |
|
|
510 |
|
link = &thread->contextList; |
511 |
|
while ((link = link->next) != &thread->contextList) { |
512 |
|
acx = CX_FROM_THREAD_LINKS(link); |
513 |
|
JS_ASSERT(acx->thread == thread); |
514 |
|
if (acx->requestDepth) |
515 |
|
JS_TriggerOperationCallback(acx); |
516 |
|
} |
517 |
|
} |
518 |
|
|
519 |
|
/* |
520 |
* Given a title with apparently non-null ownercx different from cx, try to |
* Given a title with apparently non-null ownercx different from cx, try to |
521 |
* set ownercx to cx, claiming exclusive (single-threaded) ownership of title. |
* set ownercx to cx, claiming exclusive (single-threaded) ownership of title. |
522 |
* If we claim ownership, return true. Otherwise, we wait for ownercx to be |
* If we claim ownership, return true. Otherwise, we wait for ownercx to be |
529 |
{ |
{ |
530 |
JSRuntime *rt; |
JSRuntime *rt; |
531 |
JSContext *ownercx; |
JSContext *ownercx; |
532 |
jsrefcount saveDepth; |
uint32 requestDebit; |
|
PRStatus stat; |
|
533 |
|
|
534 |
rt = cx->runtime; |
rt = cx->runtime; |
535 |
JS_RUNTIME_METER(rt, claimAttempts); |
JS_RUNTIME_METER(rt, claimAttempts); |
547 |
* If title->u.link is non-null, title has already been inserted on |
* If title->u.link is non-null, title has already been inserted on |
548 |
* the rt->titleSharingTodo list, because another thread's context |
* the rt->titleSharingTodo list, because another thread's context |
549 |
* already wanted to lock title while ownercx was running a request. |
* already wanted to lock title while ownercx was running a request. |
550 |
* We can't claim any title whose u.link is non-null at this point, |
* That context must still be in request and cannot be dead. We can |
551 |
* even if ownercx->requestDepth is 0 (see below where we suspend our |
* claim it if its thread matches ours but only if cx itself is in a |
552 |
* request before waiting on rt->titleSharingDone). |
* request. |
553 |
|
* |
554 |
|
* The latter check covers the case when the embedding triggers a call |
555 |
|
* to js_GC on a cx outside a request while having ownercx running a |
556 |
|
* request on the same thread, and then js_GC calls a mark hook or a |
557 |
|
* finalizer accessing the title. In this case we cannot claim the |
558 |
|
* title but must share it now as no title-sharing JS_EndRequest will |
559 |
|
* follow. |
560 |
*/ |
*/ |
561 |
if (!title->u.link && |
bool canClaim; |
562 |
(!js_ValidContextPointer(rt, ownercx) || |
if (title->u.link) { |
563 |
!ownercx->requestDepth || |
JS_ASSERT(js_ValidContextPointer(rt, ownercx)); |
564 |
ownercx->thread == cx->thread)) { |
JS_ASSERT(ownercx->requestDepth > 0); |
565 |
JS_ASSERT(title->u.count == 0); |
JS_ASSERT_IF(cx->requestDepth == 0, cx->thread == rt->gcThread); |
566 |
|
canClaim = (ownercx->thread == cx->thread && |
567 |
|
cx->requestDepth > 0); |
568 |
|
} else { |
569 |
|
canClaim = (!js_ValidContextPointer(rt, ownercx) || |
570 |
|
!ownercx->requestDepth || |
571 |
|
ownercx->thread == cx->thread); |
572 |
|
} |
573 |
|
if (canClaim) { |
574 |
title->ownercx = cx; |
title->ownercx = cx; |
575 |
JS_UNLOCK_GC(rt); |
JS_UNLOCK_GC(rt); |
576 |
JS_RUNTIME_METER(rt, claimedTitles); |
JS_RUNTIME_METER(rt, claimedTitles); |
578 |
} |
} |
579 |
|
|
580 |
/* |
/* |
581 |
* Avoid deadlock if title's owner context is waiting on a title that |
* Avoid deadlock if title's owner thread is waiting on a title that |
582 |
* we own, by revoking title's ownership. This approach to deadlock |
* the current thread owns, by revoking title's ownership. This |
583 |
* avoidance works because the engine never nests title locks. |
* approach to deadlock avoidance works because the engine never nests |
584 |
|
* title locks. |
585 |
* |
* |
586 |
* If cx could hold locks on ownercx->titleToShare, or if ownercx could |
* If cx->thread could hold locks on ownercx->thread->titleToShare, or |
587 |
* hold locks on title, we would need to keep reentrancy counts for all |
* if ownercx->thread could hold locks on title, we would need to keep |
588 |
* such "flyweight" (ownercx != NULL) locks, so that control would |
* reentrancy counts for all such "flyweight" (ownercx != NULL) locks, |
589 |
* unwind properly once these locks became "thin" or "fat". The engine |
* so that control would unwind properly once these locks became |
590 |
* promotes a title from exclusive to shared access only when locking, |
* "thin" or "fat". The engine promotes a title from exclusive to |
591 |
* never when holding or unlocking. |
* shared access only when locking, never when holding or unlocking. |
592 |
* |
* |
593 |
* Avoid deadlock before any of this title/context cycle detection if |
* Avoid deadlock before any of this title/context cycle detection if |
594 |
* cx is on the active GC's thread, because in that case, no requests |
* cx is on the active GC's thread, because in that case, no requests |
595 |
* will run until the GC completes. Any title wanted by the GC (from |
* will run until the GC completes. Any title wanted by the GC (from |
596 |
* a finalizer) that can't be claimed must become shared. |
* a finalizer or a mark hook) that can't be claimed must become |
597 |
|
* shared. |
598 |
*/ |
*/ |
599 |
if (rt->gcThread == cx->thread || |
if (rt->gcThread == cx->thread || WillDeadlock(ownercx, cx->thread)) { |
|
(ownercx->titleToShare && |
|
|
WillDeadlock(ownercx->titleToShare, cx))) { |
|
600 |
ShareTitle(cx, title); |
ShareTitle(cx, title); |
601 |
break; |
break; |
602 |
} |
} |
607 |
* non-null test, and avoid double-insertion bugs. |
* non-null test, and avoid double-insertion bugs. |
608 |
*/ |
*/ |
609 |
if (!title->u.link) { |
if (!title->u.link) { |
610 |
|
js_HoldScope(TITLE_TO_SCOPE(title)); |
611 |
title->u.link = rt->titleSharingTodo; |
title->u.link = rt->titleSharingTodo; |
612 |
rt->titleSharingTodo = title; |
rt->titleSharingTodo = title; |
|
js_HoldObjectMap(cx, TITLE_TO_MAP(title)); |
|
613 |
} |
} |
614 |
|
|
615 |
/* |
/* |
616 |
* Inline JS_SuspendRequest before we wait on rt->titleSharingDone, |
* Discount all the requests running on the current thread so a |
617 |
* saving and clearing cx->requestDepth so we don't deadlock if the |
* possible GC can proceed on another thread while we wait on |
618 |
* GC needs to run on ownercx. |
* rt->titleSharingDone. |
|
* |
|
|
* Unlike JS_SuspendRequest and JS_EndRequest, we must take care not |
|
|
* to decrement rt->requestCount if cx is active on the GC's thread, |
|
|
* because the GC has already reduced rt->requestCount to exclude all |
|
|
* such such contexts. |
|
619 |
*/ |
*/ |
620 |
saveDepth = cx->requestDepth; |
requestDebit = js_DiscountRequestsForGC(cx); |
621 |
if (saveDepth) { |
if (title->ownercx != ownercx) { |
622 |
cx->requestDepth = 0; |
/* |
623 |
if (rt->gcThread != cx->thread) { |
* js_DiscountRequestsForGC released and reacquired the GC lock, |
624 |
JS_ASSERT(rt->requestCount > 0); |
* and the title was taken or shared. Start over. |
625 |
rt->requestCount--; |
*/ |
626 |
if (rt->requestCount == 0) |
js_RecountRequestsAfterGC(rt, requestDebit); |
627 |
JS_NOTIFY_REQUEST_DONE(rt); |
continue; |
|
} |
|
628 |
} |
} |
629 |
|
|
630 |
/* |
/* |
631 |
* We know that some other thread's context owns title, which is now |
* We know that some other thread's context owns title, which is now |
632 |
* linked onto rt->titleSharingTodo, awaiting the end of that other |
* linked onto rt->titleSharingTodo, awaiting the end of that other |
633 |
* thread's request. So it is safe to wait on rt->titleSharingDone. |
* thread's request. So it is safe to wait on rt->titleSharingDone. |
634 |
|
* But before waiting, we force the operation callback for that other |
635 |
|
* thread so it can quickly suspend. |
636 |
*/ |
*/ |
637 |
cx->titleToShare = title; |
NudgeThread(ownercx->thread); |
638 |
stat = PR_WaitCondVar(rt->titleSharingDone, PR_INTERVAL_NO_TIMEOUT); |
|
639 |
|
JS_ASSERT(!cx->thread->titleToShare); |
640 |
|
cx->thread->titleToShare = title; |
641 |
|
#ifdef DEBUG |
642 |
|
PRStatus stat = |
643 |
|
#endif |
644 |
|
PR_WaitCondVar(rt->titleSharingDone, PR_INTERVAL_NO_TIMEOUT); |
645 |
JS_ASSERT(stat != PR_FAILURE); |
JS_ASSERT(stat != PR_FAILURE); |
646 |
|
|
647 |
/* |
js_RecountRequestsAfterGC(rt, requestDebit); |
|
* Inline JS_ResumeRequest after waiting on rt->titleSharingDone, |
|
|
* restoring cx->requestDepth. Same note as above for the inlined, |
|
|
* specialized JS_SuspendRequest code: beware rt->gcThread. |
|
|
*/ |
|
|
if (saveDepth) { |
|
|
if (rt->gcThread != cx->thread) { |
|
|
while (rt->gcLevel > 0) |
|
|
JS_AWAIT_GC_DONE(rt); |
|
|
rt->requestCount++; |
|
|
} |
|
|
cx->requestDepth = saveDepth; |
|
|
} |
|
648 |
|
|
649 |
/* |
/* |
650 |
* Don't clear cx->titleToShare until after we're through waiting on |
* Don't clear titleToShare until after we're through waiting on |
651 |
* all condition variables protected by rt->gcLock -- that includes |
* all condition variables protected by rt->gcLock -- that includes |
652 |
* rt->titleSharingDone *and* rt->gcDone (hidden in JS_AWAIT_GC_DONE, |
* rt->titleSharingDone *and* rt->gcDone (hidden in the call to |
653 |
* in the inlined JS_ResumeRequest code immediately above). |
* js_RecountRequestsAfterGC immediately above). |
654 |
* |
* |
655 |
* Otherwise, the GC could easily deadlock with another thread that |
* Otherwise, the GC could easily deadlock with another thread that |
656 |
* owns a title wanted by a finalizer. By keeping cx->titleToShare |
* owns a title wanted by a finalizer. By keeping cx->titleToShare |
658 |
* results in the finalized object's title being shared (it must, of |
* results in the finalized object's title being shared (it must, of |
659 |
* course, have other, live objects sharing it). |
* course, have other, live objects sharing it). |
660 |
*/ |
*/ |
661 |
cx->titleToShare = NULL; |
cx->thread->titleToShare = NULL; |
662 |
} |
} |
663 |
|
|
664 |
JS_UNLOCK_GC(rt); |
JS_UNLOCK_GC(rt); |
665 |
return JS_FALSE; |
return JS_FALSE; |
666 |
} |
} |
667 |
|
|
668 |
|
void |
669 |
|
js_ShareWaitingTitles(JSContext *cx) |
670 |
|
{ |
671 |
|
JSTitle *title, **todop; |
672 |
|
bool shared; |
673 |
|
|
674 |
|
/* See whether cx has any single-threaded titles to start sharing. */ |
675 |
|
todop = &cx->runtime->titleSharingTodo; |
676 |
|
shared = false; |
677 |
|
while ((title = *todop) != NO_TITLE_SHARING_TODO) { |
678 |
|
if (title->ownercx != cx) { |
679 |
|
todop = &title->u.link; |
680 |
|
continue; |
681 |
|
} |
682 |
|
*todop = title->u.link; |
683 |
|
title->u.link = NULL; /* null u.link for sanity ASAP */ |
684 |
|
|
685 |
|
/* |
686 |
|
* If js_DropScope returns false, we held the last ref to scope. The |
687 |
|
* waiting thread(s) must have been killed, after which the GC |
688 |
|
* collected the object that held this scope. Unlikely, because it |
689 |
|
* requires that the GC ran (e.g., from an operation callback) |
690 |
|
* during this request, but possible. |
691 |
|
*/ |
692 |
|
if (js_DropScope(cx, TITLE_TO_SCOPE(title), NULL)) { |
693 |
|
FinishSharingTitle(cx, title); /* set ownercx = NULL */ |
694 |
|
shared = true; |
695 |
|
} |
696 |
|
} |
697 |
|
if (shared) |
698 |
|
JS_NOTIFY_ALL_CONDVAR(cx->runtime->titleSharingDone); |
699 |
|
} |
700 |
|
|
701 |
/* Exported to js.c, which calls it via OBJ_GET_* and JSVAL_IS_* macros. */ |
/* Exported to js.c, which calls it via OBJ_GET_* and JSVAL_IS_* macros. */ |
702 |
JS_FRIEND_API(jsval) |
JS_FRIEND_API(jsval) |
703 |
js_GetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot) |
js_GetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot) |
730 |
scope = OBJ_SCOPE(obj); |
scope = OBJ_SCOPE(obj); |
731 |
title = &scope->title; |
title = &scope->title; |
732 |
JS_ASSERT(title->ownercx != cx); |
JS_ASSERT(title->ownercx != cx); |
733 |
JS_ASSERT(slot < obj->map->freeslot); |
JS_ASSERT(slot < scope->freeslot); |
734 |
|
|
735 |
/* |
/* |
736 |
* Avoid locking if called from the GC. Also avoid locking an object |
* Avoid locking if called from the GC. Also avoid locking an object |
760 |
if (!NativeCompareAndSwap(&tl->owner, me, 0)) { |
if (!NativeCompareAndSwap(&tl->owner, me, 0)) { |
761 |
/* Assert that scope locks never revert to flyweight. */ |
/* Assert that scope locks never revert to flyweight. */ |
762 |
JS_ASSERT(title->ownercx != cx); |
JS_ASSERT(title->ownercx != cx); |
763 |
LOGIT(scope, '1'); |
LOGIT(title, '1'); |
764 |
title->u.count = 1; |
title->u.count = 1; |
765 |
js_UnlockObj(cx, obj); |
js_UnlockObj(cx, obj); |
766 |
} |
} |
825 |
scope = OBJ_SCOPE(obj); |
scope = OBJ_SCOPE(obj); |
826 |
title = &scope->title; |
title = &scope->title; |
827 |
JS_ASSERT(title->ownercx != cx); |
JS_ASSERT(title->ownercx != cx); |
828 |
JS_ASSERT(slot < obj->map->freeslot); |
JS_ASSERT(slot < scope->freeslot); |
829 |
|
|
830 |
/* |
/* |
831 |
* Avoid locking if called from the GC. Also avoid locking an object |
* Avoid locking if called from the GC. Also avoid locking an object |
850 |
if (!NativeCompareAndSwap(&tl->owner, me, 0)) { |
if (!NativeCompareAndSwap(&tl->owner, me, 0)) { |
851 |
/* Assert that scope locks never revert to flyweight. */ |
/* Assert that scope locks never revert to flyweight. */ |
852 |
JS_ASSERT(title->ownercx != cx); |
JS_ASSERT(title->ownercx != cx); |
853 |
LOGIT(scope, '1'); |
LOGIT(title, '1'); |
854 |
title->u.count = 1; |
title->u.count = 1; |
855 |
js_UnlockObj(cx, obj); |
js_UnlockObj(cx, obj); |
856 |
} |
} |
1306 |
JS_ASSERT(0); /* unbalanced unlock */ |
JS_ASSERT(0); /* unbalanced unlock */ |
1307 |
return; |
return; |
1308 |
} |
} |
1309 |
LOGIT(scope, '-'); |
LOGIT(title, '-'); |
1310 |
if (--title->u.count == 0) |
if (--title->u.count == 0) |
1311 |
ThinUnlock(&title->lock, me); |
ThinUnlock(&title->lock, me); |
1312 |
} |
} |
1374 |
/* |
/* |
1375 |
* Reset oldtitle's lock state so that it is completely unlocked. |
* Reset oldtitle's lock state so that it is completely unlocked. |
1376 |
*/ |
*/ |
1377 |
LOGIT(oldscope, '0'); |
LOGIT(oldtitle, '0'); |
1378 |
oldtitle->u.count = 0; |
oldtitle->u.count = 0; |
1379 |
ThinUnlock(&oldtitle->lock, CX_THINLOCK_ID(cx)); |
ThinUnlock(&oldtitle->lock, CX_THINLOCK_ID(cx)); |
1380 |
} |
} |
1445 |
void |
void |
1446 |
js_FinishTitle(JSContext *cx, JSTitle *title) |
js_FinishTitle(JSContext *cx, JSTitle *title) |
1447 |
{ |
{ |
1448 |
|
#ifdef DEBUG_SCOPE_COUNT |
1449 |
|
js_unlog_title(title); |
1450 |
|
#endif |
1451 |
|
|
1452 |
#ifdef JS_THREADSAFE |
#ifdef JS_THREADSAFE |
1453 |
/* Title must be single-threaded at this point, so set ownercx. */ |
/* Title must be single-threaded at this point, so set ownercx. */ |
1454 |
JS_ASSERT(title->u.count == 0); |
JS_ASSERT(title->u.count == 0); |
1468 |
JSBool |
JSBool |
1469 |
js_IsObjLocked(JSContext *cx, JSObject *obj) |
js_IsObjLocked(JSContext *cx, JSObject *obj) |
1470 |
{ |
{ |
1471 |
JSScope *scope = OBJ_SCOPE(obj); |
return js_IsTitleLocked(cx, &OBJ_SCOPE(obj)->title); |
|
|
|
|
return MAP_IS_NATIVE(&scope->map) && js_IsTitleLocked(cx, &scope->title); |
|
1472 |
} |
} |
1473 |
|
|
1474 |
JSBool |
JSBool |