lib/hgfsServerPolicyGuest/Makefile \
lib/image/Makefile \
lib/impersonate/Makefile \
+ lib/lock/Makefile \
lib/message/Makefile \
lib/misc/Makefile \
lib/netUtil/Makefile \
SUBDIRS += image
endif
SUBDIRS += impersonate
+SUBDIRS += lock
SUBDIRS += message
SUBDIRS += misc
SUBDIRS += netUtil
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2009 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+#ifndef _USERLOCK_H_
+#define _USERLOCK_H_
+
+
+#define INCLUDE_ALLOW_USERLEVEL
+#define INCLUDE_ALLOW_VMCORE
+#include "includeCheck.h"
+
+#include "vm_atomic.h"
+#include "vm_basic_types.h"
+#include "vm_basic_defs.h"
+#include "vthreadBase.h"
+
+typedef struct MXUserExclLock MXUserExclLock;
+typedef struct MXUserRecLock MXUserRecLock;
+typedef struct MXUserRWLock MXUserRWLock;
+typedef struct MXUserCondVar MXUserCondVar;
+
+/*
+ * Exclusive ownership lock
+ */
+
+MXUserExclLock *
+MXUser_CreateExclLock(const char *name,
+ MX_Rank rank);
+
+void MXUser_AcquireExclLock(MXUserExclLock *lock);
+Bool MXUser_TryAcquireExclLock(MXUserExclLock *lock);
+void MXUser_ReleaseExclLock(MXUserExclLock *lock);
+void MXUser_DestroyExclLock(MXUserExclLock *lock);
+Bool MXUser_IsCurThreadHoldingExclLock(const MXUserExclLock *lock);
+
+MXUserExclLock *
+MXUser_CreateSingletonExclLock(Atomic_Ptr *lockStorage,
+ const char *name,
+ MX_Rank rank);
+
+Bool MXUser_ControlExclLock(MXUserExclLock *lock,
+ uint32 command,
+ ...);
+
+MXUserCondVar *
+MXUser_CreateCondVarExclLock(MXUserExclLock *lock);
+
+void MXUser_WaitCondVarExclLock(MXUserExclLock *lock,
+ MXUserCondVar *condVar);
+
+
+/*
+ * Recursive lock.
+ */
+
+MXUserRecLock *
+MXUser_CreateRecLock(const char *name,
+ MX_Rank rank);
+
+void MXUser_AcquireRecLock(MXUserRecLock *lock);
+Bool MXUser_TryAcquireRecLock(MXUserRecLock *lock);
+void MXUser_ReleaseRecLock(MXUserRecLock *lock);
+void MXUser_DestroyRecLock(MXUserRecLock *lock);
+Bool MXUser_IsCurThreadHoldingRecLock(const MXUserRecLock *lock);
+
+MXUserRecLock *
+MXUser_CreateSingletonRecLock(Atomic_Ptr *lockStorage,
+ const char *name,
+ MX_Rank rank);
+
+Bool MXUser_ControlRecLock(MXUserRecLock *lock,
+ uint32 command,
+ ...);
+
+MXUserCondVar *
+MXUser_CreateCondVarRecLock(MXUserRecLock *lock);
+
+void MXUser_WaitCondVarRecLock(MXUserRecLock *lock,
+ MXUserCondVar *condVar);
+
+/*
+ * Read-write lock
+ */
+
+MXUserRWLock *
+MXUser_CreateRWLock(const char *name,
+ MX_Rank rank);
+
+void MXUser_AcquireForRead(MXUserRWLock *lock);
+void MXUser_AcquireForWrite(MXUserRWLock *lock);
+void MXUser_ReleaseRWLock(MXUserRWLock *lock);
+void MXUser_DestroyRWLock(MXUserRWLock *lock);
+
+#define MXUSER_RW_FOR_READ 0
+#define MXUSER_RW_FOR_WRITE 1
+#define MXUSER_RW_LOCKED 2
+
+Bool MXUser_IsCurThreadHoldingRWLock(MXUserRWLock *lock,
+ uint32 queryType);
+
+/*
+ * Generic conditional variable functions.
+ */
+
+void MXUser_SignalCondVar(MXUserCondVar *condVar);
+void MXUser_BroadcastCondVar(MXUserCondVar *condVar);
+void MXUser_DestroyCondVar(MXUserCondVar *condVar);
+
+/*
+ * MXUser_Control[Excl, Rec] commands
+ */
+
+#define MXUSER_CONTROL_ACQUISITION_HISTO 0 // numBins, binWidth
+#define MXUSER_CONTROL_HELD_HISTO 1 // numBins, binWidth
+
+#if defined(VMX86_VMX)
+#include "mutex.h"
+
+MXUserRecLock *MXUser_BindMXMutexRec(MX_MutexRec *mutex);
+MX_MutexRec *MXUser_GetRecLockVmm(const MXUserRecLock *lock);
+MX_Rank MXUser_GetRecLockRank(const MXUserRecLock *lock);
+
+MXUserRecLock *MXUser_InitFromMXRec(const char *name, MX_MutexRec *mutex,
+ MX_Rank rank, Bool isBelowBull);
+
+#if defined(__i386__) || defined(__x86_64__)
+#if defined(VMX86_STATS)
+#define MXUSER_STATS // stats "only inside the VMX" when requested
+#endif
+#endif // X86 and X86-64
+
+#endif // VMX86_VMX
+
+#if defined(VMX86_DEBUG)
+#define MXUSER_DEBUG // debugging "everywhere" when requested
+#endif
+
+#if defined(MXUSER_DEBUG)
+Bool MXUser_AnyLocksHeld(VThreadID tid);
+void MXUser_TryAcquireFailureControl(Bool (*func)(const char *lockName));
+#endif
+
+#if defined(MXUSER_STATS)
+void MXUser_StatisticsControl(double contentionRatio,
+ uint64 minCount);
+void MXUser_LogStats(unsigned epoch);
+#endif
+
+extern void MXUser_SetInPanic(void);
+extern Bool MXUser_InPanic(void);
+
+#endif // _USERLOCK_H_
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2006 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+/*
+ * vthreadBase.h --
+ *
+ * Subset of vthread defines that are used by libs that need to make
+ * vthread calls but don't actually do any vthreading.
+ *
+ * May be used without lib/thread or with lib/thread. (But don't try
+ * to do both simultaneously, since lib/thread needs to do more
+ * bookkeeping.)
+ */
+
+#ifndef VTHREAD_BASE_H
+#define VTHREAD_BASE_H
+
+#define INCLUDE_ALLOW_USERLEVEL
+
+#define INCLUDE_ALLOW_VMCORE
+#include "includeCheck.h"
+
+#include "vm_atomic.h"
+
+#if !defined VMM && !defined WIN32
+#define VTHREAD_USE_PTHREAD 1
+#include <signal.h>
+#endif
+
+
+/*
+ * Types
+ */
+
+typedef unsigned VThreadID;
+
+#define VTHREAD_INVALID_ID (VThreadID)(~0u)
+
+/* XXX Vestigial need as an MXState array size */
+#define VTHREAD_MAX_THREADS 96
+
+#ifdef VMM
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThread_CurID --
+ * VThread_CurName --
+ *
+ * Get the current thread ID / name. This is only inline for the monitor.
+ *
+ * The extern symbols herein are defined in monitor.c and
+ * initialized by SharedArea_PowerOn.
+ *
+ * Results:
+ * Thread ID / name.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE VThreadID
+VThread_CurID(void)
+{
+ extern const VThreadID vthreadCurID;
+
+ return vthreadCurID;
+}
+
+static INLINE const char *
+VThread_CurName(void)
+{
+ extern const char vcpuThreadName[];
+
+ return vcpuThreadName;
+}
+
+#else
+
+#define VTHREAD_VMX_ID 0
+#define VTHREAD_MKS_ID 1
+#define VTHREAD_UI_ID 2
+#define VTHREAD_OTHER_ID 3
+#define VTHREAD_ALLOCSTART_ID 4
+
+#define VTHREADBASE_MAX_NAME 32 /* Arbitrary */
+
+
+typedef struct {
+ VThreadID id;
+ char name[VTHREADBASE_MAX_NAME];
+#if !defined _WIN32
+ Atomic_Int signalNestCount;
+#endif
+} VThreadBaseData;
+
+/* Common VThreadBase functions */
+const char *VThreadBase_CurName(void);
+VThreadID VThreadBase_CurID(void);
+void VThreadBase_SetName(const char *name);
+
+/* Note: only used to set VTHREAD_ID_MKS nowadays */
+VThreadID VThreadBase_InitThread(VThreadID tid, const char *name);
+
+/* For implementing a thread library */
+Bool VThreadBase_InitWithTLS(VThreadBaseData *tls);
+VThreadBaseData * VThreadBase_ForgetSelf(void);
+void VThreadBase_SetNoIDFunc(void (*func)(void));
+
+/* Match up historical VThread_ names with VThreadBase_ names */
+static INLINE const char *
+VThread_CurName(void)
+{ return VThreadBase_CurName(); }
+
+static INLINE VThreadID
+VThread_CurID(void)
+{ return VThreadBase_CurID(); }
+
+static INLINE void
+VThread_SetName(const char *name)
+{ VThreadBase_SetName(name); }
+
+
+#ifdef _WIN32
+static INLINE Bool
+VThreadBase_IsInSignal(void)
+{
+ /* Win32 does not worry about async-signal-safety. */
+ return FALSE;
+}
+#else
+Bool VThreadBase_IsInSignal(void);
+void VThreadBase_SetIsInSignal(VThreadID tid, Bool isInSignal);
+int VThreadBase_SigMask(int how, const sigset_t *newmask, sigset_t *oldmask);
+#endif
+
+#endif /* VMM */
+
+
+#endif // VTHREAD_BASE_H
--- /dev/null
+################################################################################
+### Copyright 2010 VMware, Inc. All rights reserved.
+###
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of version 2 of the GNU General Public License as
+### published by the Free Software Foundation.
+###
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software
+### Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+################################################################################
+
+noinst_LTLIBRARIES = libLock.la
+
+libLock_la_SOURCES =
+libLock_la_SOURCES += ul.c
+libLock_la_SOURCES += ulCondVar.c
+libLock_la_SOURCES += ulExcl.c
+libLock_la_SOURCES += ulRec.c
+libLock_la_SOURCES += ulRW.c
+
+AM_CFLAGS = @LIB_USER_CPPFLAGS@
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2009 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+#include "vmware.h"
+#include "str.h"
+#include "util.h"
+#include "userlock.h"
+#include "ulInt.h"
+#include "hashTable.h"
+
+
+static Bool mxInPanic = FALSE; // track when involved in a panic
+
+Bool (*MXUserTryAcquireForceFail)() = NULL;
+
+static MX_Rank (*MXUserMxCheckRank)(void) = NULL;
+static void (*MXUserMxLockLister)(void) = NULL;
+
+
+#if defined(MXUSER_DEBUG) || defined(MXUSER_STATS)
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserMaintainMaxTid --
+ *
+ * Maintain the maximum known thread ID.
+ *
+ * Results:
+ * As Above.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Atomic_uint32 mxMaxThreadID; // implicitly initialized to 0 -- mbellon
+
+static void
+MXUserMaintainMaxTid(VThreadID tid) // IN:
+{
+ while (TRUE) {
+ uint32 curValue = Atomic_Read(&mxMaxThreadID);
+
+ if (tid <= curValue) {
+ break;
+ }
+
+ Atomic_ReadIfEqualWrite(&mxMaxThreadID, curValue, tid);
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserGetPerThread --
+ *
+ * Return a pointer to the per thread data for the specified thread.
+ *
+ * Memory is allocated for the specified thread as necessary. This memory
+ * is never released since it it is highly likely a thread will use a
+ * lock and need to record data in the perThread.
+ *
+ * Results:
+ * NULL mayAlloc was FALSE and the thread doesn't have a perThread (yet)
+ * !NULL the perThread of the specified thread
+ *
+ * Side effects:
+ * Memory may be allocated.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+MXUserPerThread *
+MXUserGetPerThread(VThreadID tid, // IN: thread ID
+ Bool mayAlloc) // IN: alloc perThread if not present?
+{
+ MXUserPerThread *perThread;
+
+#if defined(VMX86_VMX)
+ /*
+ * Inside the VMX we have a tightly controlled environment with a rigidly
+ * controlled maximum number of threads. That being the case, use a simple,
+ * low memory usage and *VERY* fast scheme to manage the perThread data.
+ */
+
+ static Atomic_Ptr perThreadArray[VTHREAD_MAX_THREADS];
+
+ if (tid >= VTHREAD_MAX_THREADS) {
+ Panic("%s: tid out of bounds (%u)\n", __FUNCTION__, tid);
+ }
+
+ perThread = Atomic_ReadPtr(&perThreadArray[tid]);
+
+ if ((perThread == NULL) && mayAlloc) {
+ MXUserPerThread *before;
+
+ perThread = Util_SafeCalloc(1, sizeof(MXUserPerThread));
+
+ before = Atomic_ReadIfEqualWritePtr(&perThreadArray[tid], NULL,
+ (void *) perThread);
+
+ if (before) {
+ free(perThread);
+ }
+
+ MXUserMaintainMaxTid(tid); // track the maximum known tid
+
+ perThread = Atomic_ReadPtr(&perThreadArray[tid]);
+ ASSERT(perThread);
+ }
+#else
+ /*
+ * Outside the VMX there are no controls on the number of threads that can
+ * use MXUser locks. Here use an open ended, reasonably fast scheme for
+ * managing the perThread data.
+ *
+ * Use an atomic hash table to manage the perThread data. This avoids a
+ * great deal of locking and syncronization overhead.
+ */
+
+ HashTable *hash;
+
+ static Atomic_Ptr hashTableMem;
+
+ hash = HashTable_AllocOnce(&hashTableMem, 1024,
+ HASH_INT_KEY | HASH_FLAG_ATOMIC, NULL);
+
+ perThread = NULL;
+
+ if (!HashTable_Lookup(hash, (void *) (uintptr_t) tid,
+ (void **) &perThread)) {
+ /* No entry for this tid was found, allocate one? */
+
+ if (mayAlloc) {
+ MXUserPerThread *newEntry = Util_SafeCalloc(1,
+ sizeof(MXUserPerThread));
+
+ /*
+ * Attempt to (racey) insert a perThread on behalf of the specified
+ * thread. If yet another thread takes care of this first, clean up
+ * the mess.
+ */
+
+ perThread = HashTable_LookupOrInsert(hash, (void *) (uintptr_t) tid,
+ newEntry);
+ ASSERT(perThread);
+
+ if (perThread != newEntry) {
+ free(newEntry);
+ }
+
+ MXUserMaintainMaxTid(tid); // track the maximum known tid
+ } else {
+ perThread = NULL;
+ }
+ }
+#endif
+
+ return perThread;
+}
+#endif
+
+
+#if defined(MXUSER_DEBUG)
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_AnyLocksHeld --
+ *
+ * Are any MXUser locks held?
+ *
+ * A tid of VTHREAD_INVALID_ID asks to check locks across all threads
+ * (via an linear search over all threads); such a check may return an
+ * incorrect or stale result in an active multi-threaded environment.
+ *
+ * A tid other than VTHREAD_INVALID_ID will check locks for the specified
+ * thread. The results of this check are always valid for the calling
+ * thread but may be incorrect or stale for other threads.
+ *
+ * Results:
+ * TRUE Yes
+ * FALSE No
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+MXUser_AnyLocksHeld(VThreadID tid) // IN:
+{
+ Bool result;
+
+ if (tid == VTHREAD_INVALID_ID) {
+ uint32 i;
+ uint32 maxThreadID = Atomic_Read(&mxMaxThreadID);
+
+ result = FALSE;
+
+ for (i = 0; i < maxThreadID; i++) {
+ MXUserPerThread *perThread = MXUserGetPerThread(i, FALSE);
+
+ if (perThread && (perThread->locksHeld != 0)) {
+ result = TRUE;
+ break;
+ }
+ }
+ } else {
+ MXUserPerThread *perThread = MXUserGetPerThread(tid, FALSE);
+
+ result = (perThread == NULL) ? FALSE : (perThread->locksHeld != 0);
+ }
+
+ return result;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserAcquisitionTracking --
+ *
+ * Perform the appropriate tracking for lock acquisition.
+ *
+ * Results:
+ * Panic when a rank violation is detected (checkRank is TRUE).
+ * Add a lock instance to perThread lock list.
+ *
+ * Side effects:
+ * Manifold.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUserAcquisitionTracking(MXUserHeader *header, // IN:
+ Bool checkRank) // IN:
+{
+ VThreadID tid = VThread_CurID();
+ MXUserPerThread *perThread = MXUserGetPerThread(tid, TRUE);
+
+ ASSERT_NOT_IMPLEMENTED(perThread->locksHeld < MXUSER_MAX_LOCKS_PER_THREAD);
+
+ /* Rank checking anyone? */
+ if (checkRank && (header->lockRank != RANK_UNRANKED)) {
+ uint32 i;
+ MX_Rank maxRank;
+ Bool firstInstance = TRUE;
+
+ /* Check MX locks when they are present */
+ maxRank = (MXUserMxCheckRank) ? (*MXUserMxCheckRank)() : RANK_UNRANKED;
+
+ /*
+ * Determine the maximum rank held. Note if the lock being acquired
+ * was previously entered into the tracking system.
+ */
+
+ for (i = 0; i < perThread->locksHeld; i++) {
+ MXUserHeader *chkHdr = perThread->lockArray[i];
+
+ maxRank = MAX(chkHdr->lockRank, maxRank);
+
+ if (chkHdr == header) {
+ firstInstance = FALSE;
+ }
+ }
+
+ /*
+ * Perform rank checking when a lock is entered into the tracking
+ * system for the first time. This works out well because:
+ *
+ * Recursive locks are rank checked only upon their first acquisition...
+ * just like MX locks.
+ *
+ * Exclusive locks will have a second entry added into the tracking
+ * system but will immediately panic due to the run time checking - no
+ * (real) harm done.
+ */
+
+ if (firstInstance && (header->lockRank <= maxRank)) {
+ Warning("%s: lock rank violation by thread %s\n", __FUNCTION__,
+ VThread_CurName());
+ Warning("%s: locks held:\n", __FUNCTION__);
+
+ if (MXUserMxLockLister) {
+ (*MXUserMxLockLister)();
+ }
+
+ MXUserListLocks();
+
+ /*
+ * When called within a panic situation, don't panic on a rank
+ * violation. This helps avoid a secondary panic which will confuse
+ * or abort obtaining a good log and/or coredump.
+ */
+
+ if (!MXUser_InPanic()) {
+ MXUserDumpAndPanic(header, "%s: rank violation\n", __FUNCTION__);
+ }
+ }
+ }
+
+ /* Add lock instance to the calling threads perThread information */
+ perThread->lockArray[perThread->locksHeld++] = header;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserReleaseTracking --
+ *
+ * Perform the appropriate tracking for lock release.
+ *
+ * Results:
+ * A panic.
+ *
+ * Side effects:
+ * Manifold.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUserReleaseTracking(MXUserHeader *header) // IN: lock, via its header
+{
+ uint32 i;
+ uint32 lastEntry;
+ VThreadID tid = VThread_CurID();
+ MXUserPerThread *perThread = MXUserGetPerThread(tid, FALSE);
+
+ /* MXUserAcquisitionTracking should have already created a perThread */
+ if (UNLIKELY(perThread == NULL)) {
+ MXUserDumpAndPanic(header, "%s: perThread not found! (thread %u)\n",
+ __FUNCTION__, tid);
+ }
+
+ /* Search the perThread for the argument lock */
+ for (i = 0; i < perThread->locksHeld; i++) {
+ if (perThread->lockArray[i] == header) {
+ break;
+ }
+ }
+
+ /* The argument lock had better be in the perThread */
+ if (UNLIKELY(i >= perThread->locksHeld)) {
+ MXUserDumpAndPanic(header, "%s: lock not found! (thread %u; count %u)\n",
+ __FUNCTION__, tid, perThread->locksHeld);
+ }
+
+ /* Remove the argument lock from the perThread */
+ lastEntry = perThread->locksHeld - 1;
+
+ if (i < lastEntry) {
+ perThread->lockArray[i] = perThread->lockArray[lastEntry];
+ }
+
+ perThread->lockArray[lastEntry] = NULL; // tidy up memory
+ perThread->locksHeld--;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_TryAcquireFailureControl --
+ *
+ * Should a TryAcquire operation fail, no matter "what", sometimes?
+ *
+ * Failures occur statistically in debug builds to force our code down
+ * all of its paths.
+ *
+ * Results:
+ * Unknown
+ *
+ * Side effects:
+ * Always entertaining...
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_TryAcquireFailureControl(Bool (*func)(const char *lockName)) // IN:
+{
+ MXUserTryAcquireForceFail = func;
+}
+#endif
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserInternalSingleton --
+ *
+ * A "singleton" function for the MXUser internal recursive lock.
+ *
+ * Internal MXUser recursive locks have no statistics gathering or
+ * tracking abilities. They need to used with care and rarely.
+ *
+ * Results:
+ * NULL Failure
+ * !NULL A pointer to an initialized MXRecLock
+ *
+ * Side effects:
+ * Manifold.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+MXRecLock *
+MXUserInternalSingleton(Atomic_Ptr *storage) // IN:
+{
+ MXRecLock *lock = (MXRecLock *) Atomic_ReadPtr(storage);
+
+ if (UNLIKELY(lock == NULL)) {
+ lock = Util_SafeMalloc(sizeof(MXRecLock));
+
+ if (MXRecLockInit(lock)) {
+ MXRecLock *before;
+
+ before = (MXRecLock *) Atomic_ReadIfEqualWritePtr(storage, NULL,
+ (void *) lock);
+
+ if (before) {
+ MXRecLockDestroy(lock);
+ free(lock);
+
+ lock = before;
+ }
+ } else {
+ free(lock);
+ lock = Atomic_ReadPtr(storage); // maybe another thread succeeded
+ }
+ }
+
+ return lock;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserDumpAndPanic --
+ *
+ * Dump a lock, print a message and die
+ *
+ * Results:
+ * A panic.
+ *
+ * Side effects:
+ * Manifold.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUserDumpAndPanic(MXUserHeader *header, // IN:
+ const char *fmt, // IN:
+ ...) // IN:
+{
+ char *msg;
+ va_list ap;
+
+ ASSERT((header != NULL) && (header->lockDumper != NULL));
+
+ (*header->lockDumper)(header);
+
+ va_start(ap, fmt);
+ msg = Str_SafeVasprintf(NULL, fmt, ap);
+ va_end(ap);
+
+ Panic("%s", msg);
+}
+
+
+/*
+ *---------------------------------------------------------------------
+ *
+ * MXUser_SetInPanic --
+ * Notify the locking system that a panic is occurring.
+ *
+ * This is the "out of the monitor" - userland - implementation. The "in
+ * the monitor" implementation lives in mutex.c.
+ *
+ * Results:
+ * Set the internal "in a panic" global variable.
+ *
+ * Side effects:
+ * None
+ *
+ *---------------------------------------------------------------------
+ */
+
+void
+MXUser_SetInPanic(void)
+{
+ mxInPanic = TRUE;
+}
+
+
+/*
+ *---------------------------------------------------------------------
+ *
+ * MXUser_InPanic --
+ * Is the caller in the midst of a panic?
+ *
+ * This is the "out of the monitor" - userland - implementation. The "in
+ * the monitor" implementation lives in mutex.c.
+ *
+ * Results:
+ * TRUE Yes
+ * FALSE No
+ *
+ * Side effects:
+ * None
+ *
+ *---------------------------------------------------------------------
+ */
+
+Bool
+MXUser_InPanic(void)
+{
+ return mxInPanic;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserInstallMxHooks --
+ *
+ * The MX facility may notify the MXUser facility that it is place and
+ * that MXUser should check with it. This function should be called from
+ * MX_Init.
+ *
+ * Results:
+ * As Above.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUserInstallMxHooks(void (*theMxLockLister)(void), // IN: MX list function
+ MX_Rank (*theMxRankFunc)(void)) // IN: MX rank function
+{
+ /* Only allow registration once */
+ ASSERT((MXUserMxLockLister == NULL) && (MXUserMxCheckRank == NULL));
+
+ MXUserMxLockLister = theMxLockLister;
+ MXUserMxCheckRank = theMxRankFunc;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserListLocks
+ *
+ * Allow a caller to list, via warnings, the list of locks the caller
+ * has acquired. Ensure that no memory for lock tracking is allocated
+ * if no locks have been taken.
+ *
+ * Results:
+ * The list is printed
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUserListLocks(void)
+{
+#if defined(MXUSER_DEBUG)
+ MXUserPerThread *perThread = MXUserGetPerThread(VThread_CurID(), FALSE);
+
+ if (perThread != NULL) {
+ uint32 i;
+
+ for (i = 0; i < perThread->locksHeld; i++) {
+ MXUserHeader *hdr = perThread->lockArray[i];
+
+ Warning("\tMXUser lock %s (@%p) rank %d\n", hdr->lockName, hdr,
+ hdr->lockRank);
+ }
+ }
+#endif
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2010 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+#include "vmware.h"
+#include "str.h"
+#include "err.h"
+#include "util.h"
+#include "userlock.h"
+#include "ulInt.h"
+
+/*
+ * A portable condition variable
+ */
+
+struct MXUserCondVar {
+ uint32 signature;
+ char *name;
+ MXRecLock *ownerLock;
+ Atomic_uint32 referenceCount;
+
+#if defined(_WIN32)
+ union {
+ struct {
+ MXRecLock condVarLock;
+ HANDLE signalEvent;
+ uint32 numWaiters;
+ uint32 numForRelease;
+ } compat;
+ CONDITION_VARIABLE condObject;
+ } x;
+#else
+ pthread_cond_t condObject;
+#endif
+};
+
+#define MXUSER_CONDVAR_SIGNATURE 0x444E4F43 // 'COND' in memory
+
+#if defined(_WIN32)
+typedef VOID (WINAPI *InitializeConditionVariableFn)(PCONDITION_VARIABLE cv);
+typedef BOOL (WINAPI *SleepConditionVariableCSFn)(PCONDITION_VARIABLE cv,
+ PCRITICAL_SECTION cs,
+ DWORD msSleep);
+typedef VOID (WINAPI *WakeAllConditionVariableFn)(PCONDITION_VARIABLE cv);
+typedef VOID (WINAPI *WakeConditionVariableFn)(PCONDITION_VARIABLE cv);
+
+static InitializeConditionVariableFn pInitializeConditionVariable;
+static SleepConditionVariableCSFn pSleepConditionVariableCS;
+static WakeAllConditionVariableFn pWakeAllConditionVariable;
+static WakeConditionVariableFn pWakeConditionVariable;
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserNativeCVSupported --
+ *
+ * Does native condition variable support exist for the Windows the
+ * caller is running on?
+ *
+ * Results:
+ * TRUE Yes
+ * FALSE No
+ *
+ * Side effects:
+ * Function pointers to the native routines are initialized upon success.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+MXUserNativeCVSupported(void)
+{
+ static Bool result;
+ static Bool done = FALSE;
+
+ if (!done) {
+ HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
+
+ if (kernel32) {
+ pInitializeConditionVariable = (InitializeConditionVariableFn)
+ GetProcAddress(kernel32,
+ "InitializeConditionVariable");
+
+ pSleepConditionVariableCS = (SleepConditionVariableCSFn)
+ GetProcAddress(kernel32,
+ "SleepConditionVariableCS");
+
+ pWakeAllConditionVariable = (WakeAllConditionVariableFn)
+ GetProcAddress(kernel32,
+ "WakeAllConditionVariable");
+
+ pWakeConditionVariable = (WakeConditionVariableFn)
+ GetProcAddress(kernel32,
+ "WakeConditionVariable");
+
+ result = ((pInitializeConditionVariable == NULL) ||
+ (pSleepConditionVariableCS == NULL) ||
+ (pWakeAllConditionVariable == NULL) ||
+ (pWakeConditionVariable == NULL)) ? FALSE : TRUE;
+
+ } else {
+ result = FALSE;
+ }
+
+ done = TRUE;
+ }
+
+ return result;
+}
+#endif
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserCreateCondVar --
+ *
+ * Create/initialize a condition variable associated with the specified
+ * lock.
+ *
+ * Results:
+ * !NULL Success; a pointer to the (new) condition variable
+ * NULL Failure
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+MXUserCondVar *
+MXUserCreateCondVar(MXUserHeader *header, // IN:
+ MXRecLock *lock) // IN:
+{
+ Bool success;
+ MXUserCondVar *condVar = Util_SafeCalloc(1, sizeof(*condVar));
+
+#if defined(_WIN32)
+ if (MXUserNativeCVSupported()) {
+ ASSERT(pInitializeConditionVariable);
+ (*pInitializeConditionVariable)(&condVar->x.condObject);
+ success = TRUE;
+ } else {
+ condVar->x.compat.numWaiters = 0;
+ condVar->x.compat.numForRelease = 0;
+
+ if (MXRecLockInit(&condVar->x.compat.condVarLock)) {
+ condVar->x.compat.signalEvent = CreateEvent(NULL, // no security
+ TRUE, // manual-reset
+ FALSE, // non-signaled
+ NULL); // unnamed
+
+ success = (condVar->x.compat.signalEvent != NULL);
+
+ if (!success) {
+ MXRecLockDestroy(&condVar->x.compat.condVarLock);
+ }
+ } else {
+ success = FALSE;
+ }
+ }
+#else
+ success = pthread_cond_init(&condVar->condObject, NULL) == 0;
+#endif
+
+ if (success) {
+ condVar->signature = MXUSER_CONDVAR_SIGNATURE;
+ condVar->name = Util_SafeStrdup(header->lockName);
+ condVar->ownerLock = lock;
+ } else {
+ free(condVar);
+ condVar = NULL;
+ }
+
+ return condVar;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserWaitCondVar --
+ *
+ * The internal wait on a condition variable routine.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * An attempt to use a lock other than the one the specified condition
+ * variable was specified for will result in a panic.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUserWaitCondVar(MXUserHeader *header, // IN:
+ MXRecLock *lock, // IN:
+ MXUserCondVar *condVar) // IN:
+{
+ int err;
+
+ ASSERT(header);
+ ASSERT(lock);
+ ASSERT(condVar && (condVar->signature == MXUSER_CONDVAR_SIGNATURE));
+
+ if (condVar->ownerLock != lock) {
+ MXUserDumpAndPanic(header,
+ "%s: invalid use of lock %s with condVar (%p; %s)\n",
+ __FUNCTION__, header->lockName, condVar->name);
+ }
+
+ Atomic_Inc(&condVar->referenceCount);
+
+#if defined(_WIN32)
+ if (pSleepConditionVariableCS) {
+ err = (*pSleepConditionVariableCS)(&condVar->x.condObject,
+ &lock->nativeLock, INFINITE) ?
+ 0 : GetLastError();
+ } else {
+ Bool done = FALSE;
+
+ MXRecLockAcquire(&condVar->x.compat.condVarLock, GetReturnAddress());
+ condVar->x.compat.numWaiters++;
+ MXRecLockRelease(&condVar->x.compat.condVarLock);
+
+ MXRecLockRelease(lock);
+
+ do {
+ WaitForSingleObject(condVar->x.compat.signalEvent, INFINITE);
+
+ MXRecLockAcquire(&condVar->x.compat.condVarLock, GetReturnAddress());
+
+ ASSERT(condVar->x.compat.numWaiters > 0);
+
+ if (condVar->x.compat.numForRelease > 0) {
+ condVar->x.compat.numWaiters--;
+
+ if (--condVar->x.compat.numForRelease == 0) {
+ ResetEvent(condVar->x.compat.signalEvent);
+ }
+
+ done = TRUE;
+ }
+
+ MXRecLockRelease(&condVar->x.compat.condVarLock);
+ } while (!done);
+
+ MXRecLockAcquire(lock, GetReturnAddress());
+
+ err = 0;
+ }
+#else
+ err = pthread_cond_wait(&condVar->condObject, &lock->nativeLock);
+#endif
+
+ if (err != 0) {
+ MXUserDumpAndPanic(header, "%s: failure %d on condVar (%p; %s)\n",
+ __FUNCTION__, err, condVar, condVar->name);
+ }
+
+ Atomic_Dec(&condVar->referenceCount);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_SignalCondVar
+ *
+ * Signal on a condVar - wake up one thread blocked on the specified
+ * condition variable.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_SignalCondVar(MXUserCondVar *condVar) // IN:
+{
+#if !defined(_WIN32)
+ int err;
+#endif
+
+ ASSERT(condVar && (condVar->signature == MXUSER_CONDVAR_SIGNATURE));
+
+#if defined(_WIN32)
+ if (pWakeConditionVariable) {
+ (*pWakeConditionVariable)(&condVar->x.condObject);
+ } else {
+ MXRecLockAcquire(&condVar->x.compat.condVarLock, GetReturnAddress());
+
+ if (condVar->x.compat.numWaiters > condVar->x.compat.numForRelease) {
+ SetEvent(condVar->x.compat.signalEvent);
+ condVar->x.compat.numForRelease++;
+ }
+
+ MXRecLockRelease(&condVar->x.compat.condVarLock);
+ }
+#else
+ err = pthread_cond_signal(&condVar->condObject);
+
+ if (err != 0) {
+ Panic("%s: failure %d on condVar (%p; %s) \n", __FUNCTION__, err,
+ condVar, condVar->name);
+ }
+#endif
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_BroadcastCondVar
+ *
+ * Broadcast on a condVar - wake up all threads blocked on the specified
+ * condition variable.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_BroadcastCondVar(MXUserCondVar *condVar) // IN:
+{
+#if !defined(_WIN32)
+ Err_Number err;
+#endif
+
+ ASSERT(condVar && (condVar->signature == MXUSER_CONDVAR_SIGNATURE));
+
+#if defined(_WIN32)
+ if (pWakeAllConditionVariable) {
+ (*pWakeAllConditionVariable)(&condVar->x.condObject);
+ } else {
+ MXRecLockAcquire(&condVar->x.compat.condVarLock, GetReturnAddress());
+
+ if (condVar->x.compat.numWaiters > condVar->x.compat.numForRelease) {
+ SetEvent(condVar->x.compat.signalEvent);
+ condVar->x.compat.numForRelease = condVar->x.compat.numWaiters;
+ }
+
+ MXRecLockRelease(&condVar->x.compat.condVarLock);
+ }
+#else
+ err = pthread_cond_broadcast(&condVar->condObject);
+
+ if (err != 0) {
+ Panic("%s: failure %d on condVar (%p; %s) \n", __FUNCTION__, err,
+ condVar, condVar->name);
+ }
+#endif
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_DestroyCondVar --
+ *
+ * Destroy a condition variable.
+ *
+ * Results:
+ * As above. Don't use the pointer again...
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_DestroyCondVar(MXUserCondVar *condVar) // IN:
+{
+ if (condVar != NULL) {
+ ASSERT(condVar && (condVar->signature == MXUSER_CONDVAR_SIGNATURE));
+
+ if (Atomic_Read(&condVar->referenceCount) != 0) {
+ Panic("%s: Attempted destroy on active condVar (%p; %s)\n",
+ __FUNCTION__, condVar, condVar->name);
+ }
+
+#if defined(_WIN32)
+ if (pInitializeConditionVariable == NULL) {
+ CloseHandle(condVar->x.compat.signalEvent);
+ MXRecLockDestroy(&condVar->x.compat.condVarLock);
+ }
+#else
+ pthread_cond_destroy(&condVar->condObject);
+#endif
+
+ condVar->signature = 0; // just in case...
+ free(condVar->name);
+ condVar->name = NULL;
+ condVar->ownerLock = NULL;
+
+ free(condVar);
+ }
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2009 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+#include "vmware.h"
+#include "str.h"
+#include "util.h"
+#include "userlock.h"
+#include "ulInt.h"
+
+struct MXUserExclLock
+{
+ MXUserHeader header;
+ MXRecLock recursiveLock;
+
+#if defined(MXUSER_STATS)
+ uint64 holdStart;
+
+ MXUserAcquisitionStats acquisitionStats;
+ Atomic_Ptr acquisitionHisto;
+
+ MXUserBasicStats heldStats;
+ Atomic_Ptr heldHisto;
+#endif
+};
+
+
+#if defined(MXUSER_STATS)
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserStatsActionExcl --
+ *
+ * Perform the statistics action for the specified lock.
+ *
+ * Results:
+ * As above.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+MXUserStatsActionExcl(MXUserHeader *header, // IN:
+ unsigned epoch) // IN:
+{
+ Bool isHot;
+ Bool doLog;
+ double contentionRatio;
+
+ MXUserExclLock *lock = (MXUserExclLock *) header;
+
+ /*
+ * Dump the statistics for the specified lock.
+ */
+
+ MXUserDumpAcquisitionStats(epoch, MXUSER_STAT_CLASS_ACQUISITION, header,
+ &lock->acquisitionStats);
+
+ if (Atomic_ReadPtr(&lock->acquisitionHisto) != NULL) {
+ MXUserHistoDump(epoch, MXUSER_STAT_CLASS_ACQUISITION, header,
+ Atomic_ReadPtr(&lock->acquisitionHisto));
+ }
+
+ MXUserDumpBasicStats(epoch, MXUSER_STAT_CLASS_HELD, header,
+ &lock->heldStats);
+
+ if (Atomic_ReadPtr(&lock->heldHisto) != NULL) {
+ MXUserHistoDump(epoch, MXUSER_STAT_CLASS_HELD, header,
+ Atomic_ReadPtr(&lock->heldHisto));
+ }
+
+ /*
+ * Has the lock gone "hot"? If so, implement the hot actions.
+ */
+
+ MXUserKitchen(&lock->acquisitionStats, &contentionRatio, &isHot, &doLog);
+
+ if (isHot) {
+ MXUserForceHisto(&lock->acquisitionHisto, MXUSER_HISTOGRAM_MAX_BINS,
+ MXUSER_HISTOGRAM_NS_PER_BIN);
+ MXUserForceHisto(&lock->heldHisto, MXUSER_HISTOGRAM_MAX_BINS,
+ MXUSER_HISTOGRAM_NS_PER_BIN);
+
+ if (doLog) {
+ Log("HOT LOCK (%s); contention ratio %f\n",
+ lock->header.lockName, contentionRatio);
+ }
+ }
+}
+#endif
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserDumpExclLock --
+ *
+ * Dump an exclusive lock.
+ *
+ * Results:
+ * A dump.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+MXUserDumpExclLock(MXUserHeader *header) // IN:
+{
+ MXUserExclLock *lock = (MXUserExclLock *) header;
+
+ Warning("%s: Exclusive lock @ %p\n", __FUNCTION__, lock);
+
+ Warning("\tsignature %X\n", lock->header.lockSignature);
+ Warning("\tname %s\n", lock->header.lockName);
+ Warning("\trank %d\n", lock->header.lockRank);
+
+ Warning("\tcount %u\n", lock->recursiveLock.referenceCount);
+
+#if defined(MXUSER_DEBUG)
+ Warning("\tcaller %p\n", lock->recursiveLock.ownerRetAddr);
+ Warning("\tVThreadID %d\n", (int) lock->recursiveLock.portableThreadID);
+#endif
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_CreateExclLock --
+ *
+ * Create an exclusive lock.
+ *
+ * Results:
+ * NULL Creation failed
+ * !NULL Creation succeeded
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+MXUserExclLock *
+MXUser_CreateExclLock(const char *userName, // IN:
+ MX_Rank rank) // IN:
+{
+ char *properName;
+ MXUserExclLock *lock;
+
+ lock = Util_SafeCalloc(1, sizeof(*lock));
+
+ if (userName == NULL) {
+ properName = Str_SafeAsprintf(NULL, "X-%p", GetReturnAddress());
+ } else {
+ properName = Util_SafeStrdup(userName);
+ }
+
+ if (!MXRecLockInit(&lock->recursiveLock)) {
+ free(properName);
+ free(lock);
+
+ return NULL;
+ }
+
+ lock->header.lockName = properName;
+ lock->header.lockSignature = USERLOCK_SIGNATURE;
+ lock->header.lockRank = rank;
+ lock->header.lockDumper = MXUserDumpExclLock;
+
+#if defined(MXUSER_STATS)
+ lock->header.lockStatsAction = MXUserStatsActionExcl;
+ lock->header.lockID = MXUserAllocID();
+
+ MXUserAddToList(&lock->header);
+ MXUserAcquisitionStatsSetUp(&lock->acquisitionStats);
+ MXUserBasicStatsSetUp(&lock->heldStats);
+#endif
+
+ return lock;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_DestroyExclLock --
+ *
+ * Destroy an exclusive lock.
+ *
+ * Results:
+ * Lock is destroyed. Don't use the pointer again.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_DestroyExclLock(MXUserExclLock *lock) // IN:
+{
+ if (lock != NULL) {
+ ASSERT(lock->header.lockSignature == USERLOCK_SIGNATURE);
+
+ if (MXRecLockCount(&lock->recursiveLock) > 0) {
+ MXUserDumpAndPanic(&lock->header,
+ "%s: Destroy of an acquired exclusive lock\n",
+ __FUNCTION__);
+ }
+
+ MXRecLockDestroy(&lock->recursiveLock);
+
+#if defined(MXUSER_STATS)
+ MXUserRemoveFromList(&lock->header);
+ MXUserHistoTearDown(Atomic_ReadPtr(&lock->acquisitionHisto));
+ MXUserHistoTearDown(Atomic_ReadPtr(&lock->heldHisto));
+#endif
+
+ lock->header.lockSignature = 0; // just in case...
+ free((void *) lock->header.lockName); // avoid const warnings
+ lock->header.lockName = NULL;
+ free(lock);
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_AcquireExclLock --
+ *
+ * An acquisition is made (lock is taken) on the specified exclusive lock.
+ *
+ * Results:
+ * The lock is acquired (locked).
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_AcquireExclLock(MXUserExclLock *lock) // IN/OUT:
+{
+#if defined(MXUSER_STATS)
+ uint64 begin;
+ uint64 value;
+ Bool contended;
+ MXUserHisto *histo;
+#endif
+
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ MXUserAcquisitionTracking(&lock->header, TRUE);
+
+#if defined(MXUSER_STATS)
+ begin = MXUserReadTimerNS();
+
+ contended =
+#endif
+
+ MXRecLockAcquire(&lock->recursiveLock, GetReturnAddress());
+
+#if defined(MXUSER_STATS)
+ value = MXUserReadTimerNS() - begin;
+
+ MXUserAcquisitionSample(&lock->acquisitionStats, contended, value);
+
+ histo = Atomic_ReadPtr(&lock->acquisitionHisto);
+
+ if (UNLIKELY(histo != NULL)) {
+ MXUserHistoSample(histo, value);
+ }
+#endif
+
+ if (MXRecLockCount(&lock->recursiveLock) > 1) {
+ MXUserDumpAndPanic(&lock->header,
+ "%s: Acquire on an acquired exclusive lock\n",
+ __FUNCTION__);
+ }
+
+#if defined(MXUSER_STATS)
+ lock->holdStart = MXUserReadTimerNS();
+#endif
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_ReleaseExclLock --
+ *
+ * Release (unlock) an exclusive lock.
+ *
+ * Results:
+ * The lock is released.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_ReleaseExclLock(MXUserExclLock *lock) // IN/OUT:
+{
+#if defined(MXUSER_STATS)
+ uint64 value;
+ MXUserHisto *histo;
+#endif
+
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+#if defined(MXUSER_STATS)
+ value = MXUserReadTimerNS() - lock->holdStart;
+
+ MXUserBasicStatsSample(&lock->heldStats, value);
+
+ histo = Atomic_ReadPtr(&lock->heldHisto);
+
+ if (UNLIKELY(histo != NULL)) {
+ MXUserHistoSample(histo, value);
+ }
+#endif
+
+ if (!MXRecLockIsOwner(&lock->recursiveLock)) {
+ uint32 lockCount = MXRecLockCount(&lock->recursiveLock);
+
+ MXUserDumpAndPanic(&lock->header,
+ "%s: Non-owner release of an %s exclusive lock\n",
+ __FUNCTION__,
+ lockCount == 0 ? "unacquired" : "acquired");
+ }
+
+ MXUserReleaseTracking(&lock->header);
+
+ MXRecLockRelease(&lock->recursiveLock);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_TryAcquireExclLock --
+ *
+ * An attempt is made to conditionally acquire (lock) an exclusive lock.
+ *
+ * Results:
+ * TRUE Acquired (locked)
+ * FALSE Not acquired
+ *
+ * Side effects:
+ * None
+ *
+ * NOTE:
+ * A "TryAcquire" does not rank check should the acquisition succeed.
+ * This duplicates the behavor of MX locks.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+MXUser_TryAcquireExclLock(MXUserExclLock *lock) // IN/OUT:
+{
+ Bool success;
+
+#if defined(MXUSER_STATS)
+ uint64 begin;
+#endif
+
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ if (MXUserTryAcquireFail(lock->header.lockName)) {
+ return FALSE;
+ }
+
+#if defined(MXUSER_STATS)
+ begin = MXUserReadTimerNS();
+#endif
+
+ success = MXRecLockTryAcquire(&lock->recursiveLock, GetReturnAddress());
+
+ if (success) {
+#if defined(MXUSER_STATS)
+ uint64 end = MXUserReadTimerNS();
+#endif
+
+ MXUserAcquisitionTracking(&lock->header, FALSE);
+
+ if (MXRecLockCount(&lock->recursiveLock) > 1) {
+ MXUserDumpAndPanic(&lock->header,
+ "%s: Acquire on an acquired exclusive lock\n",
+ __FUNCTION__);
+ }
+
+#if defined(MXUSER_STATS)
+ MXUserAcquisitionSample(&lock->acquisitionStats, FALSE, end - begin);
+#endif
+ }
+
+ return success;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_IsCurThreadHoldingExclLock --
+ *
+ * Is an exclusive lock held by the calling thread?
+ *
+ * Results:
+ * TRUE Yes
+ * FALSE No
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+MXUser_IsCurThreadHoldingExclLock(const MXUserExclLock *lock) // IN:
+{
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ return MXRecLockIsOwner(&lock->recursiveLock);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_ControlExclLock --
+ *
+ * Perform the specified command on the specified lock.
+ *
+ * Results:
+ * TRUE succeeded
+ * FALSE failed
+ *
+ * Side effects:
+ * Depends on the command, no?
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+MXUser_ControlExclLock(MXUserExclLock *lock, // IN/OUT:
+ uint32 command, // IN:
+ ...) // IN:
+{
+ Bool result;
+
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ switch (command) {
+#if defined(MXUSER_STATS)
+ case MXUSER_CONTROL_ACQUISITION_HISTO: {
+ va_list a;
+ uint32 numBins;
+ uint32 binWidth;
+
+ va_start(a, command);
+ numBins = va_arg(a, uint32);
+ binWidth = va_arg(a, uint32);
+ va_end(a);
+
+ MXUserForceHisto(&lock->acquisitionHisto, numBins, binWidth);
+
+ result = TRUE;
+ break;
+ }
+
+ case MXUSER_CONTROL_HELD_HISTO: {
+ va_list a;
+ uint32 numBins;
+ uint32 binWidth;
+
+ va_start(a, command);
+ numBins = va_arg(a, uint32);
+ binWidth = va_arg(a, uint32);
+ va_end(a);
+
+ MXUserForceHisto(&lock->heldHisto, numBins, binWidth);
+
+ result = TRUE;
+ break;
+ }
+#endif
+
+ default:
+ result = FALSE;
+ }
+
+ return result;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_CreateSingletonExclLock --
+ *
+ * Ensures that the specified backing object (Atomic_Ptr) contains a
+ * exclusive lock. This is useful for modules that need to protect
+ * something with a lock but don't have an existing Init() entry point
+ * where a lock can be created.
+ *
+ * Results:
+ * A pointer to the requested lock.
+ *
+ * Side effects:
+ * Generally the lock's resources are intentionally leaked (by design).
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+MXUserExclLock *
+MXUser_CreateSingletonExclLock(Atomic_Ptr *lockStorage, // IN/OUT:
+ const char *name, // IN:
+ MX_Rank rank) // IN:
+{
+ MXUserExclLock *lock;
+
+ ASSERT(lockStorage);
+
+ lock = (MXUserExclLock *) Atomic_ReadPtr(lockStorage);
+
+ if (UNLIKELY(lock == NULL)) {
+ MXUserExclLock *before;
+
+ lock = MXUser_CreateExclLock(name, rank);
+
+ before = (MXUserExclLock *) Atomic_ReadIfEqualWritePtr(lockStorage, NULL,
+ (void *) lock);
+
+ if (before) {
+ MXUser_DestroyExclLock(lock);
+
+ lock = before;
+ }
+ }
+
+ return lock;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_CreateCondVarExclLock --
+ *
+ * Create a condition variable for use with the specified exclusive lock.
+ *
+ * Results:
+ * As above.
+ *
+ * Side effects:
+ * The created condition variable will cause a run-time error if it is
+ * used with a lock other than the one it was created for.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+MXUserCondVar *
+MXUser_CreateCondVarExclLock(MXUserExclLock *lock)
+{
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ return MXUserCreateCondVar(&lock->header, &lock->recursiveLock);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_CondWaitExclLock --
+ *
+ * Block (sleep) on the specified condition variable. The specified lock
+ * is released upon blocking and is reacquired before returning from this
+ * function.
+ *
+ * Results:
+ * As above.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_WaitCondVarExclLock(MXUserExclLock *lock, // IN:
+ MXUserCondVar *condVar) // IN:
+{
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ MXUserWaitCondVar(&lock->header, &lock->recursiveLock, condVar);
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2009 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+#ifndef _ULINT_H_
+#define _ULINT_H_
+
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(_WIN32)
+typedef DWORD MXThreadID;
+#define MXUSER_INVALID_OWNER 0xFFFFFFFF
+#else
+#include <pthread.h>
+#include <errno.h>
+typedef pthread_t MXThreadID;
+#endif
+
+#include "vm_basic_types.h"
+#include "vthreadBase.h"
+#include "ulIntShared.h"
+
+#if defined(MXUSER_STATS)
+#include "circList.h"
+
+#define MXUSER_HISTOGRAM_NS_PER_BIN 2000
+#define MXUSER_HISTOGRAM_MAX_BINS 500
+
+#define MXUSER_STAT_CLASS_ACQUISITION "acquisition"
+#define MXUSER_STAT_CLASS_HELD "held"
+#endif
+
+/*
+ * A portable recursive lock.
+ */
+
+#define MXUSER_MAX_REC_DEPTH 16
+
+typedef struct {
+#if defined(_WIN32)
+ CRITICAL_SECTION nativeLock; // Native lock object
+#else
+ pthread_mutex_t nativeLock; // Native lock object
+#endif
+
+ int referenceCount; // Acquisition count
+ MXThreadID nativeThreadID; // Native thread ID
+
+#if defined(MXUSER_DEBUG)
+ VThreadID portableThreadID; // VThreadID, when available
+ const void *ownerRetAddr; // return address of acquisition routine
+#endif
+} MXRecLock;
+
+
+/*
+ * Environment specific implementations of portable recursive locks.
+ *
+ * A recursive lock is used throughput the MXUser locks because:
+ * - it can be used recursively for recursive locks
+ * - exclusive (non-recursive) locks catch the recursion and panic
+ * rather than deadlock.
+ *
+ * There are 6 environment specific primitives:
+ *
+ * MXRecLockObjectInit initialize native lock before use
+ * MXRecLockDestroy destroy lock after use
+ * MXRecLockIsOwner is lock owned by caller?
+ * MXRecLockAcquire lock
+ * MXRecLockTryAcquire conditional lock
+ * MXRecLockRelease unlock
+ *
+ * Windows has a native recursive lock, the CRITICAL_SECTION. POSIXen,
+ * unfortunately, do not ensure access to such a facility. The recursive
+ * attribute of pthread_mutex_t is not implemented in all environments so
+ * we create a recursive implementation using an exclusive pthread_mutex_t
+ * and a few lines of code (most of which we need to do anyway).
+ */
+
+#if defined(_WIN32)
+static INLINE Bool
+MXRecLockObjectInit(CRITICAL_SECTION *nativeLock) // IN/OUT:
+{
+ /* http://msdn.microsoft.com/en-us/library/ms682530(VS.85).aspx */
+ /* magic number - allocate resources immediately; spin 0x400 times */
+ return InitializeCriticalSectionAndSpinCount(nativeLock, 0x80000400) != 0;
+}
+
+
+static INLINE void
+MXRecLockDestroy(MXRecLock *lock) // IN/OUT:
+{
+ DeleteCriticalSection(&lock->nativeLock);
+}
+
+
+static INLINE Bool
+MXRecLockIsOwner(const MXRecLock *lock) // IN:
+{
+ return lock->nativeThreadID == GetCurrentThreadId();
+}
+
+
+static INLINE Bool
+MXRecLockAcquire(MXRecLock *lock, // IN/OUT:
+ const void *location) // IN:
+{
+ Bool acquired;
+ Bool contended;
+
+ acquired = TryEnterCriticalSection(&lock->nativeLock);
+
+ if (acquired) {
+ contended = FALSE;
+ } else {
+ EnterCriticalSection(&lock->nativeLock);
+ contended = TRUE;
+ }
+
+ ASSERT((lock->referenceCount >= 0) &&
+ (lock->referenceCount < MXUSER_MAX_REC_DEPTH));
+
+ if (lock->referenceCount == 0) {
+ ASSERT(lock->nativeThreadID == MXUSER_INVALID_OWNER);
+ lock->nativeThreadID = GetCurrentThreadId();
+
+#if defined(MXUSER_DEBUG)
+ lock->ownerRetAddr = location;
+ lock->portableThreadID = VThread_CurID();
+#endif
+ }
+
+ lock->referenceCount++;
+
+ return contended;
+}
+
+
+static INLINE Bool
+MXRecLockTryAcquire(MXRecLock *lock, // IN/OUT:
+ const void *location) // IN:
+{
+ Bool acquired;
+
+ acquired = TryEnterCriticalSection(&lock->nativeLock);
+
+ if (acquired) {
+ ASSERT((lock->referenceCount >= 0) &&
+ (lock->referenceCount < MXUSER_MAX_REC_DEPTH));
+
+ if (lock->referenceCount == 0) {
+ ASSERT(lock->nativeThreadID == MXUSER_INVALID_OWNER);
+ lock->nativeThreadID = GetCurrentThreadId();
+
+#if defined(MXUSER_DEBUG)
+ lock->ownerRetAddr = location;
+ lock->portableThreadID = VThread_CurID();
+#endif
+ }
+
+ lock->referenceCount++;
+ }
+
+ return acquired;
+}
+
+
+static INLINE void
+MXRecLockRelease(MXRecLock *lock) // IN/OUT:
+{
+ ASSERT((lock->referenceCount > 0) &&
+ (lock->referenceCount < MXUSER_MAX_REC_DEPTH));
+
+ lock->referenceCount--;
+
+ if (lock->referenceCount == 0) {
+ lock->nativeThreadID = MXUSER_INVALID_OWNER;
+
+#if defined(MXUSER_DEBUG)
+ lock->ownerRetAddr = NULL;
+ lock->portableThreadID = VTHREAD_INVALID_ID;
+#endif
+ }
+
+ LeaveCriticalSection(&lock->nativeLock);
+}
+#else
+static INLINE Bool
+MXRecLockObjectInit(pthread_mutex_t *nativeLock) // IN/OUT:
+{
+ int err;
+
+ err = pthread_mutex_init(nativeLock, NULL);
+
+ if (vmx86_debug && (err != 0)) {
+ Panic("%s: pthread_mutex_init returned %d\n", __FUNCTION__, err);
+ }
+
+ return err == 0;
+}
+
+
+static INLINE void
+MXRecLockDestroy(MXRecLock *lock) // IN/OUT:
+{
+ int err;
+
+ err = pthread_mutex_destroy(&lock->nativeLock);
+
+ if (vmx86_debug && (err != 0)) {
+ Panic("%s: pthread_mutex_destroy returned %d\n", __FUNCTION__, err);
+ }
+}
+
+
+static INLINE Bool
+MXRecLockIsOwner(const MXRecLock *lock) // IN:
+{
+ return pthread_equal(lock->nativeThreadID, pthread_self());
+}
+
+
+static INLINE Bool
+MXRecLockAcquire(MXRecLock *lock, // IN/OUT:
+ void *location) // IN:
+{
+ Bool contended;
+
+ if ((lock->referenceCount != 0) &&
+ pthread_equal(lock->nativeThreadID, pthread_self())) {
+ ASSERT((lock->referenceCount > 0) &&
+ (lock->referenceCount < MXUSER_MAX_REC_DEPTH));
+
+ lock->referenceCount++;
+
+ contended = FALSE;
+ } else {
+ int err;
+
+ err = pthread_mutex_trylock(&lock->nativeLock);
+
+ if (err == 0) {
+ contended = FALSE;
+ } else {
+ if (vmx86_debug && (err != EBUSY)) {
+ Panic("%s: pthread_mutex_trylock returned %d\n", __FUNCTION__,
+ err);
+ }
+
+ err = pthread_mutex_lock(&lock->nativeLock);
+ contended = TRUE;
+ }
+
+ if (vmx86_debug && (err != 0)) {
+ Panic("%s: pthread_mutex_lock returned %d\n", __FUNCTION__, err);
+ }
+
+ ASSERT(lock->referenceCount == 0);
+
+#if defined(MXUSER_DEBUG)
+ ASSERT(lock->portableThreadID == VTHREAD_INVALID_ID);
+
+ lock->ownerRetAddr = location;
+ lock->portableThreadID = VThread_CurID();
+#endif
+
+ lock->nativeThreadID = pthread_self();
+ lock->referenceCount = 1;
+ }
+
+ return contended;
+}
+
+
+static INLINE Bool
+MXRecLockTryAcquire(MXRecLock *lock, // IN/OUT:
+ void *location) // IN:
+{
+ int err;
+ Bool acquired;
+
+ err = pthread_mutex_trylock(&lock->nativeLock);
+
+ if (err == 0) {
+ ASSERT((lock->referenceCount >= 0) &&
+ (lock->referenceCount < MXUSER_MAX_REC_DEPTH));
+
+ if (lock->referenceCount == 0) {
+#if defined(MXUSER_DEBUG)
+ ASSERT(lock->portableThreadID == VTHREAD_INVALID_ID);
+
+ lock->ownerRetAddr = location;
+ lock->portableThreadID = VThread_CurID();
+#endif
+
+ lock->nativeThreadID = pthread_self();
+ }
+
+ lock->referenceCount++;
+
+ acquired = TRUE;
+ } else {
+ if (vmx86_debug && (err != EBUSY)) {
+ Panic("%s: pthread_mutex_trylock returned %d\n", __FUNCTION__, err);
+ }
+
+ acquired = FALSE;
+ }
+
+ return acquired;
+}
+
+
+static INLINE void
+MXRecLockRelease(MXRecLock *lock) // IN/OUT:
+{
+ ASSERT((lock->referenceCount > 0) &&
+ (lock->referenceCount < MXUSER_MAX_REC_DEPTH));
+
+ lock->referenceCount--;
+
+ if (lock->referenceCount == 0) {
+ int err;
+
+ /* a hack but it works portably */
+ memset((void *) &lock->nativeThreadID, 0xFF,
+ sizeof(lock->nativeThreadID));
+
+#if defined(MXUSER_DEBUG)
+ lock->ownerRetAddr = NULL;
+ lock->portableThreadID = VTHREAD_INVALID_ID;
+#endif
+
+ err = pthread_mutex_unlock(&lock->nativeLock);
+
+ if (vmx86_debug && (err != 0)) {
+ Panic("%s: pthread_mutex_unlock returned %d\n", __FUNCTION__, err);
+ }
+ }
+}
+#endif
+
+
+/*
+ * Initialization of portable recursive lock.
+ */
+
+static INLINE Bool
+MXRecLockInit(MXRecLock *lock) // IN/OUT:
+{
+ if (!MXRecLockObjectInit(&lock->nativeLock)) {
+ return FALSE;
+ }
+
+#if defined(_WIN32)
+ lock->nativeThreadID = MXUSER_INVALID_OWNER;
+#else
+ /* a hack but it works portably */
+ memset((void *) &lock->nativeThreadID, 0xFF, sizeof(lock->nativeThreadID));
+#endif
+
+ lock->referenceCount = 0;
+
+#if defined(MXUSER_DEBUG)
+ lock->portableThreadID = VTHREAD_INVALID_ID;
+ lock->ownerRetAddr = NULL;
+#endif
+
+ return TRUE;
+}
+
+
+static INLINE uint32
+MXRecLockCount(const MXRecLock *lock) // IN:
+{
+ return lock->referenceCount;
+}
+
+/*
+ * MXUser lock header - all MXUser locks start with this
+ */
+
+#define USERLOCK_SIGNATURE 0x4B434F4C // 'LOCK' in memory
+
+typedef struct MXUserHeader {
+ uint32 lockSignature;
+ MX_Rank lockRank;
+ const char *lockName;
+ void (*lockDumper)(struct MXUserHeader *);
+
+#if defined(MXUSER_STATS)
+ void (*lockStatsAction)(struct MXUserHeader *, unsigned epoch);
+ ListItem lockItem;
+ uint32 lockID;
+#endif
+} MXUserHeader;
+
+
+/*
+ * The per thread information.
+ */
+
+#if defined(MXUSER_DEBUG) || defined(MXUSER_STATS)
+#define MXUSER_MAX_LOCKS_PER_THREAD (2 * MXUSER_MAX_REC_DEPTH)
+
+typedef struct {
+#if defined(MXUSER_DEBUG)
+ uint32 locksHeld;
+ MXUserHeader *lockArray[MXUSER_MAX_LOCKS_PER_THREAD];
+#endif
+
+#if defined(MXUSER_STATS)
+ uint64 totalAcquisitions; // total thread lock acquisitions
+ uint64 contendedAcquisitions; // contended subset of above
+#endif
+} MXUserPerThread;
+
+MXUserPerThread *MXUserGetPerThread(VThreadID tid,
+ Bool mayAlloc);
+#endif
+
+/*
+ * Internal functions
+ */
+
+void MXUserDumpAndPanic(MXUserHeader *header,
+ const char *fmt,
+ ...);
+
+MXRecLock *MXUserInternalSingleton(Atomic_Ptr *storage);
+
+#if defined(MXUSER_DEBUG)
+void MXUserAcquisitionTracking(MXUserHeader *header,
+ Bool checkRank);
+void MXUserReleaseTracking(MXUserHeader *header);
+#else
+static INLINE void
+MXUserAcquisitionTracking(MXUserHeader *header, // IN:
+ Bool checkRank) // IN:
+{
+ return;
+}
+
+static INLINE void
+MXUserReleaseTracking(MXUserHeader *header) // IN:
+{
+ return;
+}
+#endif
+
+static INLINE Bool
+MXUserTryAcquireFail(const char *lockName) // IN:
+{
+ extern Bool (*MXUserTryAcquireForceFail)(const char *lockName);
+
+ return vmx86_debug && MXUserTryAcquireForceFail &&
+ (*MXUserTryAcquireForceFail)(lockName);
+}
+
+MXUserCondVar *MXUserCreateCondVar(MXUserHeader *header,
+ MXRecLock *lock);
+
+void MXUserWaitCondVar(MXUserHeader *header,
+ MXRecLock *lock,
+ MXUserCondVar *condVar);
+
+
+#if defined(MXUSER_STATS)
+typedef struct {
+ uint64 numSamples; // Population sample size
+ uint64 minTime; // Minimum
+ uint64 maxTime; // Maximum
+ uint64 timeSum; // Sum of times (for mean)
+ double timeSquaredSum; // Sum of times^2 (for S.D.)
+} MXUserBasicStats;
+
+typedef struct {
+ uint64 numContended; // Number of contended acquires
+ uint64 timeContended; // Time spent contended on acquires
+ uint64 numUncontended; // Number of uncontended acquires
+ uint64 timeUncontended; // Time spent uncontended on acquires
+
+ MXUserBasicStats basicStats;
+} MXUserAcquisitionStats;
+
+typedef struct {
+ MXUserBasicStats basicStats; // total held statistics
+} MXUserReleaseStats;
+
+uint64 MXUserReadTimerNS(void);
+uint32 MXUserAllocID(void);
+void MXUserAddToList(MXUserHeader *header);
+void MXUserRemoveFromList(MXUserHeader *header);
+
+typedef struct MXUserHisto MXUserHisto;
+
+MXUserHisto *MXUserHistoSetUp(uint32 maxBins,
+ uint32 binWidth);
+
+void MXUserHistoTearDown(MXUserHisto *histo);
+
+void MXUserHistoSample(MXUserHisto *histo,
+ uint64 value);
+
+void MXUserHistoDump(unsigned epoch,
+ const char *className,
+ MXUserHeader *header,
+ MXUserHisto *histo);
+
+void MXUserAcquisitionStatsSetUp(MXUserAcquisitionStats *stats);
+
+void MXUserAcquisitionSample(MXUserAcquisitionStats *stats,
+ Bool wasContended,
+ uint64 timeToAcquire);
+
+void MXUserDumpAcquisitionStats(unsigned epoch,
+ const char *className,
+ MXUserHeader *header,
+ MXUserAcquisitionStats *stats);
+void
+MXUserBasicStatsSetUp(MXUserBasicStats *stats);
+
+void
+MXUserBasicStatsSample(MXUserBasicStats *stats,
+ uint64 value);
+
+void MXUserDumpBasicStats(unsigned epoch,
+ const char *className,
+ MXUserHeader *header,
+ MXUserBasicStats *stats);
+
+void MXUserKitchen(MXUserAcquisitionStats *stats,
+ double *contentionRatio,
+ Bool *isHot,
+ Bool *doLog);
+
+void MXUserForceHisto(Atomic_Ptr *histoPtr,
+ uint32 maxBins,
+ uint32 binWidth);
+#endif
+
+#endif
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2010 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+#ifndef _ULINTSHARED_H_
+#define _ULINTSHARED_H_
+
+extern void MXUserListLocks(void);
+
+extern void MXUserInstallMxHooks(void (*theLockListFunc)(void),
+ MX_Rank (*theRankFunc)(void));
+
+#endif
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2009 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+#include "vmware.h"
+#include "str.h"
+#include "util.h"
+#include "hashTable.h"
+#include "userlock.h"
+#include "ulInt.h"
+
+
+/*
+ * Environment specific implementations of portable read-write locks.
+ *
+ * There are 5 environment specific primitives:
+ *
+ * MXUserNativeRWSupported Are native read-write locks supported?
+ * MXUserNativeRWInit Allocate and initialize a native read-write lock
+ * MXUserNativeRWDestroy Destroy a native read-write lock
+ * MXUserNativeRWAcquire Acquire a native read-write lock
+ * MXUserNativeRWRelease Release a native read-write lock
+ */
+
+#if defined(_WIN32)
+typedef SRWLOCK NativeRWLock;
+
+typedef VOID (WINAPI *InitializeSRWLockFn)(PSRWLOCK lock);
+typedef VOID (WINAPI *AcquireSRWLockSharedFn)(PSRWLOCK lock);
+typedef VOID (WINAPI *ReleaseSRWLockSharedFn)(PSRWLOCK lock);
+typedef VOID (WINAPI *AcquireSRWLockExclusiveFn)(PSRWLOCK lock);
+typedef VOID (WINAPI *ReleaseSRWLockExclusiveFn)(PSRWLOCK lock);
+
+static InitializeSRWLockFn pInitializeSRWLock;
+static AcquireSRWLockSharedFn pAcquireSRWLockShared;
+static ReleaseSRWLockSharedFn pReleaseSRWLockShared;
+static AcquireSRWLockExclusiveFn pAcquireSRWLockExclusive;
+static ReleaseSRWLockExclusiveFn pReleaseSRWLockExclusive;
+
+static Bool
+MXUserNativeRWSupported(void)
+{
+ static Bool result;
+ static Bool done = FALSE;
+
+ if (!done) {
+ HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
+
+ if (kernel32) {
+ pInitializeSRWLock = (InitializeSRWLockFn)
+ GetProcAddress(kernel32,
+ "InitializeSRWLock");
+
+ pAcquireSRWLockShared = (AcquireSRWLockSharedFn)
+ GetProcAddress(kernel32,
+ "AcquireSRWLockShared");
+
+ pReleaseSRWLockShared = (ReleaseSRWLockSharedFn)
+ GetProcAddress(kernel32,
+ "ReleaseSRWLockShared");
+
+ pAcquireSRWLockExclusive = (AcquireSRWLockExclusiveFn)
+ GetProcAddress(kernel32,
+ "AcquireSRWLockExclusive");
+
+ pReleaseSRWLockExclusive = (ReleaseSRWLockExclusiveFn)
+ GetProcAddress(kernel32,
+ "ReleaseSRWLockExclusive");
+
+ result = ((pInitializeSRWLock == NULL) ||
+ (pAcquireSRWLockShared == NULL) ||
+ (pAcquireSRWLockExclusive == NULL) ||
+ (pReleaseSRWLockShared == NULL) ||
+ (pReleaseSRWLockExclusive == NULL)) ? FALSE : TRUE;
+ } else {
+ result = FALSE;
+ }
+
+ done = TRUE;
+ }
+
+ return result;
+}
+
+static Bool
+MXUserNativeRWInit(NativeRWLock *lock) // IN:
+{
+ ASSERT(pInitializeSRWLock);
+ (*pInitializeSRWLock)(lock);
+
+ return TRUE;
+}
+
+static int
+MXUserNativeRWDestroy(NativeRWLock *lock) // IN:
+{
+ return 0; // nothing to do
+}
+
+
+static INLINE Bool
+MXUserNativeRWAcquire(NativeRWLock *lock, // IN:
+ Bool forRead, // IN:
+ int *err) // OUT:
+{
+ if (forRead) {
+ ASSERT(pAcquireSRWLockShared);
+ (*pAcquireSRWLockShared)(lock);
+ } else {
+ ASSERT(pAcquireSRWLockExclusive);
+ (*pAcquireSRWLockExclusive)(lock);
+ }
+
+ *err = 0;
+
+ return FALSE;
+}
+
+static INLINE int
+MXUserNativeRWRelease(NativeRWLock *lock, // IN:
+ Bool forRead) // IN:
+{
+ if (forRead) {
+ ASSERT(pReleaseSRWLockShared);
+ (*pReleaseSRWLockShared)(lock);
+ } else {
+ ASSERT(pReleaseSRWLockExclusive);
+ (*pReleaseSRWLockExclusive)(lock);
+ }
+
+ return 0;
+}
+#else // _WIN32
+#if defined(PTHREAD_RWLOCK_INITIALIZER)
+typedef pthread_rwlock_t NativeRWLock;
+#else
+typedef int NativeRWLock;
+#endif
+
+static Bool
+MXUserNativeRWSupported(void)
+{
+#if defined(PTHREAD_RWLOCK_INITIALIZER)
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+static Bool
+MXUserNativeRWInit(NativeRWLock *lock) // IN:
+{
+#if defined(PTHREAD_RWLOCK_INITIALIZER)
+ return (pthread_rwlock_init(lock, NULL) == 0);
+#else
+ return FALSE;
+#endif
+}
+
+static int
+MXUserNativeRWDestroy(NativeRWLock *lock) // IN:
+{
+#if defined(PTHREAD_RWLOCK_INITIALIZER)
+ return pthread_rwlock_destroy(lock);
+#else
+ return ENOSYS;
+#endif
+}
+
+static INLINE Bool
+MXUserNativeRWAcquire(NativeRWLock *lock, // IN:
+ Bool forRead, // IN:
+ int *err) // OUT:
+{
+ Bool contended;
+
+#if defined(PTHREAD_RWLOCK_INITIALIZER)
+ *err = (forRead) ? pthread_rwlock_tryrdlock(lock) :
+ pthread_rwlock_trywrlock(lock);
+
+ contended = (*err != 0);
+
+ if (*err == EBUSY) {
+ *err = (forRead) ? pthread_rwlock_rdlock(lock) :
+ pthread_rwlock_wrlock(lock);
+ }
+#else
+ *err = ENOSYS;
+ contended = FALSE;
+#endif
+
+ return contended;
+}
+
+static INLINE int
+MXUserNativeRWRelease(NativeRWLock *lock, // IN:
+ Bool forRead) // IN:
+{
+#if defined(PTHREAD_RWLOCK_INITIALIZER)
+ return pthread_rwlock_unlock(lock);
+#else
+ return ENOSYS;
+#endif
+}
+#endif // _WIN32
+
+typedef enum {
+ RW_UNLOCKED,
+ RW_LOCKED_FOR_READ,
+ RW_LOCKED_FOR_WRITE
+} HolderState;
+
+typedef struct {
+#if defined(MXUSER_STATS)
+ uint64 holdStart;
+#endif
+
+ HolderState state;
+} HolderContext;
+
+struct MXUserRWLock
+{
+ MXUserHeader header;
+
+ Bool useNative;
+ NativeRWLock nativeLock;
+ MXRecLock recursiveLock;
+
+ Atomic_uint32 holderCount;
+ HashTable *holderTable;
+
+#if defined(MXUSER_STATS)
+ MXUserAcquisitionStats acquisitionStats;
+ Atomic_Ptr acquisitionHisto;
+
+ MXUserBasicStats heldStats;
+ Atomic_Ptr heldHisto;
+#endif
+};
+
+
+#if defined(MXUSER_STATS)
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserStatsActionRW --
+ *
+ * Perform the statistics action for the specified lock.
+ *
+ * Results:
+ * As above.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+MXUserStatsActionRW(MXUserHeader *header, // IN:
+ unsigned epoch) // IN:
+{
+ Bool isHot;
+ Bool doLog;
+ double contentionRatio;
+
+ MXUserRWLock *lock = (MXUserRWLock *) header;
+
+ /*
+ * Dump the statistics for the specified lock.
+ */
+
+ MXUserDumpAcquisitionStats(epoch, MXUSER_STAT_CLASS_ACQUISITION, header,
+ &lock->acquisitionStats);
+
+ if (Atomic_ReadPtr(&lock->acquisitionHisto) != NULL) {
+ MXUserHistoDump(epoch, MXUSER_STAT_CLASS_ACQUISITION, header,
+ Atomic_ReadPtr(&lock->acquisitionHisto));
+ }
+
+ MXUserDumpBasicStats(epoch, MXUSER_STAT_CLASS_HELD, header,
+ &lock->heldStats);
+
+ if (Atomic_ReadPtr(&lock->heldHisto) != NULL) {
+ MXUserHistoDump(epoch, MXUSER_STAT_CLASS_HELD, header,
+ Atomic_ReadPtr(&lock->heldHisto));
+ }
+
+ /*
+ * Has the lock gone "hot"? If so, implement the hot actions.
+ * Allow the read and write statistics to go independently hot.
+ */
+
+ MXUserKitchen(&lock->acquisitionStats, &contentionRatio, &isHot, &doLog);
+
+ if (isHot) {
+ MXUserForceHisto(&lock->acquisitionHisto, MXUSER_HISTOGRAM_MAX_BINS,
+ MXUSER_HISTOGRAM_NS_PER_BIN);
+ MXUserForceHisto(&lock->heldHisto, MXUSER_HISTOGRAM_MAX_BINS,
+ MXUSER_HISTOGRAM_NS_PER_BIN);
+
+ if (doLog) {
+ Log("HOT LOCK (%s); contention ratio %f\n",
+ lock->header.lockName, contentionRatio);
+ }
+ }
+}
+#endif
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserDumpRWLock --
+ *
+ * Dump an read-write lock.
+ *
+ * Results:
+ * A dump.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUserDumpRWLock(MXUserHeader *header) // IN:
+{
+ MXUserRWLock *lock = (MXUserRWLock *) header;
+
+ Warning("%s: Read-write lock @ %p\n", __FUNCTION__, lock);
+
+ Warning("\tsignature %X\n", lock->header.lockSignature);
+ Warning("\tname %s\n", lock->header.lockName);
+ Warning("\trank %d\n", lock->header.lockRank);
+
+ if (LIKELY(lock->useNative)) {
+ Warning("\tnativeLock %p\n", &lock->nativeLock);
+ } else {
+ Warning("\tcount %u\n", lock->recursiveLock.referenceCount);
+
+#if defined(MXUSER_DEBUG)
+ Warning("\tcaller %p\n", lock->recursiveLock.ownerRetAddr);
+ Warning("\tVThreadID %d\n", (int) lock->recursiveLock.portableThreadID);
+#endif
+ }
+
+ Warning("\tholderCount %d\n", Atomic_Read(&lock->holderCount));
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_CreateRWLock --
+ *
+ * Create a read/write lock.
+ *
+ * If native read-write locks are not available, a recursive lock will
+ * be used to provide one reader or one writer access... which is
+ * better than nothing.
+ *
+ * Results:
+ * A pointer to a read/write lock.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+MXUserFreeHashEntry(void *data) // IN:
+{
+ free(data);
+}
+
+MXUserRWLock *
+MXUser_CreateRWLock(const char *userName, // IN:
+ MX_Rank rank) // IN:
+{
+ Bool lockInited;
+ char *properName;
+ MXUserRWLock *lock;
+ Bool useNative = MXUserNativeRWSupported();
+
+ lock = Util_SafeCalloc(1, sizeof(*lock));
+
+ if (userName == NULL) {
+ if (LIKELY(useNative)) {
+ properName = Str_SafeAsprintf(NULL, "RW-%p", GetReturnAddress());
+ } else {
+ /* emulated */
+ properName = Str_SafeAsprintf(NULL, "RWemul-%p", GetReturnAddress());
+ }
+ } else {
+ properName = Util_SafeStrdup(userName);
+ }
+
+ /*
+ * Always attempt to use native locks when they are available. If, for some
+ * reason, a native lock should be available but isn't, fall back to using
+ * an internal recursive lock - something is better than nothing.
+ */
+
+ lock->useNative = useNative && MXUserNativeRWInit(&lock->nativeLock);
+
+ if (lock->useNative) {
+#if defined(MXUSER_STATS)
+ /* stats builds need an internal recursive lock for data integrity */
+
+ lockInited = MXRecLockInit(&lock->recursiveLock);
+
+ if (!lockInited) {
+ MXUserNativeRWDestroy(&lock->nativeLock);
+ }
+#else
+ lockInited = TRUE;
+#endif
+ } else {
+ lockInited = MXRecLockInit(&lock->recursiveLock);
+ }
+
+ if (LIKELY(lockInited)) {
+ lock->holderTable = HashTable_Alloc(256,
+ HASH_INT_KEY | HASH_FLAG_ATOMIC,
+ MXUserFreeHashEntry);
+
+ lock->header.lockName = properName;
+ lock->header.lockSignature = USERLOCK_SIGNATURE;
+ lock->header.lockRank = rank;
+ lock->header.lockDumper = MXUserDumpRWLock;
+
+#if defined(MXUSER_STATS)
+ lock->header.lockStatsAction = MXUserStatsActionRW;
+ lock->header.lockID = MXUserAllocID();
+
+ MXUserAddToList(&lock->header);
+#endif
+ } else {
+ free(properName);
+ free(lock);
+ lock = NULL;
+ }
+
+ return lock;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_DestroyRWLock --
+ *
+ * Destroy a read-write lock.
+ *
+ * Results:
+ * The lock is destroyed. Don't try to use the pointer again.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_DestroyRWLock(MXUserRWLock *lock) // IN:
+{
+ if (LIKELY(lock != NULL)) {
+ ASSERT(lock->header.lockSignature == USERLOCK_SIGNATURE);
+
+ if (Atomic_Read(&lock->holderCount) != 0) {
+ MXUserDumpAndPanic(&lock->header,
+ "%s: Destroy on an acquired read-write lock\n",
+ __FUNCTION__);
+ }
+
+ if (LIKELY(lock->useNative)) {
+ int err = MXUserNativeRWDestroy(&lock->nativeLock);
+
+ if (UNLIKELY(err != 0)) {
+ MXUserDumpAndPanic(&lock->header, "%s: Internal error (%d)\n",
+ __FUNCTION__, err);
+ }
+
+#if defined(MXUSER_STATS)
+ MXRecLockDestroy(&lock->recursiveLock);
+#endif
+ } else {
+ MXRecLockDestroy(&lock->recursiveLock);
+ }
+
+#if defined(MXUSER_STATS)
+ MXUserRemoveFromList(&lock->header);
+ MXUserHistoTearDown(Atomic_ReadPtr(&lock->acquisitionHisto));
+ MXUserHistoTearDown(Atomic_ReadPtr(&lock->heldHisto));
+#endif
+
+ HashTable_Free(lock->holderTable);
+ lock->header.lockSignature = 0; // just in case...
+ free((void *) lock->header.lockName); // avoid const warnings
+ lock->header.lockName = NULL;
+ free(lock);
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserGetHolderContext --
+ *
+ * Look up the context of the calling thread with respect to the
+ * specified lock and return it.
+ *
+ * Results:
+ * As above.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static HolderContext *
+MXUserGetHolderContext(MXUserRWLock *lock) // IN:
+{
+ HolderContext *result;
+ VThreadID tid = VThread_CurID();
+
+ ASSERT(lock->holderTable);
+
+ if (!HashTable_Lookup(lock->holderTable, (void *) (uintptr_t) tid,
+ (void **) &result)) {
+ HolderContext *newContext = Util_SafeMalloc(sizeof(HolderContext));
+
+#if defined(MXUSER_STATS)
+ newContext->holdStart = 0;
+#endif
+
+ newContext->state = RW_UNLOCKED;
+
+ result = HashTable_LookupOrInsert(lock->holderTable,
+ (void *) (uintptr_t) tid,
+ (void *) newContext);
+
+ if (result != newContext) {
+ free(newContext);
+ }
+ }
+
+ return result;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserAcquisition --
+ *
+ * Acquire a read-write lock in the specified mode.
+ *
+ * Results:
+ * As above.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE void
+MXUserAcquisition(MXUserRWLock *lock, // IN/OUT:
+ Bool forRead) // IN:
+{
+ int err = 0;
+ Bool contended;
+ HolderContext *myContext;
+
+#if defined(MXUSER_STATS)
+ uint64 begin;
+ uint64 value;
+ MXUserHisto *histo;
+#endif
+
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ MXUserAcquisitionTracking(&lock->header, TRUE);
+
+ myContext = MXUserGetHolderContext(lock);
+
+ if (UNLIKELY(myContext->state != RW_UNLOCKED)) {
+ MXUserDumpAndPanic(&lock->header,
+ "%s: AcquireFor%s after AcquireFor%s\n",
+ __FUNCTION__,
+ forRead ? "Read" : "Write",
+ (myContext->state == RW_LOCKED_FOR_READ) ? "Read" :
+ "Write");
+ }
+
+#if defined(MXUSER_STATS)
+ begin = MXUserReadTimerNS();
+#endif
+
+ contended = lock->useNative ? MXUserNativeRWAcquire(&lock->nativeLock,
+ forRead, &err) :
+ MXRecLockAcquire(&lock->recursiveLock,
+ GetReturnAddress());
+ if (UNLIKELY(err != 0)) {
+ MXUserDumpAndPanic(&lock->header, "%s: Error %d: contended %d\n",
+ __FUNCTION__, err, contended);
+ }
+
+#if defined(MXUSER_STATS)
+ value = MXUserReadTimerNS() - begin;
+
+ /* The statistics are not atomically safe so protect them when necessary */
+ if (forRead && lock->useNative) {
+ MXRecLockAcquire(&lock->recursiveLock, GetReturnAddress());
+ }
+
+ MXUserAcquisitionSample(&lock->acquisitionStats, contended, value);
+
+ histo = Atomic_ReadPtr(&lock->acquisitionHisto);
+
+ if (UNLIKELY(histo != NULL)) {
+ MXUserHistoSample(histo, value);
+ }
+
+ if (forRead && lock->useNative) {
+ MXRecLockRelease(&lock->recursiveLock);
+ }
+#endif
+
+ if (!forRead || !lock->useNative) {
+ ASSERT(Atomic_Read(&lock->holderCount) == 0);
+ }
+
+ Atomic_Inc(&lock->holderCount);
+ myContext->state = forRead ? RW_LOCKED_FOR_READ : RW_LOCKED_FOR_WRITE;
+
+#if defined(MXUSER_STATS)
+ myContext->holdStart = MXUserReadTimerNS();
+#endif
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_AcquireForRead --
+ *
+ * Acquire a read-write lock for read-shared access.
+ *
+ * Results:
+ * The lock is acquired.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_AcquireForRead(MXUserRWLock *lock) // IN/OUT:
+{
+ MXUserAcquisition(lock, TRUE);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_AcquireForWrite --
+ *
+ * Acquire a read-write lock for write-exclusive access.
+ *
+ * Results:
+ * The lock is acquired.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_AcquireForWrite(MXUserRWLock *lock) // IN/OUT:
+{
+ MXUserAcquisition(lock, FALSE);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_IsCurThreadHolding --
+ *
+ * Is the read-write lock held in the mode queried?
+ *
+ * Results:
+ * TRUE Yes
+ * FALSE No
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+MXUser_IsCurThreadHoldingRWLock(MXUserRWLock *lock, // IN:
+ uint32 queryType) // IN:
+{
+ HolderContext *myContext;
+
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ myContext = MXUserGetHolderContext(lock);
+
+ switch (queryType) {
+ case MXUSER_RW_FOR_READ:
+ return myContext->state == RW_LOCKED_FOR_READ;
+
+ case MXUSER_RW_FOR_WRITE:
+ return myContext->state == RW_LOCKED_FOR_WRITE;
+
+ case MXUSER_RW_LOCKED:
+ return myContext->state != RW_UNLOCKED;
+
+ default:
+#if defined(MXUSER_DEBUG)
+ Panic("%s: unknown query type %d\n", __FUNCTION__, queryType);
+#else
+ return FALSE;
+#endif
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_ReleaseRWLock --
+ *
+ * A read-write lock is released (unlocked).
+ *
+ * Results:
+ * The lock is released (unlocked).
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_ReleaseRWLock(MXUserRWLock *lock) // IN/OUT:
+{
+ HolderContext *myContext;
+
+#if defined(MXUSER_STATS)
+ uint64 holdEnd = MXUserReadTimerNS();
+ uint64 duration;
+ MXUserHisto *histo;
+#endif
+
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ myContext = MXUserGetHolderContext(lock);
+
+ if (UNLIKELY(myContext->state == RW_UNLOCKED)) {
+ uint32 lockCount = Atomic_Read(&lock->holderCount);
+
+ MXUserDumpAndPanic(&lock->header,
+ "%s: Non-owner release of an %s read-write lock\n",
+ __FUNCTION__,
+ lockCount == 0 ? "unacquired" : "acquired");
+ }
+
+#if defined(MXUSER_STATS)
+ duration = holdEnd - myContext->holdStart;
+
+ /* The statistics are not atomically safe so protect them when necessary */
+ if ((myContext->state == RW_LOCKED_FOR_READ) && lock->useNative) {
+ MXRecLockAcquire(&lock->recursiveLock, GetReturnAddress());
+ }
+
+ MXUserBasicStatsSample(&lock->heldStats, duration);
+
+ histo = Atomic_ReadPtr(&lock->heldHisto);
+
+ if (UNLIKELY(histo != NULL)) {
+ MXUserHistoSample(histo, duration);
+ }
+
+ if ((myContext->state == RW_LOCKED_FOR_READ) && lock->useNative) {
+ MXRecLockRelease(&lock->recursiveLock);
+ }
+#endif
+
+ MXUserReleaseTracking(&lock->header);
+
+ if (LIKELY(lock->useNative)) {
+ int err = MXUserNativeRWRelease(&lock->nativeLock, myContext->state);
+
+ if (UNLIKELY(err != 0)) {
+ MXUserDumpAndPanic(&lock->header, "%s: Internal error (%d)\n",
+ __FUNCTION__, err);
+ }
+ } else {
+ ASSERT(Atomic_Read(&lock->holderCount) == 0);
+ MXRecLockRelease(&lock->recursiveLock);
+ }
+
+ myContext->state = RW_UNLOCKED;
+ Atomic_Dec(&lock->holderCount);
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2009 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+#include "vmware.h"
+#include "str.h"
+#include "util.h"
+#include "userlock.h"
+#include "ulInt.h"
+
+#if defined(VMX86_VMX)
+#include "mutexInt.h"
+#include "mutexRank.h"
+#else
+typedef struct MX_MutexRec MX_MutexRec;
+
+static INLINE void
+MX_LockRec(MX_MutexRec *lock) // IN:
+{
+ NOT_IMPLEMENTED();
+}
+
+static INLINE void
+MX_UnlockRec(MX_MutexRec *lock) // IN:
+{
+ NOT_IMPLEMENTED();
+}
+
+static INLINE Bool
+MX_TryLockRec(MX_MutexRec *lock) // IN:
+{
+ NOT_IMPLEMENTED();
+}
+
+static INLINE Bool
+MX_IsLockedByCurThreadRec(MX_MutexRec *lock) // IN:
+{
+ NOT_IMPLEMENTED();
+}
+#endif
+
+struct MXUserRecLock
+{
+ MXUserHeader header;
+ MXRecLock recursiveLock;
+
+ uint64 holdStart;
+
+#if defined(MXUSER_STATS)
+ MXUserAcquisitionStats acquisitionStats;
+ Atomic_Ptr acquisitionHisto;
+
+ MXUserBasicStats heldStats;
+ Atomic_Ptr heldHisto;
+#endif
+
+ /*
+ * This is the MX recursive lock override pointer. It is used within the
+ * VMX only.
+ *
+ * NULL Use the MXRecLock within this structure
+ * MXUser_CreateRecLock was used to create the lock
+ *
+ * !NULL Use the MX_MutexRec pointed to by vmmLock
+ * MXUser_BindMXMutexRec was used to create the lock
+ */
+
+ MX_MutexRec *vmmLock;
+};
+
+#if defined(MXUSER_STATS)
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserStatsActionRec --
+ *
+ * Perform the statistics action for the specified lock.
+ *
+ * Results:
+ * As above.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+MXUserStatsActionRec(MXUserHeader *header, // IN:
+ unsigned epoch) // IN:
+{
+ Bool isHot;
+ Bool doLog;
+ double contentionRatio;
+
+ MXUserRecLock *lock = (MXUserRecLock *) header;
+
+ /*
+ * Dump the statistics for the specified lock.
+ */
+
+ MXUserDumpAcquisitionStats(epoch, MXUSER_STAT_CLASS_ACQUISITION, header,
+ &lock->acquisitionStats);
+
+ if (Atomic_ReadPtr(&lock->acquisitionHisto) != NULL) {
+ MXUserHistoDump(epoch, MXUSER_STAT_CLASS_ACQUISITION, header,
+ Atomic_ReadPtr(&lock->acquisitionHisto));
+ }
+
+ MXUserDumpBasicStats(epoch, MXUSER_STAT_CLASS_HELD, header,
+ &lock->heldStats);
+
+ if (Atomic_ReadPtr(&lock->heldHisto) != NULL) {
+ MXUserHistoDump(epoch, MXUSER_STAT_CLASS_HELD, header,
+ Atomic_ReadPtr(&lock->heldHisto));
+ }
+
+ /*
+ * Has the lock gone "hot"? If so, implement the hot actions.
+ */
+
+ MXUserKitchen(&lock->acquisitionStats, &contentionRatio, &isHot, &doLog);
+
+ if (isHot) {
+ MXUserForceHisto(&lock->acquisitionHisto, MXUSER_HISTOGRAM_MAX_BINS,
+ MXUSER_HISTOGRAM_NS_PER_BIN);
+ MXUserForceHisto(&lock->heldHisto, MXUSER_HISTOGRAM_MAX_BINS,
+ MXUSER_HISTOGRAM_NS_PER_BIN);
+
+ if (doLog) {
+ Log("HOT LOCK (%s); contention ratio %f\n",
+ lock->header.lockName, contentionRatio);
+ }
+ }
+}
+#endif
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUserDumpRecLock --
+ *
+ * Dump an recursive lock.
+ *
+ * Results:
+ * A dump.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+MXUserDumpRecLock(MXUserHeader *header) // IN:
+{
+ MXUserRecLock *lock = (MXUserRecLock *) header;
+
+ Warning("%s: Recursive lock @ %p\n", __FUNCTION__, lock);
+
+ Warning("\tsignature %X\n", lock->header.lockSignature);
+ Warning("\tname %s\n", lock->header.lockName);
+ Warning("\trank %d\n", lock->header.lockRank);
+
+ if (lock->vmmLock == NULL) {
+ Warning("\tcount %u\n", lock->recursiveLock.referenceCount);
+
+#if defined(MXUSER_DEBUG)
+ Warning("\tcaller %p\n", lock->recursiveLock.ownerRetAddr);
+ Warning("\tVThreadID %d\n", (int) lock->recursiveLock.portableThreadID);
+#endif
+ } else {
+ Warning("\tvmmLock %p\n", lock->vmmLock);
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_CreateRecLock --
+ *
+ * Create a recursive lock.
+ *
+ * Only the owner (thread) of a recursive lock may recurse on it.
+ *
+ * Results:
+ * NULL Creation failed
+ * !NULL Creation succeeded
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+MXUserRecLock *
+MXUser_CreateRecLock(const char *userName, // IN:
+ MX_Rank rank) // IN:
+{
+ char *properName;
+ MXUserRecLock *lock;
+
+ lock = Util_SafeCalloc(1, sizeof(*lock));
+
+ if (userName == NULL) {
+ properName = Str_SafeAsprintf(NULL, "R-%p", GetReturnAddress());
+ } else {
+ properName = Util_SafeStrdup(userName);
+ }
+
+ if (!MXRecLockInit(&lock->recursiveLock)) {
+ free(properName);
+ free(lock);
+
+ return NULL;
+ }
+
+ lock->vmmLock = NULL;
+
+ lock->header.lockName = properName;
+ lock->header.lockSignature = USERLOCK_SIGNATURE;
+ lock->header.lockRank = rank;
+ lock->header.lockDumper = MXUserDumpRecLock;
+
+#if defined(MXUSER_STATS)
+ lock->header.lockStatsAction = MXUserStatsActionRec;
+ lock->header.lockID = MXUserAllocID();
+
+ MXUserAddToList(&lock->header);
+ MXUserAcquisitionStatsSetUp(&lock->acquisitionStats);
+ MXUserBasicStatsSetUp(&lock->heldStats);
+#endif
+
+ return lock;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_DestroyRecLock --
+ *
+ * Destroy a recursive lock.
+ *
+ * When the lock is bound to a MX lock, only the MXUser "wrapper" is
+ * freed. The caller is responsible for calling MX_DestroyLockRec() on
+ * the MX lock.
+ *
+ * Results:
+ * Lock is destroyed. Don't use the pointer again.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_DestroyRecLock(MXUserRecLock *lock) // IN:
+{
+ if (lock != NULL) {
+ ASSERT(lock->header.lockSignature == USERLOCK_SIGNATURE);
+
+ if (!lock->vmmLock) {
+ if (MXRecLockCount(&lock->recursiveLock) > 0) {
+ MXUserDumpAndPanic(&lock->header,
+ "%s: Destroy of an acquired recursive lock\n",
+ __FUNCTION__);
+ }
+
+ MXRecLockDestroy(&lock->recursiveLock);
+
+#if defined(MXUSER_STATS)
+ MXUserRemoveFromList(&lock->header);
+ MXUserHistoTearDown(Atomic_ReadPtr(&lock->acquisitionHisto));
+ MXUserHistoTearDown(Atomic_ReadPtr(&lock->heldHisto));
+#endif
+ }
+
+ lock->header.lockSignature = 0; // just in case...
+ free((void *) lock->header.lockName); // avoid const warnings
+ lock->header.lockName = NULL;
+ free(lock);
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_AcquireRecLock --
+ *
+ * An acquisition is made (lock is taken) on the specified recursive lock.
+ *
+ * Only the owner (thread) of a recursive lock may recurse on it.
+ *
+ * Results:
+ * The lock is acquired (locked).
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_AcquireRecLock(MXUserRecLock *lock) // IN/OUT:
+{
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ if (lock->vmmLock) {
+ MX_LockRec(lock->vmmLock);
+ } else {
+#if defined(MXUSER_STATS)
+ Bool contended;
+ uint64 begin;
+#endif
+ /* Rank checking is only done on the first acquisition */
+ MXUserAcquisitionTracking(&lock->header, TRUE);
+
+#if defined(MXUSER_STATS)
+ begin = MXUserReadTimerNS();
+
+ contended =
+#endif
+
+ MXRecLockAcquire(&lock->recursiveLock, GetReturnAddress());
+
+#if defined(MXUSER_STATS)
+ if (MXRecLockCount(&lock->recursiveLock) == 1) {
+ MXUserHisto *histo;
+ uint64 value = MXUserReadTimerNS() - begin;
+
+ MXUserAcquisitionSample(&lock->acquisitionStats, contended, value);
+
+ histo = Atomic_ReadPtr(&lock->acquisitionHisto);
+
+ if (UNLIKELY(histo != NULL)) {
+ MXUserHistoSample(histo, value);
+ }
+
+ lock->holdStart = MXUserReadTimerNS();
+ }
+#endif
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_ReleaseRecLock --
+ *
+ * Release (unlock) a recursive lock.
+ *
+ * Results:
+ * The lock is released.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_ReleaseRecLock(MXUserRecLock *lock) // IN/OUT:
+{
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ if (lock->vmmLock) {
+ MX_UnlockRec(lock->vmmLock);
+ } else {
+#if defined(MXUSER_STATS)
+ if (MXRecLockCount(&lock->recursiveLock) == 1) {
+ uint64 value = MXUserReadTimerNS() - lock->holdStart;
+ MXUserHisto *histo = Atomic_ReadPtr(&lock->heldHisto);
+
+ MXUserBasicStatsSample(&lock->heldStats, value);
+
+ if (UNLIKELY(histo != NULL)) {
+ MXUserHistoSample(histo, value);
+ }
+ }
+#endif
+
+ if (!MXRecLockIsOwner(&lock->recursiveLock)) {
+ uint32 lockCount = MXRecLockCount(&lock->recursiveLock);
+
+ MXUserDumpAndPanic(&lock->header,
+ "%s: Non-owner release of an %s recursive lock\n",
+ __FUNCTION__,
+ lockCount == 0 ? "unacquired" : "acquired");
+ }
+
+ MXUserReleaseTracking(&lock->header);
+
+ MXRecLockRelease(&lock->recursiveLock);
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_TryAcquireRecLock --
+ *
+ * An attempt is made to conditionally acquire (lock) a recursive lock.
+ *
+ * Only the owner (thread) of a recursive lock may recurse on it.
+ *
+ * Results:
+ * TRUE Acquired (locked)
+ * FALSE Not acquired
+ *
+ * Side effects:
+ * None
+ *
+ * NOTE:
+ * A "TryAcquire" does not rank check should the acquisition succeed.
+ * This duplicates the behavor of MX locks.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+MXUser_TryAcquireRecLock(MXUserRecLock *lock) // IN/OUT:
+{
+ Bool success;
+
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ if (lock->vmmLock) {
+ success = MX_TryLockRec(lock->vmmLock);
+ } else {
+#if defined(MXUSER_STATS)
+ uint64 begin;
+#endif
+
+ if (MXUserTryAcquireFail(lock->header.lockName)) {
+ return FALSE;
+ }
+
+#if defined(MXUSER_STATS)
+ begin = MXUserReadTimerNS();
+#endif
+
+ success = MXRecLockTryAcquire(&lock->recursiveLock, GetReturnAddress());
+
+ if (success) {
+#if defined(MXUSER_STATS)
+ if (MXRecLockCount(&lock->recursiveLock) == 1) {
+ MXUserAcquisitionSample(&lock->acquisitionStats, FALSE,
+ MXUserReadTimerNS() - begin);
+ }
+#endif
+
+ MXUserAcquisitionTracking(&lock->header, FALSE);
+ }
+ }
+
+ return success;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_IsCurThreadHoldingRecLock --
+ *
+ * Is a recursive lock held by the calling thread?
+ *
+ * Results:
+ * TRUE Yes
+ * FALSE No
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+MXUser_IsCurThreadHoldingRecLock(const MXUserRecLock *lock) // IN:
+{
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ return (lock->vmmLock) ? MX_IsLockedByCurThreadRec(lock->vmmLock) :
+ MXRecLockIsOwner(&lock->recursiveLock);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_ControlRecLock --
+ *
+ * Perform the specified command on the specified lock.
+ *
+ * Results:
+ * TRUE succeeded
+ * FALSE failed
+ *
+ * Side effects:
+ * Depends on the command, no?
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+MXUser_ControlRecLock(MXUserRecLock *lock, // IN/OUT:
+ uint32 command, // IN:
+ ...) // IN:
+{
+ Bool result;
+
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ switch (command) {
+#if defined(MXUSER_STATS)
+ case MXUSER_CONTROL_ACQUISITION_HISTO: {
+ va_list a;
+ uint32 numBins;
+ uint32 binWidth;
+
+ va_start(a, command);
+ numBins = va_arg(a, uint32);
+ binWidth = va_arg(a, uint32);
+ va_end(a);
+
+ MXUserForceHisto(&lock->acquisitionHisto, numBins, binWidth);
+
+ result = TRUE;
+ break;
+ }
+
+ case MXUSER_CONTROL_HELD_HISTO: {
+ va_list a;
+ uint32 numBins;
+ uint32 binWidth;
+
+ va_start(a, command);
+ numBins = va_arg(a, uint32);
+ binWidth = va_arg(a, uint32);
+ va_end(a);
+
+ MXUserForceHisto(&lock->heldHisto, numBins, binWidth);
+
+ result = TRUE;
+ break;
+ }
+#endif
+
+ default:
+ result = FALSE;
+ }
+
+ return result;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_CreateSingletonRecLock --
+ *
+ * Ensures that the specified backing object (Atomic_Ptr) contains a
+ * recursive lock. This is useful for modules that need to protect
+ * something with a lock but don't have an existing Init() entry point
+ * where a lock can be created.
+ *
+ * Results:
+ * A pointer to the requested lock.
+ *
+ * Side effects:
+ * Generally the lock's resources are intentionally leaked (by design).
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+MXUserRecLock *
+MXUser_CreateSingletonRecLock(Atomic_Ptr *lockStorage, // IN/OUT:
+ const char *name, // IN:
+ MX_Rank rank) // IN:
+{
+ MXUserRecLock *lock;
+
+ ASSERT(lockStorage);
+
+ lock = (MXUserRecLock *) Atomic_ReadPtr(lockStorage);
+
+ if (UNLIKELY(lock == NULL)) {
+ MXUserRecLock *before;
+
+ lock = MXUser_CreateRecLock(name, rank);
+
+ before = (MXUserRecLock *) Atomic_ReadIfEqualWritePtr(lockStorage, NULL,
+ (void *) lock);
+
+ if (before) {
+ MXUser_DestroyRecLock(lock);
+
+ lock = before;
+ }
+ }
+
+ return lock;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_CreateCondVarRecLock --
+ *
+ * Create a condition variable for use with the specified recurisve lock.
+ *
+ * Results:
+ * As above.
+ *
+ * Side effects:
+ * The created condition variable will cause a run-time error if it is
+ * used with a lock other than the one it was created for.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+MXUserCondVar *
+MXUser_CreateCondVarRecLock(MXUserRecLock *lock)
+{
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ return MXUserCreateCondVar(&lock->header, &lock->recursiveLock);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_WaitCondVarRecLock --
+ *
+ * Block (sleep) on the specified condition variable. The specified lock
+ * is released upon blocking and is reacquired before returning from this
+ * function.
+ *
+ * Results:
+ * As above.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+MXUser_WaitCondVarRecLock(MXUserRecLock *lock, // IN:
+ MXUserCondVar *condVar) // IN:
+{
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ MXUserWaitCondVar(&lock->header, &lock->recursiveLock, condVar);
+}
+
+
+#if defined(VMX86_VMX)
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_GetRecLockVmm --
+ *
+ * Return lock->vmmLock. Perhaps this lock is bound to an MX lock.
+ *
+ * Results:
+ * lock->vmmLock is returned.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+MX_MutexRec *
+MXUser_GetRecLockVmm(const MXUserRecLock *lock) // IN:
+{
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ return lock->vmmLock;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_GetRecLockRank --
+ *
+ * Return the rank of the specified recursive lock.
+ *
+ * Results:
+ * The rank of the specified recursive lock is returned.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+MX_Rank
+MXUser_GetRecLockRank(const MXUserRecLock *lock) // IN:
+{
+ ASSERT(lock && (lock->header.lockSignature == USERLOCK_SIGNATURE));
+
+ return lock->header.lockRank;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MXUser_BindMXMutexRec --
+ *
+ * Create an MXUserRecLock that is bound to an (already) initialized
+ * MX_MutexRec.
+ *
+ * Results:
+ * NULL Creation failed
+ * !NULL Creation succeeded
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+MXUserRecLock *
+MXUser_BindMXMutexRec(MX_MutexRec *mutex) // IN:
+{
+ MXUserRecLock *lock;
+
+ ASSERT(mutex);
+
+ /*
+ * Initialize the header (so it looks correct in memory) but don't connect
+ * this lock to the MXUser statistics or debugging tracking - the MX lock
+ * system will take care of this.
+ */
+
+ lock = Util_SafeCalloc(1, sizeof(*lock));
+
+ lock->header.lockName = Str_SafeAsprintf(NULL, "MX_LockID%d",
+ mutex->lck.lid);
+
+ lock->header.lockSignature = USERLOCK_SIGNATURE;
+ lock->header.lockRank = mutex->lck.rank;
+ lock->header.lockDumper = NULL;
+
+#if defined(MXUSER_STATS)
+ lock->header.lockStatsAction = NULL;
+ lock->header.lockID = MXUserAllocID();
+#endif
+
+ lock->vmmLock = mutex;
+
+ return lock;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * MXUser_InitFromMXRec --
+ *
+ * Initialize a MX_MutexRec lock and create a MXUserRecLock that binds
+ * to it.
+ *
+ * Results:
+ * Pointer to the MXUserRecLock.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+MXUserRecLock *
+MXUser_InitFromMXRec(const char *name, // IN:
+ MX_MutexRec *mutex, // IN:
+ MX_Rank rank, // IN:
+ Bool isBelowBull) // IN:
+{
+ MXUserRecLock *userLock;
+
+ ASSERT(isBelowBull == (rank < RANK_userlevelLock));
+
+ MX_InitLockRec(name, rank, mutex);
+ userLock = MXUser_BindMXMutexRec(mutex);
+ ASSERT(userLock);
+
+ return userLock;
+}
+#endif
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2010 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+/*
+ * vthreadBase.c --
+ *
+ * Base thread management functionality. Does not care whether threads
+ * are used or not.
+ *
+ * For full thread management (e.g. creation/destruction), see lib/thread.
+ *
+ * Major exposed functions and their properties:
+ * VThreadBase_CurName - Returns a thread name. Will try to assign
+ * a default name if none exists, but if called
+ * reentrantly (e.g. due to ASSERT) will supply
+ * a failsafe name instead.
+ * VThreadBase_CurID - Always populates a VThreadID; will Panic if
+ * this fails (extremely unlikely).
+ * VThreadBase_SetName - Sets current thread name. Assigns a VThreadID
+ * if one is not already present.
+ *
+ * Functions useful for implementing a full thread library:
+ * VThreadBase_InitThread - Sets up thread with a specific VThreadID
+ * and name.
+ * VThreadBase_SetNoIDFunc - The NoID function is called whenever an
+ * unknown thread is seen; it must call
+ * VThreadBase_InitThread to assign a
+ * VThreadID. Note that this function
+ * runs with all signals masked!
+ * VThreadBase_ForgetSelf - Clears the VThreadID for current thread,
+ * to clean up resource usage prior to thread
+ * exit.
+ * Historical quirks:
+ * * Default thread numbering starts at VTHREAD_MAX_VCPUs + 2
+ * to allow VThread_IsVCPU() to run efficiently.
+ * * Most other code uses VThread_Foo instead of VThreadBase_Foo; the
+ * public header file uses inlines to convert names.
+ *
+ * VThreadBase is self-initializing; by default, threads will be given
+ * names like "vthread-1", "vthread-32", etc. Use VThread_SetName to
+ * provide more meaningful names (often, this is the only initialization
+ * needed).
+ *
+ * The default implementation supports an (effectively) unlimited number
+ * of threads, and OS-specific primitives may be used to start the
+ * threads. If lib/thread is used on top of this library, the lib/thread
+ * NoID function may introduce a smaller limit.
+ *
+ * On Windows and Mac, there is no way to compile single-threaded,
+ * so just make all the multi-threaded calls.
+ *
+ * On Linux, use some more complex logic to operate in two modes:
+ * - if _REENTRANT is defined (or implied by -pthread), link
+ * directly to all multithreaded symbols.
+ * - otherwise, compile in fake-pthread functions and choose at
+ * runtime whether to use them, depending on if pthreads are loaded.
+ */
+
+#if defined __linux__ && !defined _REENTRANT
+# define FAKE_PTHREADS
+#endif
+
+#if defined _WIN32
+# include <windows.h>
+#else
+# if defined(sun) && !defined(_XOPEN_SOURCE)
+ /*
+ * Solaris headers don't define constants we need unless
+ * the Posix standard is somewhat modern. Most of our builds
+ * set this; we should chase down the oversight.
+ */
+# define _XOPEN_SOURCE 500
+# endif
+# include <pthread.h>
+# include <signal.h>
+# include <errno.h>
+# include <limits.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h> /* snprintf */
+
+#include "vmware.h"
+#include "vm_atomic.h"
+#include "vthreadBase.h"
+#include "str.h"
+#include "util.h"
+#include "hashTable.h"
+
+
+/*
+ * Table of thread types
+ * =====================
+ * OS Thread type TLS key type Max TLS keys
+ * -----------------------------------------------------------------
+ * Windows HANDLE / void * DWORD 0xFFFFFFFF
+ * (Posix) (pthread_t) (pthread_key_t) (PTHREAD_KEYS_MAX)
+ * Linux unsigned long unsigned int 1024
+ * OS X 10.5 struct _opaque * unsigned long 512
+ * Solaris 10 unsigned int unsigned int 128
+ * FreeBSD 8 struct pthread * int 256
+ */
+#if defined _WIN32
+typedef DWORD VThreadBaseKeyType;
+#define VTHREADBASE_INVALID_KEY (VThreadBaseKeyType)(TLS_OUT_OF_INDEXES)
+#else
+typedef pthread_key_t VThreadBaseKeyType;
+#define VTHREADBASE_INVALID_KEY (VThreadBaseKeyType)(PTHREAD_KEYS_MAX)
+#endif
+
+static void VThreadBaseSimpleNoID(void);
+
+static struct {
+ Atomic_Int key;
+ Atomic_Int dynamicID;
+ Atomic_Int numThreads;
+ Atomic_Ptr nativeHash;
+ void (*noIDFunc)(void);
+} vthreadBaseGlobals = {
+ { VTHREADBASE_INVALID_KEY },
+ { VTHREAD_ALLOCSTART_ID },
+ { 0 },
+ { 0 },
+ VThreadBaseSimpleNoID,
+};
+
+
+#if defined FAKE_PTHREADS
+/*
+ * glibc can either be used with pthreads or without.
+ *
+ * Nominally, glibc-without-pthreads is useful in cases like embedded systems
+ * where the overhead of libpthread (in terms of memory or CPU time) is not
+ * justified. Unfortunately for us, ESX userworlds are close enough to
+ * embedded systems that it's easier to navigate the pain than argue in favor
+ * of linking extraneous libraries. (Eventually, we should just link.)
+ *
+ * A quick run-through of all the different ways to run a Linux program:
+ *
+ * Compile with -pthread: (best, but uncommon)
+ * Compiler passes -pthread to gcc, which implicitly defines _REENTRANT.
+ * If this constant is defined, it is OK to link directly to all pthread
+ * symbols: the program is known multithreaded at compile-time. We will
+ * also accept somebody passing -D_REENTRANT as an indication they know
+ * what they are doing and want compile-time threading support.
+ *
+ * Compile without -pthread, link with -lpthread (common)
+ * Running multi-threaded, but this cannot be distinguised from running
+ * single-threaded without peeking into the dynamic linker.
+ * at run-time.
+ *
+ * Compile without -pthread, link without -lpthread, dlopen("libpthread.so")
+ * This is broken. libc starts using a simplified pthread implementation,
+ * then the dynamic load changes the implementation mid-stream. Any locks
+ * created before the dynamic open are thus unsafe. DO NOT DO THIS.
+ *
+ * Compile without -pthread, link without -lpthread, no dlopen() (common)
+ * Running single-threaded.
+ *
+ *
+ * After much experimentation, the only sane way to construct VThreadBase is
+ * with weak symbols, and require a ((sym != NULL) ? pthread_sym : fake_sym)
+ * construct for all pthread symbols used herein. This approach has two
+ * downsides:
+ * - extra branch on all TLS lookups (though easily predicted)
+ * - some compilers (gcc-4.1.0, gcc-4.1.1) have a buggy weak-symbol
+ * optimizer.
+ * Both of these downsides can be avoided by passing -D_REENTRANT to
+ * the compilation of this environment and supplying -lpthread at link time.
+ *
+ * Rejected approaches:
+ * - use dl_xxx to detect libpthread and link at runtime. This adds a libdl
+ * dependency that is as bad as the libpthread dependency in the first place.
+ * - use __libc_dlxxx@_GLIBC_PRIVATE symbols to detect libpthread and link
+ * at runtime. This works; however, 'rpm' refuses to package binaries
+ * with @_GLIBC_PRIVATE symbols and so builds break.
+ * - use a fake TLS backed by a locked hash table. Unfortunately,
+ * pthread_mutex_lock isn't async-signal-safe and we do read TLS within
+ * our signal handlers. (Oddly enough, pthread_getspecific is not marked
+ * as async-signal-safe either, but it happens to work fine.)
+ * - use a fake TLS backed by an atomic hash table. Alas, our atomic
+ * hash tables are insert-only, which causes a memory leak even if threads
+ * exit cleanly.
+ *
+ * If anything here ever breaks, the best thing to do is simply admit that
+ * this is the 21st century and always compile with -pthread.
+ */
+
+#if defined __GNUC__
+/* gcc-4.1.0 and gcc-4.1.1 have buggy weak-symbol optimiziation. */
+# if __GNUC__ == 4 && __GNUC_MINOR__ == 1 && \
+ (__GNUC_PATCHLEVEL__ == 0 || __GNUC_PATCHLEVEL__ == 1)
+# error Cannot build VThreadBase with weak symbols: buggy gcc version
+# endif
+#endif
+
+extern int pthread_key_create(pthread_key_t *key, void (*destr_function)(void *))
+ __attribute__ ((weak));
+extern int pthread_key_delete(pthread_key_t key)
+ __attribute__ ((weak));
+extern int pthread_setspecific(pthread_key_t key, const void *pointer)
+ __attribute__ ((weak));
+extern void * pthread_getspecific(pthread_key_t key)
+ __attribute__ ((weak));
+extern int pthread_sigmask(int how, const sigset_t *newmask, sigset_t *oldmask)
+ __attribute__ ((weak));
+extern int pthread_kill(pthread_t thread, int signo)
+ __attribute__ ((weak));
+
+
+static const pthread_key_t nothreadTLSKey = 0x12345; /* Chosen to be obvious */
+static void *nothreadTLSData;
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * fake_key_create --
+ * fake_key_delete --
+ * fake_setspecific --
+ * fake_getspecific --
+ * fake_sigmask --
+ * fake_kill --
+ *
+ * Trivial implementations of equivalent pthread functions, to be used
+ * when the weak pthread_xxx symbols are not defined (e.g. when
+ * libpthread.so is not loaded).
+ *
+ * These versions always succeed and are hard-coded to assume one thread.
+ *
+ * NOTE: These functions will not work if libpthread is dlopen()ed.
+ * That said, any pthread functions (like pthread_mutex_lock) also
+ * will not work, so we have lost nothing.
+ *
+ * Results:
+ * See pthread_xxx; these versions always succeed.
+ *
+ * Side effects:
+ * See pthread_xxx.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+fake_key_create(pthread_key_t *key,
+ void (*destr_function)(void *))
+{
+ *key = nothreadTLSKey;
+ return 0;
+}
+
+static int
+fake_key_delete(pthread_key_t key)
+{
+ /*
+ * fake_key_delete will not be called if there are no threads because:
+ * - fake_key_create does not fail with no threads
+ * - single threads cannot race with themselves in VThreadBaseGetKey
+ * (a race requires deleting extra keys)
+ */
+ NOT_REACHED();
+}
+
+static int
+fake_setspecific(pthread_key_t key,
+ const void *pointer)
+{
+ ASSERT(key == nothreadTLSKey);
+ ASSERT(Atomic_Read(&vthreadBaseGlobals.numThreads) <= 1);
+ nothreadTLSData = (void *)pointer;
+ return 0;
+}
+
+static void *
+fake_getspecific(pthread_key_t key)
+{
+ ASSERT(key == nothreadTLSKey);
+ ASSERT(Atomic_Read(&vthreadBaseGlobals.numThreads) <= 1);
+ return nothreadTLSData;
+}
+
+static int
+fake_sigmask(int how, const sigset_t *newmask, sigset_t *oldmask)
+{
+ /*
+ * Verified against glibc sources: pthread_sigmask and sigprocmask
+ * use the same implementation for any kernel that supports
+ * the rt_sigprocmask syscall (and all kernels we support do).
+ */
+ return sigprocmask(how, newmask, oldmask);
+}
+
+static int
+fake_kill(pthread_t thread, int signo)
+{
+ /*
+ * If there is no thread library, there is only one thread,
+ * and it is obviously running because it called this function.
+ * It is impossible to obtain any other pthread_t value.
+ */
+ ASSERT(signo == 0);
+ ASSERT(pthread_equal(thread, pthread_self()));
+ return 0;
+}
+
+/*
+ * Replace all pthread_xxx calls with a runtime choice. This
+ * code is not quite optimal; if perfection is necessary,
+ * compile with -D_REENTRANT to link directly to pthreads.
+ */
+#define WRAP_WEAK(_fn) \
+ ((pthread_##_fn != NULL) ? pthread_##_fn : fake_##_fn)
+#define pthread_key_create WRAP_WEAK(key_create)
+#define pthread_key_delete WRAP_WEAK(key_delete)
+#define pthread_getspecific WRAP_WEAK(getspecific)
+#define pthread_setspecific WRAP_WEAK(setspecific)
+#define pthread_sigmask WRAP_WEAK(sigmask)
+#define pthread_kill WRAP_WEAK(kill)
+
+#endif /* FAKE_PTHREADS */
+
+/*
+ * Code to mask all asynchronous signals
+ *
+ * There are some stretches where an async signal will cause reentrancy,
+ * and that breaks allocating a VThreadID and/or setting the TLS slot
+ * atomically. So mask all asynchronous signals; synchronous signals
+ * (which are generally fatal) are still OK.
+ *
+ * Though these functions can return errors; it is not worthwhile to check
+ * them because the inputs are hard-coded. The only errors allowed
+ * are EINVAL for invalid argument (and they are all hardcoded valid) and
+ * EFAULT for the addresses (and a stack pointer will not EFAULT). Besides,
+ * these routines are used in code paths that cannot Panic without creating
+ * a Panic-loop.
+ */
+#if defined _WIN32
+#define NO_ASYNC_SIGNALS_START do {
+#define NO_ASYNC_SIGNALS_END } while (0)
+#else
+#define NO_ASYNC_SIGNALS_START \
+ do { \
+ sigset_t setMask, oldMask; \
+ sigfillset(&setMask); \
+ sigdelset(&setMask, SIGBUS); \
+ sigdelset(&setMask, SIGSEGV); \
+ sigdelset(&setMask, SIGILL); \
+ sigdelset(&setMask, SIGABRT); \
+ pthread_sigmask(SIG_BLOCK, &setMask, &oldMask);
+#define NO_ASYNC_SIGNALS_END \
+ pthread_sigmask(SIG_SETMASK, &oldMask, NULL); \
+ } while(0)
+#endif
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBaseGetKey --
+ *
+ * Get the host-specific TLS slot.
+ *
+ * Failure to allocate a TLS slot is immediately fatal. Note that
+ * a TLS slot is generally allocated at the first of:
+ * - VThread_Init() (e.g. uses lib/thread)
+ * - VThread_SetName()
+ * - a Posix signal
+ * - a lock acquisition
+ * Since most Panic paths do look up a thread name (and thus need a TLS
+ * slot), a program that does not want to Panic-loop should call
+ * one of the above functions very early to "prime" the TLS slot.
+ *
+ * Results:
+ * OS-specific TLS key.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static VThreadBaseKeyType
+VThreadBaseGetKey(void)
+{
+ VThreadBaseKeyType key = Atomic_Read(&vthreadBaseGlobals.key);
+
+ if (key == VTHREADBASE_INVALID_KEY) {
+ VThreadBaseKeyType newKey;
+
+#if defined _WIN32
+ newKey = TlsAlloc();
+ ASSERT_NOT_IMPLEMENTED(newKey != VTHREADBASE_INVALID_KEY);
+#else
+ Bool success = pthread_key_create(&newKey, NULL) == 0;
+ ASSERT_NOT_IMPLEMENTED(success);
+#endif
+
+ if (Atomic_ReadIfEqualWrite(&vthreadBaseGlobals.key,
+ VTHREADBASE_INVALID_KEY,
+ newKey) != VTHREADBASE_INVALID_KEY) {
+ /* Race: someone else init'd */
+#if defined _WIN32
+ TlsFree(newKey);
+#else
+ pthread_key_delete(newKey);
+#endif
+ }
+
+ key = Atomic_Read(&vthreadBaseGlobals.key);
+ ASSERT(key != VTHREADBASE_INVALID_KEY);
+ }
+
+ return key;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBaseRaw --
+ *
+ * Get the per-thread data, but do not assign data if not present.
+ *
+ * Inlined; hitting TLS needs to be a fastpath.
+ *
+ * Results:
+ * A VThreadBaseData *, or NULL if thread has not been initialized.
+ *
+ * Side effects:
+ * Does NOT assign data to the TLS slot.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE VThreadBaseData *
+VThreadBaseRaw(void)
+{
+ VThreadBaseKeyType key = Atomic_Read(&vthreadBaseGlobals.key);
+
+ if (UNLIKELY(key == VTHREADBASE_INVALID_KEY)) {
+ key = VThreadBaseGetKey(); /* Non-inlined slow path */
+ }
+
+#if defined _WIN32
+ return (VThreadBaseData *) TlsGetValue(key);
+#else
+ return (VThreadBaseData *) pthread_getspecific(key);
+#endif
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBaseCooked --
+ *
+ * Get the per-thread data, and assign if there is no data.
+ *
+ * Results:
+ * A VThreadBaseData *. Will succeed or ASSERT.
+ *
+ * Side effects:
+ * Can assign a dynamic VThreadID to the current thread.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static VThreadBaseData *
+VThreadBaseCooked(void)
+{
+ VThreadBaseData *base = VThreadBaseRaw();
+
+ ASSERT(vthreadBaseGlobals.noIDFunc);
+
+ if (UNLIKELY(base == NULL)) {
+ /*
+ * The code between the last pthread_getspecific and the eventual
+ * call to pthread_setspecific either needs to run with async signals
+ * blocked or tolerate reentrancy. Simpler to just block the signals.
+ * See bugs 295686 & 477318. Here, the problem is that we could allocate
+ * two VThreadIDs (via a signal during the NoID callback).
+ */
+ NO_ASYNC_SIGNALS_START;
+ if (VThreadBaseRaw() == NULL) {
+ (*vthreadBaseGlobals.noIDFunc)();
+ }
+ NO_ASYNC_SIGNALS_END;
+
+ base = VThreadBaseRaw();
+ ASSERT(base);
+ }
+
+ return base;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBase_CurID --
+ *
+ * Get the current thread ID.
+ *
+ * Results:
+ * A VThreadID. Always succeeds.
+ *
+ * Side effects:
+ * May assign a dynamic VThreadID if this thread is not known.
+ *
+ * Threads desiring static VThreadIDs should call VThreadBase_InitSelf()
+ * before allowing VThread_CurID() to be called to avoid allocating
+ * a dynamic VThreadID.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+VThreadID
+VThreadBase_CurID(void)
+{
+ return VThreadBaseCooked()->id;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBase_CurName --
+ *
+ * Get the current thread name.
+ *
+ * This function always returns, at some level of reentrancy. That is,
+ * the first call either returns successfully or Panics; the Panic may
+ * reentrantly call this function, and that reentrant call always
+ * returns.
+ *
+ * Results:
+ * The current thread name.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+const char *
+VThreadBase_CurName(void)
+{
+ static Atomic_Int curNameRecursion;
+ VThreadBaseData *base = VThreadBaseRaw();
+
+ if (LIKELY(base != NULL)) {
+ return base->name;
+ } else if (Atomic_Read(&curNameRecursion) == 0) {
+ /* Unnamed thread, try to name it. */
+ Atomic_Inc(&curNameRecursion);
+ base = VThreadBaseCooked(); /* Assigns name as side effect */
+ Atomic_Dec(&curNameRecursion);
+
+ return base->name;
+ } else {
+ /*
+ * Unnamed thread, but naming it failed (recursed back to here).
+ * The heuristic is not perfect (a second unnamed thread could
+ * be looking for a name while the first thread names itself),
+ * but getting here nonrecursively is unlikely and we cannot
+ * do better about detection without thread-local storage, and
+ * in the recursive case thread-local storage won't exist after
+ * a failure to set up thread-local storage in the first place.
+ *
+ * This clause function should not ASSERT, Panic or call a Str_
+ * function (that can ASSERT or Panic), as the Panic handler is
+ * very likey to query the thread name and end up right back here.
+ * Thus, use a static buffer for a partly-sane name and hope the
+ * Panic handler dumps enough information to figure out what went
+ * wrong.
+ */
+
+ static char name[48];
+ uintptr_t hostTid;
+
+#if defined(_WIN32)
+ hostTid = GetCurrentThreadId();
+#elif defined(__linux__)
+ hostTid = pthread_self();
+#elif defined(__APPLE__)
+ hostTid = (uintptr_t)(void*)pthread_self();
+#else
+ hostTid = 0;
+#endif
+ snprintf(name, sizeof name - 1 /* keep buffer NUL-terminated */,
+ "host-%"FMTPD"u", hostTid);
+
+ return name;
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBase_InitWithTLS --
+ *
+ * (Atomic) VThreadBase initialization, using caller-managed memory.
+ * Gets the VThreadID and thread name from within that caller-managed
+ * memory, so both must be populated.
+ *
+ * Always "succeeds" in initialization; the return value is useful
+ * as an indication of whether this was the first initialization.
+ *
+ * Note: will NEVER overwrite an existing TLS allocation. May return a
+ * different VThreadID than requested; this is logged and considered a bug,
+ * code that cares about the assigned VThreadID should be using lib/thread
+ * to manage threads, not native OS primitives. (However, there is code
+ * like that today, so we cannot ASSERT.)
+ *
+ * Results:
+ * TRUE if this is the first initialization (e.g. TLS uses supplied
+ * pointer), FALSE otherwise.
+ *
+ * Side effects:
+ * Sets TLS if this is the first initialization.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+VThreadBase_InitWithTLS(VThreadBaseData *base) // IN: caller-managed storage
+{
+ Bool firstTime, success;
+ /* Require key allocation before TLS read */
+ VThreadBaseKeyType key = VThreadBaseGetKey();
+
+ ASSERT(base != NULL && base->id != VTHREAD_INVALID_ID);
+
+ NO_ASYNC_SIGNALS_START;
+ if (VThreadBaseRaw() == NULL) {
+#if defined _WIN32
+ /*
+ * Windows potentially has the async-signal problem mentioned
+ * below due to APCs, but in practice it will not happen:
+ * APCs only get serviced at a few points (Sleep or WaitFor calls),
+ * and we obviously do not make those between TlsGetValue and
+ * TlsSetValue.
+ */
+ success = TlsSetValue(key, base);
+#else
+ /*
+ * The code between the check of pthread_getspecific and the eventually
+ * call to pthread_setspecific MUST run with async signals blocked, see
+ * bugs 295686 & 477318. Here, the problem is that we could
+ * accidentally set the TLS slot twice (it's not atomic).
+ */
+ success = pthread_setspecific(key, base) == 0;
+#endif
+ firstTime = TRUE;
+ } else {
+ success = TRUE;
+ firstTime = FALSE;
+ }
+ NO_ASYNC_SIGNALS_END;
+ /* Try not to ASSERT while signals are blocked */
+ ASSERT_NOT_IMPLEMENTED(success);
+ ASSERT(!firstTime || (base == VThreadBaseRaw()));
+
+ if (firstTime) {
+ Atomic_Inc(&vthreadBaseGlobals.numThreads);
+ } else {
+ VThreadBaseData *realBase = VThreadBaseRaw();
+
+ /*
+ * If this happens, it means:
+ * 1) A thread was created w/o lib/thread but caller tried to initialize
+ * it with a specific VThreadID. This shouldn't happen: callers
+ * should either use lib/thread or not try to initialize VThreadIDs.
+ * Or,
+ * 2) An asynchronous signal interrupted the process of assigning
+ * a VThreadID and resulted in multiple allocation. This either
+ * means the cooking function is broken - it should block signals - or
+ * an ASSERT triggered while setting up the VThreadID.
+ */
+ Log("VThreadBase reinitialization, old: %d %s, new: %d %s.\n",
+ realBase->id, realBase->name, base->id, base->name);
+ }
+
+ return firstTime;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBase_InitThread --
+ *
+ * (Atomic) VThreadID allocation.
+ *
+ * If the VThreadID of a thread does not matter (e.g. dynamic allocation),
+ * use VThread_SetName() instead, which arrives here with a default name.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Stores VThreadID/name in TLS if successful.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+VThreadID
+VThreadBase_InitThread(VThreadID newID, // IN: new VThreadID
+ const char *name) // IN: new name
+{
+ VThreadBaseData *base;
+ /* Require key allocation before TLS read */
+ VThreadBaseKeyType key = VThreadBaseGetKey();
+
+ ASSERT(name != NULL);
+
+ if (newID == VTHREAD_INVALID_ID) {
+ /* Client doesn't care about ID. Be compatible: just set name */
+ VThreadBase_SetName(name);
+
+ return VThreadBase_CurID();
+ }
+
+ if ((base = VThreadBaseRaw()) == NULL) {
+ VThreadBaseData tmpBase = { 0 };
+
+ /*
+ * Be careful calling calloc() here. Our AllocTrack implementation
+ * uses locks, which query VThreadIDs, and thus can cause this code
+ * to become reentrant (and thus pick up the wrong VThreadID). This
+ * is actually OK for fatal errors because the VThreadID does not
+ * matter, but is a problem on success.
+ *
+ * As a solution, cheat and put a stack-allocated structure into TLS,
+ * then allocate memory, then put the real structure into TLS.
+ * This depends on not crashing during that interval (a crash
+ * could invoke the TLS destructor and free a stack address). A flag
+ * within the field is not sufficient: our code could have been
+ * dlclose()ed so there is no resident destructor to read it. It is
+ * also convienient to allow lib/thread to supply memory that it
+ * manages independently, to avoid two copies of some state.
+ *
+ * So in the end, just don't use a TLS destructor. If threads
+ * exit outside of our control, leak a little memory (~40 bytes).
+ * Since starting/stopping lots of threads is very unusual, it
+ * is best to not add complexity to tolerate that workload.
+ */
+
+ tmpBase.id = newID;
+ Str_Strcpy(tmpBase.name, name, sizeof tmpBase.name);
+
+ if (VThreadBase_InitWithTLS(&tmpBase)) {
+ VThreadBaseData *newBase = Util_SafeCalloc(1, sizeof *newBase);
+ Bool success;
+
+ *newBase = tmpBase;
+#if defined _WIN32
+ success = TlsSetValue(key, newBase);
+#else
+ success = pthread_setspecific(key, newBase) == 0;
+#endif
+ ASSERT_NOT_IMPLEMENTED(success);
+ }
+
+ base = VThreadBaseRaw();
+ ASSERT(base);
+ }
+
+ return base->id;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBase_ForgetSelf --
+ *
+ * Forget the TLS parts of a thread.
+ *
+ * If not intending to reallocate TLS, avoid querying the thread's
+ * VThread_CurID or VThread_CurName between this call and thread
+ * destruction.
+ *
+ * BUG: right now, the memory for the data block is unavoidably
+ * leaked (see comment in VThreadBase_InitThread for details) unless
+ * the caller manages memory (which lib/thread does).
+ * As creation/destruction of many threads is not common, this is
+ * tolerable.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+VThreadBaseData *
+VThreadBase_ForgetSelf(void)
+{
+ VThreadBaseKeyType key = VThreadBaseGetKey();
+ VThreadBaseData *data = VThreadBaseRaw();
+ Bool success;
+
+#if defined _WIN32
+ success = TlsSetValue(key, NULL);
+#else
+ success = pthread_setspecific(key, NULL) == 0;
+#endif
+ ASSERT_NOT_IMPLEMENTED(success);
+ if (data != NULL) {
+ Atomic_Dec(&vthreadBaseGlobals.numThreads);
+ }
+ return data;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBase_SetName --
+ *
+ * Override the default thread name with a new name.
+ *
+ * Historical: this subsumes the behavior of the old
+ * lib/nothread VThread_Init, replacing it with something that is
+ * optional.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If current thread does not have a TLS block, one is allocated.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+VThreadBase_SetName(const char *name) // IN: new name
+{
+ VThreadBaseData *base = VThreadBaseCooked();
+
+ ASSERT(name);
+ Str_Strcpy(base->name, name, sizeof base->name);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBaseGetNative --
+ *
+ * Gets a native representation of the thread ID, which can be stored
+ * in a pointer.
+ *
+ * Results:
+ * Native representation of a thread ID.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static INLINE void *
+VThreadBaseGetNative(void)
+{
+ /* All thread types must fit into a uintptr_t so we can set them atomically. */
+#ifdef _WIN32
+ /*
+ * On Windows, use a ThreadId instead of the thread handle, to avoid
+ * holding a reference that is hard to clean up.
+ */
+ ASSERT_ON_COMPILE(sizeof (DWORD) <= sizeof (void*));
+ return (void *)(uintptr_t)GetCurrentThreadId();
+#else
+ ASSERT_ON_COMPILE(sizeof (pthread_t) <= sizeof (void*));
+ return (void *)(uintptr_t)pthread_self();
+#endif
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBaseNativeIsAlive --
+ *
+ * Determine if the thread described by the native thread ID is alive.
+ *
+ * The algorithm is not perfect - native thread IDs can be reused
+ * by the host OS. But in such cases we will simply fail to reclaim
+ * VThreadIDs, and such cases are rare.
+ *
+ * Results:
+ * TRUE if alive, may have (rare) false positives.
+ * FALSE if definitely dead.
+ *
+ * Side effects:
+ * Makes OS calls to determine thread liveliness.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+VThreadBaseNativeIsAlive(void *native)
+{
+#ifdef _WIN32
+ HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE,
+ (DWORD)(uintptr_t)native);
+
+ if (hThread != NULL) {
+ DWORD exitCode;
+ BOOL success;
+
+ success = GetExitCodeThread(hThread, &exitCode);
+ ASSERT(success); /* No known ways GetExitCodeThread can fail */
+ CloseHandle(hThread);
+ return exitCode == STILL_ACTIVE;
+ } else {
+ return FALSE;
+ }
+#else
+ return pthread_kill((pthread_t)(uintptr_t)native, 0) != ESRCH;
+#endif
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBaseSimpleNoID --
+ *
+ * Default implementation of a function that is called whenever a thread
+ * is found that contains no VThreadID.
+ *
+ * This implementation recycles VThreadIDs of dead threads, trying to keep
+ * the VThreadID namespace compacted as much as possible.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * After running, this thread has a VThreadID with a default name.
+ * (Or Panics before returning.)
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+VThreadBaseSimpleNoID(void)
+{
+ char name[VTHREADBASE_MAX_NAME];
+ VThreadID newID;
+ HashTable *ht = HashTable_AllocOnce(&vthreadBaseGlobals.nativeHash,
+ 128, HASH_INT_KEY | HASH_FLAG_ATOMIC, NULL);
+
+ /*
+ * Before allocating a new ID, try to reap any old IDs.
+ */
+ for (newID = 0;
+ newID < Atomic_Read(&vthreadBaseGlobals.dynamicID);
+ newID++) {
+ const void *newKey = (const void *)(uintptr_t)newID;
+ void *oldNative;
+ Bool found = HashTable_Lookup(ht, newKey, &oldNative);
+
+ /*
+ * Entry may not be found due to races if multiple threads are created
+ * at the same time. However, any entry that is found represents
+ * a live or reclaimable VThreadID.
+ */
+ if (found && !VThreadBaseNativeIsAlive(oldNative) &&
+ HashTable_ReplaceIfEqual(ht, newKey, oldNative,
+ VThreadBaseGetNative())) {
+ /*
+ * We reclaimed a stale VThreadID. It is already updated with
+ * the current thread's native ID. Now populate TLS.
+ */
+ Atomic_Dec(&vthreadBaseGlobals.numThreads);
+ Str_Sprintf(name, sizeof name, "vthread-%u", newID);
+ VThreadBase_InitThread(newID, name);
+ if (vmx86_debug) {
+ Log("VThreadBase reused VThreadID %d.\n", newID);
+ }
+ return;
+ }
+ }
+
+ newID = Atomic_FetchAndInc(&vthreadBaseGlobals.dynamicID);
+ {
+ Bool result;
+ const void *newKey = (const void *)(uintptr_t)newID;
+
+ /*
+ * Detect VThreadID overflow (~0 is used as a sentinal).
+ * Leave a space of ~10 IDs, since the increment and bounds-check
+ * are not atomic.
+ */
+ ASSERT_NOT_IMPLEMENTED(newID < VTHREAD_INVALID_ID - 10);
+
+ Str_Sprintf(name, sizeof name, "vthread-%u", newID);
+ VThreadBase_InitThread(newID, name);
+
+ result = HashTable_Insert(ht, newKey, VThreadBaseGetNative());
+ ASSERT(result);
+ }
+
+ if (Atomic_Read(&vthreadBaseGlobals.numThreads) > 1) {
+ /*
+ * We actually know that there are many binaries that used to link
+ * lib/nothread but are multithreaded anyway. This log message and
+ * (presently disabled) ASSERT are intended to flush out such usages.
+ */
+ LOG_ONCE(("VThreadBase detected multiple threads.\n"));
+ //ASSERT(FALSE);
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBase_SetNoIDFunc --
+ *
+ * Sets the hook function to be called when a thread is found with
+ * no VThreadID. The hook function is expected to call
+ * VThreadBase_InitThread() with a valid new ID.
+ *
+ * On Posix, this callback is called with signals masked to prevent
+ * accidental double-allocation of IDs. On Windows, the constraint is
+ * that the callback cannot service an APC between allocating an ID and
+ * initializing the thread with that ID, as such is likely
+ * to accidentally double-allocate IDs.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+VThreadBase_SetNoIDFunc(void (*func)(void)) // IN: new hook function
+{
+ ASSERT(func);
+
+ /*
+ * The hook function can only be set once, and must be set before
+ * any VThreadIDs are allocated so that the hook can control the VThreadID
+ * namespace.
+ *
+ * If the process has had only a single thread, that thread can be forgotten
+ * via VThreadBase_ForgetSelf() and this function safely called.
+ */
+ ASSERT(vthreadBaseGlobals.noIDFunc == VThreadBaseSimpleNoID &&
+ Atomic_Read(&vthreadBaseGlobals.numThreads) == 0);
+
+ vthreadBaseGlobals.noIDFunc = func;
+}
+
+
+#if !defined _WIN32
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBase_IsInSignal --
+ *
+ * Accessor for whether current thread is or is not in a signal.
+ * lib/sig handles keeping this accurate.
+ *
+ * Results:
+ * Returns TRUE if a signal handler is somewhere on the stack.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+VThreadBase_IsInSignal(void)
+{
+ VThreadBaseData *base = VThreadBaseCooked();
+
+ return Atomic_Read(&base->signalNestCount) > 0;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBase_SetIsInSignal --
+ *
+ * Marks the current thread as or as not being inside a signal
+ * handler.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * As above.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+VThreadBase_SetIsInSignal(VThreadID tid, // IN:
+ Bool isInSignal) // IN:
+{
+ VThreadBaseData *base = VThreadBaseCooked();
+
+ /* It is an error to clear isInSignal while not in a signal. */
+ ASSERT(Atomic_Read(&base->signalNestCount) > 0 || isInSignal);
+
+ Atomic_Add(&base->signalNestCount, isInSignal ? 1 : -1);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VThreadBase_SigMask --
+ *
+ * Wrapper for pthread_sigmask that uses VThreadBase's no-dependency
+ * magic to get the effects of pthread_sigmask without actually
+ * depending on lib/pthread.
+ *
+ * Results:
+ * See 'man pthread_setmask'.
+ *
+ * Side effects:
+ * See 'man pthread_setmask'.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+VThreadBase_SigMask(int how, // IN:
+ const sigset_t *newmask, // IN:
+ sigset_t *oldmask) // IN:
+{
+ return pthread_sigmask(how, newmask, oldmask);
+}
+#endif