]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
lib/lock: make the userland portion available for tools
authorVMware, Inc <>
Thu, 17 Jun 2010 21:05:13 +0000 (14:05 -0700)
committerMarcelo Vanzin <mvanzin@vmware.com>
Thu, 17 Jun 2010 21:05:13 +0000 (14:05 -0700)
We're using MXUser (userland) locks all over bora/lib. Some of these
libraries are showing up in the open source tools build. Make only
the MXUser portion of lib/lock available to the tools. We're OK with
open sourcing this.

Once this change goes in bora/lib changes that depend on it will
start showing up almost immediately.

I've elected to a a minimumally invasive approach to ulRec.c, one
that optimized out the conditional for acquisition and release
(by far the largest usage) and leaving the farless used TryLock
and IsLocked routines using the same conditional as before this
change.

Signed-off-by: Marcelo Vanzin <mvanzin@vmware.com>
13 files changed:
open-vm-tools/configure.ac
open-vm-tools/lib/Makefile.am
open-vm-tools/lib/include/userlock.h [new file with mode: 0644]
open-vm-tools/lib/include/vthreadBase.h [new file with mode: 0644]
open-vm-tools/lib/lock/Makefile.am [new file with mode: 0644]
open-vm-tools/lib/lock/ul.c [new file with mode: 0644]
open-vm-tools/lib/lock/ulCondVar.c [new file with mode: 0644]
open-vm-tools/lib/lock/ulExcl.c [new file with mode: 0644]
open-vm-tools/lib/lock/ulInt.h [new file with mode: 0644]
open-vm-tools/lib/lock/ulIntShared.h [new file with mode: 0644]
open-vm-tools/lib/lock/ulRW.c [new file with mode: 0644]
open-vm-tools/lib/lock/ulRec.c [new file with mode: 0644]
open-vm-tools/lib/misc/vthreadBase.c [new file with mode: 0644]

index 13422b689706807ddba2c67a6b13c965bcec77cd..9b1baaf7319ddf8b80dd16cea2fd3648d4d0d474 100644 (file)
@@ -1230,6 +1230,7 @@ AC_CONFIG_FILES([                      \
    lib/hgfsServerPolicyGuest/Makefile  \
    lib/image/Makefile                  \
    lib/impersonate/Makefile            \
+   lib/lock/Makefile                   \
    lib/message/Makefile                \
    lib/misc/Makefile                   \
    lib/netUtil/Makefile                \
index 61a051eb21f58393921d86cc4527ced3cd3530f1..0cf87d7e13f119174c256989cdf647e41c3121f2 100644 (file)
@@ -50,6 +50,7 @@ if ENABLE_UNITY
    SUBDIRS += image
 endif
 SUBDIRS += impersonate
+SUBDIRS += lock
 SUBDIRS += message
 SUBDIRS += misc
 SUBDIRS += netUtil
diff --git a/open-vm-tools/lib/include/userlock.h b/open-vm-tools/lib/include/userlock.h
new file mode 100644 (file)
index 0000000..c0bdc92
--- /dev/null
@@ -0,0 +1,167 @@
+/*********************************************************
+ * 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_
diff --git a/open-vm-tools/lib/include/vthreadBase.h b/open-vm-tools/lib/include/vthreadBase.h
new file mode 100644 (file)
index 0000000..bf62032
--- /dev/null
@@ -0,0 +1,156 @@
+/*********************************************************
+ * 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
diff --git a/open-vm-tools/lib/lock/Makefile.am b/open-vm-tools/lib/lock/Makefile.am
new file mode 100644 (file)
index 0000000..7085f38
--- /dev/null
@@ -0,0 +1,27 @@
+################################################################################
+### 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@
diff --git a/open-vm-tools/lib/lock/ul.c b/open-vm-tools/lib/lock/ul.c
new file mode 100644 (file)
index 0000000..2e76647
--- /dev/null
@@ -0,0 +1,610 @@
+/*********************************************************
+ * 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
+}
diff --git a/open-vm-tools/lib/lock/ulCondVar.c b/open-vm-tools/lib/lock/ulCondVar.c
new file mode 100644 (file)
index 0000000..d1a2dfa
--- /dev/null
@@ -0,0 +1,428 @@
+/*********************************************************
+ * 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);
+   }
+}
diff --git a/open-vm-tools/lib/lock/ulExcl.c b/open-vm-tools/lib/lock/ulExcl.c
new file mode 100644 (file)
index 0000000..e142e0c
--- /dev/null
@@ -0,0 +1,621 @@
+/*********************************************************
+ * 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);
+}
diff --git a/open-vm-tools/lib/lock/ulInt.h b/open-vm-tools/lib/lock/ulInt.h
new file mode 100644 (file)
index 0000000..e4d4773
--- /dev/null
@@ -0,0 +1,562 @@
+/*********************************************************
+ * 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
diff --git a/open-vm-tools/lib/lock/ulIntShared.h b/open-vm-tools/lib/lock/ulIntShared.h
new file mode 100644 (file)
index 0000000..3303323
--- /dev/null
@@ -0,0 +1,27 @@
+/*********************************************************
+ * 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
diff --git a/open-vm-tools/lib/lock/ulRW.c b/open-vm-tools/lib/lock/ulRW.c
new file mode 100644 (file)
index 0000000..753a371
--- /dev/null
@@ -0,0 +1,837 @@
+/*********************************************************
+ * 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);
+}
diff --git a/open-vm-tools/lib/lock/ulRec.c b/open-vm-tools/lib/lock/ulRec.c
new file mode 100644 (file)
index 0000000..23efcc7
--- /dev/null
@@ -0,0 +1,815 @@
+/*********************************************************
+ * 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
diff --git a/open-vm-tools/lib/misc/vthreadBase.c b/open-vm-tools/lib/misc/vthreadBase.c
new file mode 100644 (file)
index 0000000..11e3ec8
--- /dev/null
@@ -0,0 +1,1161 @@
+/*********************************************************
+ * 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