/[jscoverage]/trunk/js/jslock.cpp
ViewVC logotype

Diff of /trunk/js/jslock.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 332 by siliconforks, Thu Oct 23 19:03:33 2008 UTC revision 460 by siliconforks, Sat Sep 26 23:15:22 2009 UTC
# Line 69  Line 69 
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      }      }
# Line 92  Line 94 
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__)
# Line 235  Line 233 
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 {
# Line 305  Line 314 
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 {
# Line 316  Line 325 
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    
# Line 326  Line 335 
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
# Line 406  Line 433 
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) &&
# Line 461  Line 482 
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
# Line 473  Line 529 
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);
# Line 492  Line 547 
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);
# Line 508  Line 578 
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          }          }
# Line 537  Line 607 
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
# Line 598  Line 658 
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)
# Line 637  Line 730 
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
# Line 667  Line 760 
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              }              }
# Line 732  Line 825 
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
# Line 757  Line 850 
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              }              }
# Line 1213  Line 1306 
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  }  }
# Line 1281  Line 1374 
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  }  }
# Line 1352  Line 1445 
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);
# Line 1371  Line 1468 
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

Legend:
Removed from v.332  
changed lines
  Added in v.460

  ViewVC Help
Powered by ViewVC 1.1.24