public void AcquireWriterLock(int millisecondsTimeout)
{
if (millisecondsTimeout < -1)
{
throw GetInvalidTimeoutException(nameof(millisecondsTimeout));
}
int threadID = GetCurrentThreadID();
// Check for the fast path
if (Interlocked.CompareExchange(ref _state, LockStates.Writer, 0) == 0)
{
Debug.Assert((_state & LockStates.ReadersMask) == 0);
}
// Check if the thread already has writer lock
else if (_writerID == threadID)
{
if (_writerLevel == MaxAcquireCount)
{
throw new OverflowException(SR.Overflow_UInt16);
}
++_writerLevel;
return;
}
else
{
int spinCount = 0;
int currentState = _state;
do
{
int knownState = currentState;
// Writer need not wait if there are no readers and writer
if (knownState == 0 || knownState == LockStates.CachingEvents)
{
// Can be a writer
currentState = Interlocked.CompareExchange(ref _state, knownState + LockStates.Writer, knownState);
if (currentState == knownState)
{
// Only writer
break;
}
continue;
}
// Check for too many waiting writers
if ((knownState & LockStates.WaitingWritersMask) == LockStates.WaitingWritersMask)
{
Helpers.Sleep(1000);
spinCount = 0;
currentState = _state;
continue;
}
++spinCount;
// Check if events are being cached. The purpose of this check is that "caching" events could involve
// disposing one or both of {_readerEvent, _writerEvent}. This check prevents the waiting code below from
// trying to use these events during this dangerous time, and instead causes the loop to spin until the
// caching state is cleared and events can be recreated. See ReleaseEvents() and callers.
if ((knownState & LockStates.CachingEvents) == LockStates.CachingEvents)
{
if (spinCount > DefaultSpinCount)
{
Helpers.Sleep(1);
spinCount = 0;
}
currentState = _state;
continue;
}
// Check spin count
if (spinCount <= DefaultSpinCount)
{
currentState = _state;
continue;
}
// Add to waiting writers
currentState = Interlocked.CompareExchange(ref _state, knownState + LockStates.WaitingWriter, knownState);
if (currentState != knownState)
{
continue;
}
int modifyState = -LockStates.WaitingWriter;
AutoResetEvent writerEvent = null;
bool waitSucceeded = false;
try
{
writerEvent = GetOrCreateWriterEvent();
waitSucceeded = writerEvent.WaitOne(millisecondsTimeout);
if (waitSucceeded)
{
// Become a writer and remove the writer-signaled state
Debug.Assert((_state & LockStates.WriterSignaled) != 0);
modifyState += LockStates.Writer - LockStates.WriterSignaled;
}
}
finally
{
// Make the state changes determined above
knownState = Interlocked.Add(ref _state, modifyState) - modifyState;
if (!waitSucceeded &&
(knownState & LockStates.WriterSignaled) != 0 &&
(knownState & LockStates.WaitingWritersMask) == LockStates.WaitingWriter)
{
if (writerEvent == null)
{
writerEvent = _writerEvent;
Debug.Assert(writerEvent != null);
}
while (true)
{
knownState = _state;
if ((knownState & LockStates.WriterSignaled) == 0 ||
(knownState & LockStates.WaitingWritersMask) != 0)
{
break;
}
if (!writerEvent.WaitOne(10))
{
continue;
}
modifyState = LockStates.Writer - LockStates.WriterSignaled;
knownState = Interlocked.Add(ref _state, modifyState) - modifyState;
Debug.Assert((knownState & LockStates.WriterSignaled) != 0);
Debug.Assert((knownState & LockStates.Writer) == 0);
// Honor the orginal status
_writerID = threadID;
Debug.Assert(_writerLevel == 0);
_writerLevel = 1;
ReleaseWriterLock();
break;
}
}
}
if (!waitSucceeded)
{
throw GetTimeoutException();
}
break;
} while (YieldProcessor());
}
// Success
Debug.Assert((_state & LockStates.Writer) != 0);
Debug.Assert((_state & LockStates.ReadersMask) == 0);
Debug.Assert(_writerID == InvalidThreadID);
// Save threadid of the writer
_writerID = threadID;
_writerLevel = 1;
++_writerSeqNum;
return;
}