// ReaderWriterLockSlimmer.cs // // Copyright (c) 2010 Jérémie "Garuma" Laval // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // // using System; using System.Threading; namespace Mono.Threading { public struct ReaderWriterLockSlimmer { const int RwWait = 1; const int RwWrite = 2; const int RwRead = 4; int rwlock; public void EnterReadLock (ref bool taken) { if (taken) throw new ArgumentException ("taken", "taken needs to be set to false"); SpinWait sw = new SpinWait (); bool cont = true; do { while ((rwlock & (RwWrite | RwWait)) > 0) sw.SpinOnce (); try {} finally { if ((Interlocked.Add (ref rwlock, RwRead) & (RwWait | RwWait)) == 0) { taken = true; cont = false; } else { Interlocked.Add (ref rwlock, -RwRead); } } } while (cont); } public void TryEnterReadLock (ref bool taken) { if (taken) throw new ArgumentException ("taken", "taken needs to be set to false"); try {} finally { if ((Interlocked.Add (ref rwlock, RwRead) & (RwWait | RwWrite)) == 0) taken = true; else Interlocked.Add (ref rwlock, -RwRead); } } public void ExitReadLock () { Interlocked.Add (ref rwlock, -RwRead); } public void EnterWriteLock (ref bool taken) { if (taken) throw new ArgumentException ("taken", "taken needs to be set to false"); SpinWait sw = new SpinWait (); int state = rwlock; try { do { state = rwlock; if (state < RwWrite) { try {} finally { if (Interlocked.CompareExchange (ref rwlock, RwWrite, state) == state) taken = true; } if (taken) return; state = rwlock; } while ((state & RwWait) == 0 && Interlocked.CompareExchange (ref rwlock, state | RwWait, state) != state) state = rwlock; while (rwlock > RwWait) sw.SpinOnce (); } while (true); } finally { state = rwlock; if (!taken && (state & RwWait) != 0) Interlocked.CompareExchange (ref rwlock, state - RwWait, state); } } public void TryEnterWriteLock (ref bool taken) { if (taken) throw new ArgumentException ("taken", "taken needs to be set to false"); int state = rwlock; if (state >= RwWrite) return; try {} finally { if (Interlocked.CompareExchange (ref rwlock, RwWrite, state) == state) taken = true; } } public void ExitWriteLock () { Interlocked.Add (ref rwlock, -RwWrite); } } }