jslock.c

来自「一个基于alice开发的机器人」· C语言 代码 · 共 1,242 行 · 第 1/3 页

C
1,242
字号
/* -*- 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 "prthread.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 void
js_LockGlobal(void *id)
{
    uint32 i = GLOBAL_LOCK_INDEX(id);
    PR_Lock(global_locks[i]);
}

static void
js_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 int
js_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 int
js_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(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC)

static JS_INLINE int
js_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 int
js_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 */

jsword
js_CurrentThreadId()
{
    return CurrentThreadId();
}

void
js_InitLock(JSThinLock *tl)
{
#ifdef NSPR_LOCK
    tl->owner = 0;
    tl->fat = (JSFatLock*)JS_NEW_LOCK();
#else
    memset(tl, 0, sizeof(JSThinLock));
#endif
}

void
js_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 void
logit(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;
}

void
js_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 JSBool
WillDeadlock(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 void
ShareScope(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 = scope->ownercx->thread;
#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_MACRO

void
js_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 JSBool
ClaimScope(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.
         *
         * If scope->u.link is non-null, scope has already been inserted on
         * the rt->scopeSharingTodo list, because another thread's context
         * already wanted to lock scope while ownercx was running a request.
         * We can't claim any scope whose u.link is non-null at this point,
         * even if ownercx->requestDepth is 0 (see below where we suspend our
         * request before waiting on rt->scopeSharingDone).
         */
        if (!scope->u.link &&
            (!js_ValidContextPointer(rt, ownercx) ||
             !ownercx->requestDepth ||

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?