📄 jslock.c
字号:
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */#ifdef JS_THREADSAFE/* * JS locking stubs. */#include "jsstddef.h"#include <stdlib.h>#include "jspubtd.h"#include "jsutil.h" /* Added by JSIFY */#include "jstypes.h"#include "jsbit.h"#include "jscntxt.h"#include "jsdtoa.h"#include "jsgc.h"#include "jslock.h"#include "jsscope.h"#include "jsstr.h"#define ReadWord(W) (W)#ifndef NSPR_LOCK#include <memory.h>static PRLock **global_locks;static uint32 global_lock_count = 1;static uint32 global_locks_log2 = 0;static uint32 global_locks_mask = 0;#define GLOBAL_LOCK_INDEX(id) (((uint32)(id) >> 2) & global_locks_mask)static voidjs_LockGlobal(void *id){ uint32 i = GLOBAL_LOCK_INDEX(id); PR_Lock(global_locks[i]);}static voidjs_UnlockGlobal(void *id){ uint32 i = GLOBAL_LOCK_INDEX(id); PR_Unlock(global_locks[i]);}/* Exclude Alpha NT. */#if defined(_WIN32) && defined(_M_IX86)#pragma warning( disable : 4035 )static JS_INLINE intjs_CompareAndSwap(jsword *w, jsword ov, jsword nv){ __asm { mov eax, ov mov ecx, nv mov ebx, w lock cmpxchg [ebx], ecx sete al and eax, 1h }}#elif defined(__GNUC__) && defined(__i386__)/* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */static JS_INLINE intjs_CompareAndSwap(jsword *w, jsword ov, jsword nv){ unsigned int res; __asm__ __volatile__ ( "lock\n" "cmpxchgl %2, (%1)\n" "sete %%al\n" "andl $1, %%eax\n" : "=a" (res) : "r" (w), "r" (nv), "a" (ov) : "cc", "memory"); return (int)res;}#elif (defined(__USLC__) || defined(_SCO_DS)) && defined(i386)/* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */asm intjs_CompareAndSwap(jsword *w, jsword ov, jsword nv){%ureg w, nv; movl ov,%eax lock cmpxchgl nv,(w) sete %al andl $1,%eax%ureg w; mem ov, nv; movl ov,%eax movl nv,%ecx lock cmpxchgl %ecx,(w) sete %al andl $1,%eax%ureg nv; movl ov,%eax movl w,%edx lock cmpxchgl nv,(%edx) sete %al andl $1,%eax%mem w, ov, nv; movl ov,%eax movl nv,%ecx movl w,%edx lock cmpxchgl %ecx,(%edx) sete %al andl $1,%eax}#pragma asm full_optimization js_CompareAndSwap#elif defined(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC)static JS_INLINE intjs_CompareAndSwap(jsword *w, jsword ov, jsword nv){#if defined(__GNUC__) unsigned int res; JS_ASSERT(ov != nv); asm volatile ("\stbar\n\cas [%1],%2,%3\n\cmp %2,%3\n\be,a 1f\n\mov 1,%0\n\mov 0,%0\n\1:" : "=r" (res) : "r" (w), "r" (ov), "r" (nv)); return (int)res;#else /* !__GNUC__ */ extern int compare_and_swap(jsword*, jsword, jsword); JS_ASSERT(ov != nv); return compare_and_swap(w, ov, nv);#endif}#elif defined(AIX)#include <sys/atomic_op.h>static JS_INLINE intjs_CompareAndSwap(jsword *w, jsword ov, jsword nv){ return !_check_lock((atomic_p)w, ov, nv);}#else#error "Define NSPR_LOCK if your platform lacks a compare-and-swap instruction."#endif /* arch-tests */#endif /* !NSPR_LOCK */voidjs_InitLock(JSThinLock *tl){#ifdef NSPR_LOCK tl->owner = 0; tl->fat = (JSFatLock*)JS_NEW_LOCK();#else memset(tl, 0, sizeof(JSThinLock));#endif}voidjs_FinishLock(JSThinLock *tl){#ifdef NSPR_LOCK tl->owner = 0xdeadbeef; if (tl->fat) JS_DESTROY_LOCK(((JSLock*)tl->fat));#else JS_ASSERT(tl->owner == 0); JS_ASSERT(tl->fat == NULL);#endif}static void js_Dequeue(JSThinLock *);#ifdef DEBUG_SCOPE_COUNT#include <stdio.h>#include "jsdhash.h"static FILE *logfp;static JSDHashTable logtbl;typedef struct logentry { JSDHashEntryStub stub; char op; const char *file; int line;} logentry;static voidlogit(JSScope *scope, char op, const char *file, int line){ logentry *entry; if (!logfp) { logfp = fopen("/tmp/scope.log", "w"); if (!logfp) return; setvbuf(logfp, NULL, _IONBF, 0); } fprintf(logfp, "%p %c %s %d\n", scope, op, file, line); if (!logtbl.entryStore && !JS_DHashTableInit(&logtbl, JS_DHashGetStubOps(), NULL, sizeof(logentry), 100)) { return; } entry = (logentry *) JS_DHashTableOperate(&logtbl, scope, JS_DHASH_ADD); if (!entry) return; entry->stub.key = scope; entry->op = op; entry->file = file; entry->line = line;}voidjs_unlog_scope(JSScope *scope){ if (!logtbl.entryStore) return; (void) JS_DHashTableOperate(&logtbl, scope, JS_DHASH_REMOVE);}# define LOGIT(scope,op) logit(scope, op, __FILE__, __LINE__)#else# define LOGIT(scope,op) /* nothing */#endif /* DEBUG_SCOPE_COUNT *//* * Return true if scope's ownercx, or the ownercx of a single-threaded scope * for which ownercx is waiting to become multi-threaded and shared, is cx. * That condition implies deadlock in ClaimScope if cx's thread were to wait * to share scope. * * (i) rt->gcLock held */static JSBoolWillDeadlock(JSScope *scope, JSContext *cx){ JSContext *ownercx; do { ownercx = scope->ownercx; if (ownercx == cx) { JS_RUNTIME_METER(cx->runtime, deadlocksAvoided); return JS_TRUE; } } while (ownercx && (scope = ownercx->scopeToShare) != NULL); return JS_FALSE;}/* * Make scope multi-threaded, i.e. share its ownership among contexts in rt * using a "thin" or (if necessary due to contention) "fat" lock. Called only * from ClaimScope, immediately below, when we detect deadlock were we to wait * for scope's lock, because its ownercx is waiting on a scope owned by the * calling cx. * * (i) rt->gcLock held */static voidShareScope(JSRuntime *rt, JSScope *scope){ JSScope **todop; if (scope->u.link) { for (todop = &rt->scopeSharingTodo; *todop != scope; todop = &(*todop)->u.link) { JS_ASSERT(*todop != NO_SCOPE_SHARING_TODO); } *todop = scope->u.link; scope->u.link = NULL; /* null u.link for sanity ASAP */ JS_NOTIFY_ALL_CONDVAR(rt->scopeSharingDone); } js_InitLock(&scope->lock); if (scope == rt->setSlotScope) { /* * Nesting locks on another thread that's using scope->ownercx: give * the held lock a reentrancy count of 1 and set its lock.owner field * directly (no compare-and-swap needed while scope->ownercx is still * non-null). See below in ClaimScope, before the ShareScope call, * for more on why this is necessary. * * If NSPR_LOCK is defined, we cannot deadlock holding rt->gcLock and * acquiring scope->lock.fat here, against another thread holding that * fat lock and trying to grab rt->gcLock. This is because no other * thread can attempt to acquire scope->lock.fat until scope->ownercx * is null *and* our thread has released rt->gcLock, which interlocks * scope->ownercx's transition to null against tests of that member * in ClaimScope. */ scope->lock.owner = CX_THINLOCK_ID(scope->ownercx);#ifdef NSPR_LOCK JS_ACQUIRE_LOCK((JSLock*)scope->lock.fat);#endif scope->u.count = 1; } else { scope->u.count = 0; } js_FinishSharingScope(rt, scope);}/* * js_FinishSharingScope is the tail part of ShareScope, split out to become a * subroutine of JS_EndRequest too. The bulk of the work here involves making * mutable strings in the scope's object's slots be immutable. We have to do * this because such strings will soon be available to multiple threads, so * their buffers can't be realloc'd any longer in js_ConcatStrings, and their * members can't be modified by js_ConcatStrings, js_MinimizeDependentStrings, * or js_UndependString. * * The last bit of work done by js_FinishSharingScope nulls scope->ownercx and * updates rt->sharedScopes. */#define MAKE_STRING_IMMUTABLE(rt, v, vp) \ JS_BEGIN_MACRO \ JSString *str_ = JSVAL_TO_STRING(v); \ uint8 *flagp_ = js_GetGCThingFlags(str_); \ if (*flagp_ & GCF_MUTABLE) { \ if (JSSTRING_IS_DEPENDENT(str_) && \ !js_UndependString(NULL, str_)) { \ JS_RUNTIME_METER(rt, badUndependStrings); \ *vp = JSVAL_VOID; \ } else { \ *flagp_ &= ~GCF_MUTABLE; \ } \ } \ JS_END_MACROvoidjs_FinishSharingScope(JSRuntime *rt, JSScope *scope){ JSObject *obj; uint32 nslots; jsval v, *vp, *end; obj = scope->object; nslots = JS_MIN(obj->map->freeslot, obj->map->nslots); for (vp = obj->slots, end = vp + nslots; vp < end; vp++) { v = *vp; if (JSVAL_IS_STRING(v)) MAKE_STRING_IMMUTABLE(rt, v, vp); } scope->ownercx = NULL; /* NB: set last, after lock init */ JS_RUNTIME_METER(rt, sharedScopes);}/* * Given a scope with apparently non-null ownercx different from cx, try to * set ownercx to cx, claiming exclusive (single-threaded) ownership of scope. * If we claim ownership, return true. Otherwise, we wait for ownercx to be * set to null (indicating that scope is multi-threaded); or if waiting would * deadlock, we set ownercx to null ourselves via ShareScope. In any case, * once ownercx is null we return false. */static JSBoolClaimScope(JSScope *scope, JSContext *cx){ JSRuntime *rt; JSContext *ownercx; jsrefcount saveDepth; PRStatus stat; rt = cx->runtime; JS_RUNTIME_METER(rt, claimAttempts); JS_LOCK_GC(rt); /* Reload in case ownercx went away while we blocked on the lock. */ while ((ownercx = scope->ownercx) != NULL) { /* * Avoid selflock if ownercx is dead, or is not running a request, or * has the same thread as cx. Set scope->ownercx to cx so that the * matching JS_UNLOCK_SCOPE or JS_UNLOCK_OBJ macro call will take the * fast path around the corresponding js_UnlockScope or js_UnlockObj * function call. *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -