public LockCookie UpgradeToWriterLock(int millisecondsTimeout)
{
if (millisecondsTimeout < -1)
{
throw GetInvalidTimeoutException(nameof(millisecondsTimeout));
}
var lockCookie = new LockCookie();
int threadID = GetCurrentThreadID();
lockCookie._threadID = threadID;
// Check if the thread is already a writer
if (_writerID == threadID)
{
// Update cookie state
lockCookie._flags = LockCookieFlags.Upgrade | LockCookieFlags.OwnedWriter;
lockCookie._writerLevel = _writerLevel;
// Acquire the writer lock again
AcquireWriterLock(millisecondsTimeout);
return lockCookie;
}
ThreadLocalLockEntry threadLocalLockEntry = ThreadLocalLockEntry.GetCurrent(_lockID);
if (threadLocalLockEntry == null)
{
lockCookie._flags = LockCookieFlags.Upgrade | LockCookieFlags.OwnedNone;
}
else
{
// Sanity check
Debug.Assert((_state & LockStates.ReadersMask) != 0);
Debug.Assert(threadLocalLockEntry._readerLevel > 0);
// Save lock state in the cookie
lockCookie._flags = LockCookieFlags.Upgrade | LockCookieFlags.OwnedReader;
lockCookie._readerLevel = threadLocalLockEntry._readerLevel;
// If there is only one reader, try to convert reader to a writer
int knownState = Interlocked.CompareExchange(ref _state, LockStates.Writer, LockStates.Reader);
if (knownState == LockStates.Reader)
{
// Thread is no longer a reader
threadLocalLockEntry._readerLevel = 0;
Debug.Assert(threadLocalLockEntry.IsFree);
// Thread is a writer
_writerID = threadID;
_writerLevel = 1;
++_writerSeqNum;
return lockCookie;
}
// Release the reader lock
threadLocalLockEntry._readerLevel = 1;
ReleaseReaderLock();
}
// We are aware of the contention on the lock and the thread will most probably block to acquire writer lock
bool acquired = false;
try
{
AcquireWriterLock(millisecondsTimeout);
acquired = true;
return lockCookie;
}
finally
{
if (!acquired)
{
// Invalidate cookie
LockCookieFlags flags = lockCookie._flags;
lockCookie._flags = LockCookieFlags.Invalid;
RecoverLock(ref lockCookie, flags & LockCookieFlags.OwnedReader);
}
}
}