summaryrefslogtreecommitdiff
path: root/src/utilcode/utsem.cpp
diff options
context:
space:
mode:
authordotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
committerdotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
commitef1e2ab328087c61a6878c1e84f4fc5d710aebce (patch)
treedee1bbb89e9d722e16b0d1485e3cdd1b6c8e2cfa /src/utilcode/utsem.cpp
downloadcoreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.gz
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.bz2
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.zip
Initial commit to populate CoreCLR repo
[tfs-changeset: 1407945]
Diffstat (limited to 'src/utilcode/utsem.cpp')
-rw-r--r--src/utilcode/utsem.cpp553
1 files changed, 553 insertions, 0 deletions
diff --git a/src/utilcode/utsem.cpp b/src/utilcode/utsem.cpp
new file mode 100644
index 0000000000..0d3125068d
--- /dev/null
+++ b/src/utilcode/utsem.cpp
@@ -0,0 +1,553 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+/******************************************************************************
+ FILE : UTSEM.CPP
+
+
+
+ Purpose: Part of the utilities library for the VIPER project
+
+ Abstract : Implements the UTSemReadWrite class.
+-------------------------------------------------------------------------------
+Revision History:
+
+
+*******************************************************************************/
+#include "stdafx.h"
+#include "clrhost.h"
+#include "ex.h"
+
+#include <utsem.h>
+#include "contract.h"
+
+// Consider replacing this with a #ifdef INTEROP_DEBUGGING
+#if !defined(SELF_NO_HOST) && defined(_TARGET_X86_)
+// For Interop debugging, the UTSemReadWrite class must inform the debugger
+// that this thread can't be suspended currently. See vm\util.hpp for the
+// implementation of these methods.
+void IncCantStopCount();
+void DecCantStopCount();
+#else
+#define IncCantStopCount()
+#define DecCantStopCount()
+#endif // !SELF_NO_HOST && _TARGET_X86_
+
+/******************************************************************************
+Definitions of the bit fields in UTSemReadWrite::m_dwFlag:
+
+Warning: The code assume that READER_MASK is in the low-order bits of the DWORD.
+******************************************************************************/
+
+const ULONG READERS_MASK = 0x000003FF; // field that counts number of readers
+const ULONG READERS_INCR = 0x00000001; // amount to add to increment number of readers
+
+// The following field is 2 bits long to make it easier to catch errors.
+// (If the number of writers ever exceeds 1, we've got problems.)
+const ULONG WRITERS_MASK = 0x00000C00; // field that counts number of writers
+const ULONG WRITERS_INCR = 0x00000400; // amount to add to increment number of writers
+
+const ULONG READWAITERS_MASK = 0x003FF000; // field that counts number of threads waiting to read
+const ULONG READWAITERS_INCR = 0x00001000; // amount to add to increment number of read waiters
+
+const ULONG WRITEWAITERS_MASK = 0xFFC00000; // field that counts number of threads waiting to write
+const ULONG WRITEWAITERS_INCR = 0x00400000; // amoun to add to increment number of write waiters
+
+// ======================================================================================
+// Spinning support
+
+// Copy of definition from file:..\VM\spinlock.h
+#define CALLER_LIMITS_SPINNING 0
+
+#if defined(SELF_NO_HOST) && !defined(CROSSGEN_COMPILE)
+
+// When we do not have host, we just call OS - see file:..\VM\hosting.cpp#__SwitchToThread
+BOOL __SwitchToThread(DWORD dwSleepMSec, DWORD dwSwitchCount)
+{
+ // This is just simple implementation that does not support full dwSwitchCount arg
+ _ASSERTE(dwSwitchCount == CALLER_LIMITS_SPINNING);
+ return SwitchToThread();
+}
+
+Volatile<BOOL> g_fInitializedGlobalSystemInfo = FALSE;
+
+// Global System Information
+SYSTEM_INFO g_SystemInfo;
+
+// Configurable constants used across our spin locks
+SpinConstants g_SpinConstants = {
+ 50, // dwInitialDuration
+ 40000, // dwMaximumDuration - ideally (20000 * max(2, numProc)) ... updated in code:InitializeSpinConstants_NoHost
+ 3, // dwBackoffFactor
+ 10 // dwRepetitions
+};
+
+inline void InitializeSpinConstants_NoHost()
+{
+ g_SpinConstants.dwMaximumDuration = max(2, g_SystemInfo.dwNumberOfProcessors) * 20000;
+}
+
+#else //!SELF_NO_HOST || CROSSGEN_COMPILE
+
+// Use VM/CrossGen functions and variables
+BOOL __SwitchToThread (DWORD dwSleepMSec, DWORD dwSwitchCount);
+extern SYSTEM_INFO g_SystemInfo;
+extern SpinConstants g_SpinConstants;
+
+#endif //!SELF_NO_HOST || CROSSGEN_COMPILE
+
+/******************************************************************************
+Function : UTSemReadWrite::UTSemReadWrite
+
+Abstract: Constructor.
+******************************************************************************/
+UTSemReadWrite::UTSemReadWrite()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#if defined(SELF_NO_HOST) && !defined(CROSSGEN_COMPILE)
+ if (!g_fInitializedGlobalSystemInfo)
+ {
+ GetSystemInfo(&g_SystemInfo);
+ InitializeSpinConstants_NoHost();
+
+ g_fInitializedGlobalSystemInfo = TRUE;
+ }
+#endif //SELF_NO_HOST && !CROSSGEN_COMPILE
+
+ m_dwFlag = 0;
+ m_pReadWaiterSemaphore = NULL;
+ m_pWriteWaiterEvent = NULL;
+}
+
+
+/******************************************************************************
+Function : UTSemReadWrite::~UTSemReadWrite
+
+Abstract: Destructor
+******************************************************************************/
+UTSemReadWrite::~UTSemReadWrite()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE_MSG((m_dwFlag == (ULONG)0), "Destroying a UTSemReadWrite while in use");
+
+ if (m_pReadWaiterSemaphore != NULL)
+ delete m_pReadWaiterSemaphore;
+
+ if (m_pWriteWaiterEvent != NULL)
+ delete m_pWriteWaiterEvent;
+}
+
+//=======================================================================================
+//
+// Initialize the lock (its semaphore and event)
+//
+HRESULT
+UTSemReadWrite::Init()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ _ASSERTE(m_pReadWaiterSemaphore == NULL);
+ _ASSERTE(m_pWriteWaiterEvent == NULL);
+
+ EX_TRY
+ {
+ CONTRACT_VIOLATION(ThrowsViolation);
+
+ m_pReadWaiterSemaphore = new Semaphore();
+ m_pReadWaiterSemaphore->Create(0, MAXLONG);
+
+ m_pWriteWaiterEvent = new Event();
+ m_pWriteWaiterEvent->CreateAutoEvent(FALSE);
+ }
+ EX_CATCH
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+ IfFailGo(hr);
+
+ErrExit:
+ return hr;
+} // UTSemReadWrite::Init
+
+/******************************************************************************
+Function : UTSemReadWrite::LockRead
+
+Abstract: Obtain a shared lock
+******************************************************************************/
+HRESULT UTSemReadWrite::LockRead()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ // Inform CLR that the debugger shouldn't suspend this thread while
+ // holding this lock.
+ IncCantStopCount();
+
+ // First do some spinning - copied from file:..\VM\crst.cpp#CrstBase::SpinEnter
+ for (DWORD iter = 0; iter < g_SpinConstants.dwRepetitions; iter++)
+ {
+ DWORD i = g_SpinConstants.dwInitialDuration;
+
+ do
+ {
+ DWORD dwFlag = m_dwFlag;
+
+ if (dwFlag < READERS_MASK)
+ { // There are just readers in the play, try to add one more
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + READERS_INCR, dwFlag))
+ {
+ goto ReadLockAcquired;
+ }
+ }
+
+ if (g_SystemInfo.dwNumberOfProcessors <= 1)
+ { // We do not need to spin on a single processor
+ break;
+ }
+
+ // Delay by approximately 2*i clock cycles (Pentium III).
+ // This is brittle code - future processors may of course execute this
+ // faster or slower, and future code generators may eliminate the loop altogether.
+ // The precise value of the delay is not critical, however, and I can't think
+ // of a better way that isn't machine-dependent.
+ int sum = 0;
+
+ for (int delayCount = i; --delayCount; )
+ {
+ sum += delayCount;
+ YieldProcessor(); // indicate to the processor that we are spining
+ }
+
+ if (sum == 0)
+ {
+ // never executed, just to fool the compiler into thinking sum is live here,
+ // so that it won't optimize away the loop.
+ static char dummy;
+ dummy++;
+ }
+ // exponential backoff: wait a factor longer in the next iteration
+ i *= g_SpinConstants.dwBackoffFactor;
+ } while (i < g_SpinConstants.dwMaximumDuration);
+
+ __SwitchToThread(0, CALLER_LIMITS_SPINNING);
+ }
+ // Stop spinning
+
+ // Start waiting
+ for (;;)
+ {
+ DWORD dwFlag = m_dwFlag;
+
+ if (dwFlag < READERS_MASK)
+ { // There are just readers in the play, try to add one more
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + READERS_INCR, dwFlag))
+ {
+ break;
+ }
+ }
+ else if ((dwFlag & READERS_MASK) == READERS_MASK)
+ { // The number of readers has reached the maximum (0x3ff), wait 1s
+ ClrSleepEx(1000, FALSE);
+ }
+ else if ((dwFlag & READWAITERS_MASK) == READWAITERS_MASK)
+ { // The number of readers waiting on semaphore has reached the maximum (0x3ff), wait 1s
+ ClrSleepEx(1000, FALSE);
+ }
+ else
+ { // Try to add waiting reader and then wait for signal
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + READWAITERS_INCR, dwFlag))
+ {
+ m_pReadWaiterSemaphore->Wait(INFINITE, FALSE);
+ break;
+ }
+ }
+ }
+
+ReadLockAcquired:
+ _ASSERTE ((m_dwFlag & READERS_MASK) != 0 && "reader count is zero after acquiring read lock");
+ _ASSERTE ((m_dwFlag & WRITERS_MASK) == 0 && "writer count is nonzero after acquiring write lock");
+ EE_LOCK_TAKEN(this);
+
+ return S_OK;
+} // UTSemReadWrite::LockRead
+
+
+
+/******************************************************************************
+Function : UTSemReadWrite::LockWrite
+
+Abstract: Obtain an exclusive lock
+******************************************************************************/
+HRESULT UTSemReadWrite::LockWrite()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ // Inform CLR that the debugger shouldn't suspend this thread while
+ // holding this lock.
+ IncCantStopCount();
+
+ // First do some spinning - copied from file:..\VM\crst.cpp#CrstBase::SpinEnter
+ for (DWORD iter = 0; iter < g_SpinConstants.dwRepetitions; iter++)
+ {
+ DWORD i = g_SpinConstants.dwInitialDuration;
+
+ do
+ {
+ DWORD dwFlag = m_dwFlag;
+
+ if (dwFlag == 0)
+ { // No readers/writers in play, try to add a writer
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, WRITERS_INCR, dwFlag))
+ {
+ goto WriteLockAcquired;
+ }
+ }
+
+ if (g_SystemInfo.dwNumberOfProcessors <= 1)
+ { // We do not need to spin on a single processor
+ break;
+ }
+
+ // Delay by approximately 2*i clock cycles (Pentium III).
+ // This is brittle code - future processors may of course execute this
+ // faster or slower, and future code generators may eliminate the loop altogether.
+ // The precise value of the delay is not critical, however, and I can't think
+ // of a better way that isn't machine-dependent.
+ int sum = 0;
+
+ for (int delayCount = i; --delayCount; )
+ {
+ sum += delayCount;
+ YieldProcessor(); // indicate to the processor that we are spining
+ }
+
+ if (sum == 0)
+ {
+ // never executed, just to fool the compiler into thinking sum is live here,
+ // so that it won't optimize away the loop.
+ static char dummy;
+ dummy++;
+ }
+ // exponential backoff: wait a factor longer in the next iteration
+ i *= g_SpinConstants.dwBackoffFactor;
+ } while (i < g_SpinConstants.dwMaximumDuration);
+
+ __SwitchToThread(0, CALLER_LIMITS_SPINNING);
+ }
+ // Stop spinning
+
+ // Start waiting
+ for (;;)
+ {
+ DWORD dwFlag = m_dwFlag;
+
+ if (dwFlag == 0)
+ { // No readers/writers in play, try to add a writer
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, WRITERS_INCR, dwFlag))
+ {
+ break;
+ }
+ }
+ else if ((dwFlag & WRITEWAITERS_MASK) == WRITEWAITERS_MASK)
+ { // The number of writers waiting on semaphore has reached the maximum (0x3ff), wait 1s
+ ClrSleepEx(1000, FALSE);
+ }
+ else
+ { // Try to add waiting writer and then wait for signal
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + WRITEWAITERS_INCR, dwFlag))
+ {
+ m_pWriteWaiterEvent->Wait(INFINITE, FALSE);
+ break;
+ }
+ }
+
+ }
+
+WriteLockAcquired:
+ _ASSERTE ((m_dwFlag & READERS_MASK) == 0 && "reader count is nonzero after acquiring write lock");
+ _ASSERTE ((m_dwFlag & WRITERS_MASK) == WRITERS_INCR && "writer count is not 1 after acquiring write lock");
+ EE_LOCK_TAKEN(this);
+
+ return S_OK;
+} // UTSemReadWrite::LockWrite
+
+
+
+/******************************************************************************
+Function : UTSemReadWrite::UnlockRead
+
+Abstract: Release a shared lock
+******************************************************************************/
+void UTSemReadWrite::UnlockRead()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ ULONG dwFlag;
+
+
+ _ASSERTE ((m_dwFlag & READERS_MASK) != 0 && "reader count is zero before releasing read lock");
+ _ASSERTE ((m_dwFlag & WRITERS_MASK) == 0 && "writer count is nonzero before releasing read lock");
+
+ for (;;)
+ {
+ dwFlag = m_dwFlag;
+
+ if (dwFlag == READERS_INCR)
+ { // we're the last reader, and nobody is waiting
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, (ULONG)0, dwFlag))
+ {
+ break;
+ }
+ }
+
+ else if ((dwFlag & READERS_MASK) > READERS_INCR)
+ { // we're not the last reader
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag - READERS_INCR, dwFlag))
+ {
+ break;
+ }
+ }
+
+ else
+ {
+ // here, there should be exactly 1 reader (us), and at least one waiting writer.
+ _ASSERTE ((dwFlag & READERS_MASK) == READERS_INCR && "UnlockRead consistency error 1");
+ _ASSERTE ((dwFlag & WRITEWAITERS_MASK) != 0 && "UnlockRead consistency error 2");
+
+ // one or more writers is waiting, do one of them next
+ // (remove a reader (us), remove a write waiter, add a writer
+ if (dwFlag ==
+ InterlockedCompareExchangeT(
+ &m_dwFlag,
+ dwFlag - READERS_INCR - WRITEWAITERS_INCR + WRITERS_INCR,
+ dwFlag))
+ {
+ m_pWriteWaiterEvent->Set();
+ break;
+ }
+ }
+ }
+
+ DecCantStopCount();
+ EE_LOCK_RELEASED(this);
+} // UTSemReadWrite::UnlockRead
+
+
+/******************************************************************************
+Function : UTSemReadWrite::UnlockWrite
+
+Abstract: Release an exclusive lock
+******************************************************************************/
+void UTSemReadWrite::UnlockWrite()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ ULONG dwFlag;
+ ULONG count;
+
+ _ASSERTE ((m_dwFlag & READERS_MASK) == 0 && "reader count is nonzero before releasing write lock");
+ _ASSERTE ((m_dwFlag & WRITERS_MASK) == WRITERS_INCR && "writer count is not 1 before releasing write lock");
+
+ for (;;)
+ {
+ dwFlag = m_dwFlag;
+
+ if (dwFlag == WRITERS_INCR)
+ { // nobody is waiting
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, (ULONG)0, dwFlag))
+ {
+ break;
+ }
+ }
+
+ else if ((dwFlag & READWAITERS_MASK) != 0)
+ { // one or more readers are waiting, do them all next
+ count = (dwFlag & READWAITERS_MASK) / READWAITERS_INCR;
+ // remove a writer (us), remove all read waiters, turn them into readers
+ if (dwFlag ==
+ InterlockedCompareExchangeT(
+ &m_dwFlag,
+ dwFlag - WRITERS_INCR - count * READWAITERS_INCR + count * READERS_INCR,
+ dwFlag))
+ {
+ m_pReadWaiterSemaphore->Release(count, NULL);
+ break;
+ }
+ }
+
+ else
+ { // one or more writers is waiting, do one of them next
+ _ASSERTE ((dwFlag & WRITEWAITERS_MASK) != 0 && "UnlockWrite consistency error");
+ // (remove a writer (us), remove a write waiter, add a writer
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag - WRITEWAITERS_INCR, dwFlag))
+ {
+ m_pWriteWaiterEvent->Set();
+ break;
+ }
+ }
+ }
+
+ DecCantStopCount();
+ EE_LOCK_RELEASED(this);
+} // UTSemReadWrite::UnlockWrite
+
+#ifdef _DEBUG
+
+//=======================================================================================
+BOOL
+UTSemReadWrite::Debug_IsLockedForRead()
+{
+ return ((m_dwFlag & READERS_MASK) != 0);
+}
+
+//=======================================================================================
+BOOL
+UTSemReadWrite::Debug_IsLockedForWrite()
+{
+ return ((m_dwFlag & WRITERS_MASK) != 0);
+}
+
+#endif //_DEBUG
+