private void Cleanup(WaitBlock toRemove) { WaitBlock prev; WaitBlock wb = (prev = head).next; WaitBlock t = tail; while (wb != null && wb != t) { if (wb == toRemove || wb.parker.IsLocked) { if (prev == head) { head = prev = wb; } else { prev.next = wb.next; } } else { prev = wb; } wb = wb.next; } }
internal Mutant(bool initialState, int sc) { head = tail = new WaitBlock(); if (initialState) { head.next = SET; } spinCount = Platform.IsMultiProcessor ? sc : 0; }
// // Waits until the synchronizer allows the acquire, activating // the specified cancellers. // public bool WaitOne(StCancelArgs cargs) { if (_TryAcquire()) { return(true); } if (cargs.Timeout == 0) { return(false); } var pk = new StParker(); WaitBlock hint = null; int sc = 0; WaitBlock wb; if ((wb = _WaitAnyPrologue(pk, StParkStatus.Success, ref hint, ref sc)) == null) { return(true); } int ws = pk.Park(sc, cargs); if (ws == StParkStatus.Success) { _WaitEpilogue(); return(true); } _CancelAcquire(wb, hint); StCancelArgs.ThrowIfException(ws); return(false); }
// // Waits until acquire the specified number of permits, activating // the specified cancellers. // public bool Wait(int acquireCount, StCancelArgs cargs) { if (acquireCount <= 0 || acquireCount > maximumCount) { throw new ArgumentException("acquireCount"); } if (TryAcquireInternal(acquireCount)) { return(true); } if (cargs.Timeout == 0) { return(false); } var wb = new WaitBlock(WaitType.WaitAny, acquireCount); int sc = EnqueueAcquire(wb, acquireCount); int ws = wb.parker.Park(sc, cargs); if (ws == StParkStatus.Success) { return(true); } CancelAcquire(wb); StCancelArgs.ThrowIfException(ws); return(false); }
private int SlowWait(StCancelArgs cargs, WaitBlock wb) { do { WaitBlock s; if ((s = state) == SET) { return(StParkStatus.Success); } wb.next = s; if (Interlocked.CompareExchange(ref state, wb, s) == s) { break; } } while (true); int ws = wb.parker.Park(wb.next == null ? spinCount : 0, cargs); if (ws != StParkStatus.Success) { Unlink(wb); } return(ws); }
// // Waits until acquire the specified number of permits, activating // the specified cancellers. // public bool Wait(int acquireCount, StCancelArgs cargs) { if (acquireCount <= 0 || acquireCount > maximumCount) { throw new ArgumentException("acquireCount"); } if (TryAcquireInternal(acquireCount)) { return true; } if (cargs.Timeout == 0) { return false; } var wb = new WaitBlock(WaitType.WaitAny, acquireCount); int sc = EnqueueAcquire(wb, acquireCount); int ws = wb.parker.Park(sc, cargs); if (ws == StParkStatus.Success) { return true; } CancelAcquire(wb); StCancelArgs.ThrowIfException(ws); return false; }
internal override WaitBlock _WaitAnyPrologue(StParker pk, int key, ref WaitBlock hint, ref int sc) { WaitBlock wb = null; do { if (_TryAcquire()) { return(null); } if (wb == null) { wb = new WaitBlock(pk, WaitType.WaitAny, ACQUIRE, key); } WaitBlock pred; if (EnqueueWaiter(wb, out pred)) { sc = ((hint = pred) == head) ? spinCount : 0; return(wb); } } while (true); }
internal WaitBlock WaitWithParker(StParker pk, WaitType type, int key, ref int sc) { WaitBlock wb = null; do { WaitBlock s; if ((s = state) == SET) { return(null); } if (wb == null) { wb = new WaitBlock(pk, type, 0, key); } wb.next = s; if (Interlocked.CompareExchange(ref state, wb, s) == s) { sc = s == null ? spinCount : 0; return(wb); } } while (true); }
internal void SetHeadAndUnlock(WaitBlock nh) { // // First, remove the cancelled wait blocks that follow the // new queue's head. // do { WaitBlock w; if ((w = nh.next) == null || !w.parker.IsLocked || w.request < 0) { break; } nh.next = nh; // Mark old head wait block as unlinked. nh = w; } while (true); // // Set the new head and release the queue lock, making // the lock and queue changes visible to all processors. // head = nh; Interlocked.Exchange(ref qlock, FREE); }
internal override WaitBlock _WaitAllPrologue(StParker pk, ref WaitBlock hint, ref int sc) { WaitBlock wb = null; if (_AllowsAcquire) { return(null); } if (wb == null) { wb = new WaitBlock(pk, WaitType.WaitAll, ACQUIRE, StParkStatus.StateChange); } WaitBlock pred; if (EnqueueWaiter(wb, out pred)) { sc = ((hint = pred) == head) ? spinCount : 0; return(wb); } return(null); }
private bool EnqueueWaiter(WaitBlock wb, out WaitBlock pred) { do { WaitBlock t, tn; if ((tn = (t = tail).next) == SET) { pred = null; return(false); } if (tn != null) { AdvanceTail(t, tn); continue; } if (Interlocked.CompareExchange(ref t.next, wb, null) == null) { AdvanceTail(t, wb); pred = t; return(true); } } while (true); }
private void AdvanceTail(WaitBlock t, WaitBlock nt) { if (t == tail) { Interlocked.CompareExchange(ref tail, nt, t); } }
internal bool Set() { if (state == SET) { return(true); } WaitBlock p = Interlocked.Exchange(ref state, SET); // // If the event queue is empty, return the previous state of the event. // if (p == null || p == SET) { return(p == SET); } StParker pk; // // If spinning is configured and there is more than one thread in the // wait queue, we first release the thread that is spinning. As only // one thread spins, we maximize the chances of unparking that thread // before it blocks. // if (spinCount != 0 && p.next != null) { WaitBlock pv = p; WaitBlock n; while ((n = pv.next).next != null) { pv = n; } pv.next = null; if ((pk = n.parker).TryLock()) { pk.Unpark(n.waitKey); } } do { if ((pk = p.parker).TryLock()) { pk.Unpark(p.waitKey); } } while ((p = p.next) != null); // // Return the previous state of the event. // return(false); }
// // Enqueues the specified wait block in the wait queue as a locked // acquire request. This is used to implement wait morphing. When // the method is called the mutant is non-signalled. // internal void EnqueueLockedWaiter(WaitBlock wb) { wb.request = LOCKED_ACQUIRE; wb.next = null; WaitBlock pred; EnqueueWaiter(wb, out pred); }
private bool TryAdvanceHead(WaitBlock h, WaitBlock nh) { if (h == head && Interlocked.CompareExchange(ref head, nh, h) == h) { h.next = h; // Mark the old head as unlinked. return(true); } return(false); }
// // Cancels an enter lock attempt. // private void CancelAcquire(WaitBlock wb) { WaitBlock wbn; if ((wbn = wb.next) != wb && wbn != null && queue.TryLock()) { queue.Unlink(wb); ReleaseWaitersAndUnlockQueue(); } }
internal void Enqueue(WaitBlock wb) { if (head == null) { head = wb; } else { tail.next = wb; } tail = wb; }
// // Waits until enter the write lock, activating the specified // cancellers. // public bool TryEnterWrite(StCancelArgs cargs) { // // Try to enter the write lock and, if succeed, return success. // if (TryEnterWriteInternal()) { return(true); } // // Return failure, if a null timeout was specified. // if (cargs.Timeout == 0) { return(false); } // // Create a wait block and insert it in the r/w lock's queue. // WaitBlock wb = new WaitBlock(WaitType.WaitAny, ENTER_WRITE, StParkStatus.Success); int sc = EnqueueEnterWrite(wb); // // Park the current thread, activating the specified cancellers // and spinning if appropriate. // int ws = wb.parker.Park(sc, cargs); // // If we entered the write lock, return success. // if (ws == StParkStatus.Success) { return(true); } // // The request was cancelled; so, cancel the enter write lock // attempt and report the failure appropriately. // CancelAcquire(wb); StCancelArgs.ThrowIfException(ws); return(false); }
private bool SlowEnter(StCancelArgs cargs) { int lastTime = cargs.Timeout != Timeout.Infinite ? Environment.TickCount : 0; bool timeRemaining = true; WaitBlock wb = EnqueueWaiter(); do { if (TryEnter()) { Cleanup(wb); return(true); } if (!timeRemaining) { return(false); } int ws = wb.parker.Park(head.next == wb ? spinCount : 0, cargs); if (ws != StParkStatus.Success) { StCancelArgs.ThrowIfException(ws); return(false); } if (TryEnter()) { Cleanup(wb); return(true); } // // We failed to acquire the lock so we must clear the current // thread as the candidate owner. After doing so we must recheck // the state of lock. If the wait timed out, we exit the loop // before parking again. // if ((timeRemaining = cargs.AdjustTimeout(ref lastTime))) { wb.parker.Reset(); } // // Avoid a release-followed-by-acquire hazard. // Interlocked.Exchange(ref wb.request, ACQUIRE); } while (true); }
// // Executes the prologue of the Waitable.WaitAll method. // internal override WaitBlock _WaitAllPrologue(StParker pk, ref WaitBlock ignored, ref int sc) { if (_AllowsAcquire) { return(null); } var wb = new WaitBlock(pk, WaitType.WaitAll, ENTER_WRITE, StParkStatus.StateChange); sc = EnqueueEnterWrite(wb); return(wb); }
// // Enqueues the specified wait block in the lock's queue. // When this method is called, the lock is owned by the // current thread. // void IMonitorLock.EnqueueWaiter(WaitBlock wb) { wb.request = LOCKED_ACQUIRE; do { WaitBlock t; wb.next = (t = top); if (Interlocked.CompareExchange(ref top, wb, t) == t) { return; } } while (true); }
internal override WaitBlock _WaitAnyPrologue(StParker pk, int key, ref WaitBlock ignored, ref int sc) { if (TryAcquireInternal(1)) { return(null); } var wb = new WaitBlock(pk, WaitType.WaitAny, 1, key); sc = EnqueueAcquire(wb, 1); return(wb); }
// // Executes the prologue of the Waitable.WaitAny method. // internal override WaitBlock _WaitAnyPrologue(StParker pk, int key, ref WaitBlock ignored, ref int sc) { if (TryEnterWriteInternal()) { return(null); } var wb = new WaitBlock(pk, WaitType.WaitAny, ENTER_WRITE, key); sc = EnqueueEnterWrite(wb); return(wb); }
// // Exits the lock. // public void Exit() { // // Because atomic operations on references are more expensive than // on integers, we try to optimize the release when the wait queue // is empty. However, when the wait queue is seen as non-empty after // the lock is released, our algorithm resorts to another atomic // instruction in order to unoark pending waiters. // if (top == null) { Interlocked.Exchange(ref state, FREE); if (top == null) { return; } } else { state = FREE; } // // Unpark all waiting threads. Because the spin lock's queue is implemented // as a stack, we build another one in order to unpark the waiting threads // according to their arrival order. // WaitBlock p = Interlocked.Exchange(ref top, null); WaitBlock ws = null, n; while (p != null) { n = p.next; if (p.request == LOCKED_ACQUIRE || p.parker.TryLock()) { p.next = ws; ws = p; } p = n; } while (ws != null) { n = ws.next; ws.parker.Unpark(StParkStatus.Success); ws = n; } }
internal WaitBlock Dequeue() { WaitBlock wb; if ((wb = head) == null) { return(null); } if ((head = wb.next) == null) { tail = null; } return(wb); }
private void CancelAcquire(WaitBlock wb) { // // If the wait block is still linked and it isn't the last wait block // in the queue and the queue's lock is free, unlink the wait block. // WaitBlock wbn; if ((wbn = wb.next) != wb && wbn != null && queue.TryLock()) { queue.Unlink(wb); ReleaseWaitersAndUnlockQueue(null); } }
internal void Unlink(WaitBlock wb) { // // We can return immediately if the queue is empty or if the WaitBlock // is the only one in it and we succeeded in removing it. // WaitBlock s; if ((s = state) == SET || s == null || (wb.next == null && s == wb && Interlocked.CompareExchange(ref state, null, s) == s)) { return; } SlowUnlink(wb); }
private int EnqueueAcquire(WaitBlock wb, int acquireCount) { bool isFirst = queue.Enqueue(wb); // // If the wait block was inserted at the front of the queue and // the current thread can now acquire the requested permits, try // to lock the queue and execute the release processing. // if (isFirst && state >= acquireCount && queue.TryLock()) { ReleaseWaitersAndUnlockQueue(wb); } return(isFirst ? spinCount : 0); }
internal void Remove(WaitBlock wb) { // // Return immediately if the wait block has been unlinked. // if (wb.next == wb) { return; } // // Compute the previous wait block and perform the removal. // WaitBlock p = head; WaitBlock pv = null; while (p != null) { if (p == wb) { if (pv == null) { if ((head = wb.next) == null) { tail = null; } } else { if ((pv.next = wb.next) == null) { tail = pv; } } wb.next = wb; return; } pv = p; p = p.next; } throw new InvalidOperationException(); }
// // Enqueues an enter write lock attempt. // private int EnqueueEnterWrite(WaitBlock wb) { // // Enqueue the wait block in the r/w lock queue. // bool isFirst = queue.Enqueue(wb); // // If the wait block was inserted at front of the queue, check if // we can acquire now; if so, execute the release processing. // if (isFirst && state == 0 && queue.TryLock()) { ReleaseWaitersAndUnlockQueue(); } return(isFirst ? spinCount : 0); }
private WaitBlock EnqueueWaiter() { var wb = new WaitBlock(ACQUIRE); do { WaitBlock t, tn; if ((tn = (t = tail).next) != null) { AdvanceTail(t, tn); continue; } if (Interlocked.CompareExchange(ref t.next, wb, null) == null) { AdvanceTail(t, wb); return(wb); } } while (true); }
// // Executes the prologue of the Waitable.WaitAll method. // internal override WaitBlock _WaitAllPrologue(StParker pk, ref WaitBlock hint, ref int sc) { return tmrEvent._WaitAllPrologue(pk, ref hint, ref sc); }
// // Waits until enter the write lock, activating the specified // cancellers. // public bool TryEnterWrite(StCancelArgs cargs) { // // Try to enter the write lock and, if succeed, return success. // if (TryEnterWriteInternal()) { return true; } // // Return failure, if a null timeout was specified. // if (cargs.Timeout == 0) { return false; } // // Create a wait block and insert it in the r/w lock's queue. // WaitBlock wb = new WaitBlock(WaitType.WaitAny, ENTER_WRITE, StParkStatus.Success); int sc = EnqueueEnterWrite(wb); // // Park the current thread, activating the specified cancellers // and spinning if appropriate. // int ws = wb.parker.Park(sc, cargs); // // If we entered the write lock, return success. // if (ws == StParkStatus.Success) { return true; } // // The request was cancelled; so, cancel the enter write lock // attempt and report the failure appropriately. // CancelAcquire(wb); StCancelArgs.ThrowIfException(ws); return false; }
private void AdvanceTail(WaitBlock t, WaitBlock nt) { if (tail == t) { Interlocked.CompareExchange(ref tail, nt, t); } }
internal void Init() { head = tail = new WaitBlock(); }
internal WaitBlock Dequeue() { WaitBlock wb; if ((wb = head) == null) { return null; } if ((head = wb.next) == null) { tail = null; } return wb; }
internal override void _CancelAcquire(WaitBlock wb, WaitBlock hint) { flock._CancelAcquire(wb, hint); }
// // Executes the prologue of the Waitable.WaitAny method. // internal override WaitBlock _WaitAnyPrologue(StParker pk, int key, ref WaitBlock hint, ref int sc) { return FastTryEnterWrite(Thread.CurrentThread.ManagedThreadId) ? null : rwlock._WaitAnyPrologue(pk, key, ref hint, ref sc); }
// // Adds a writer waiter to the waiting list. // // NOTE: When this method is called, we know that the write // lock is owned by the current thread. // void IMonitorLock.EnqueueWaiter(WaitBlock wb) { ((IMonitorLock)rwlock).EnqueueWaiter(wb); }
// // Executes the unpark callback. // private void UnparkCallback(int ws) { Debug.Assert(ws == StParkStatus.Success || ws == StParkStatus.Timeout || ws == StParkStatus.WaitCancelled); // // If the registered wait was cancelled, cancel the acquire // attempt and return immediately. // if (ws == StParkStatus.WaitCancelled) { waitable._CancelAcquire(waitBlock, hint); return; } // // Set state to *our busy* grabing the current state, and execute // the unpark callback processing. // StParker myBusy = new SentinelParker(); StParker oldState = Interlocked.Exchange<StParker>(ref state, myBusy); do { // // If the acquire operation was cancelled, cancel the acquire // attempt on the waitable. // if (ws != StParkStatus.Success) { waitable._CancelAcquire(waitBlock, hint); } // // Execute the user callback routine. // cbtid = Thread.CurrentThread.ManagedThreadId; callback(cbState, ws == StParkStatus.Timeout); cbtid = 0; // // If the registered wait was configured to execute once or // there is an unregister in progress, set the state to INACTIVE. // If a thread is waiting to unregister, unpark it. // if (executeOnce || !(oldState is SentinelParker)) { if (!(oldState is SentinelParker)) { oldState.Unpark(StParkStatus.Success); } else { state = INACTIVE; } return; } // // We must re-register with the Waitable. // So, initialize the parker and execute the WaitAny prologue. // cbparker.Reset(1); int ignored = 0; waitBlock = waitable._WaitAnyPrologue(cbparker, StParkStatus.Success, ref hint, ref ignored); // // Enable the unpark callback. // ws = cbparker.EnableCallback(timeout, toTimer); if (ws == StParkStatus.Pending) { // // If the *state* field constains still *my busy* set it to ACTIVE. // if (state == myBusy) { Interlocked.CompareExchange<StParker>(ref state, ACTIVE, myBusy); } return; } // // The waitable was already signalled. So, execute the unpark // callback inline. // } while (true); }
// // Constructor: registers a wait with a Waitable synchronizer. // internal StRegisteredWait(StWaitable waitObject, WaitOrTimerCallback callback, object cbState, int timeout, bool executeOnce) { // // Validate the arguments. // if (timeout == 0) { throw new ArgumentOutOfRangeException("\"timeout\" can't be zero"); } if (callback == null) { throw new ArgumentOutOfRangeException("\"callback\" can't be null"); } if ((waitObject is StReentrantFairLock) || (waitObject is StReentrantReadWriteLock)) { throw new InvalidOperationException("can't register waits on reentrant locks"); } if ((waitObject is StNotificationEvent) && !executeOnce) { throw new InvalidOperationException("Notification event can't register waits" + " to execute more than once"); } // // Initialize the register wait fields // waitable = waitObject; cbparker = new CbParker(UnparkCallback); toTimer = new RawTimer(cbparker); this.timeout = timeout; this.executeOnce = executeOnce; this.callback = callback; this.cbState = (cbState != null) ? cbState : this; // // Execute the WaitAny prologue on the waitable. // int ignored = 0; waitBlock = waitObject._WaitAnyPrologue(cbparker, StParkStatus.Success, ref hint, ref ignored); // // Set the registered wait state to active and enable the // unpark callback. // state = ACTIVE; int ws = cbparker.EnableCallback(timeout, toTimer); if (ws != StParkStatus.Pending) { // // The acquire operation was already accomplished. To prevent // uncontrolled reentrancy, the unpark callback is executed inline. // UnparkCallback(ws); } }
// // Only one thread act as a releaser at any given time. // private void ReleaseWaitersAndUnlockQueue(WaitBlock self) { do { WaitBlock qh = queue.head; WaitBlock w; while (state > 0 && (w = qh.next) != null) { StParker pk = w.parker; if (w.waitType == WaitType.WaitAny) { if (!TryAcquireInternalQueued(w.request)) { break; } if (pk.TryLock()) { if (w == self) { pk.UnparkSelf(w.waitKey); } else { pk.Unpark(w.waitKey); } } else { UndoAcquire(w.request); } } else if (pk.TryLock()) { if (w == self) { pk.UnparkSelf(w.waitKey); } else { pk.Unpark(w.waitKey); } } // // Remove the wait block from the semaphore's queue, // marking the previous head as unlinked, and advance // the local queues's head. // qh.next = qh; qh = w; } // // It seems that no more waiters can be released; so, // set the new semaphore queue's head and unlock it. // queue.SetHeadAndUnlock(qh); // // If after the semaphore's queue is unlocked, it seems that // more waiters can be released, repeat the release processing. // if (!IsReleasePending) { return; } } while (true); }
// // Enqueues the specified wait block in the lock's queue as a locked // acquire request. When this method is callead the lock is owned by // the current thread. // void IMonitorLock.EnqueueWaiter(WaitBlock wb) { flock.EnqueueLockedWaiter(wb); }
internal void Remove(WaitBlock wb) { // // Return immediately if the wait block has been unlinked. // if (wb.next == wb) { return; } // // Compute the previous wait block and perform the removal. // WaitBlock p = head; WaitBlock pv = null; while (p != null) { if (p == wb) { if (pv == null) { if ((head = wb.next) == null) { tail = null; } } else { if ((pv.next = wb.next) == null) tail = pv; } wb.next = wb; return; } pv = p; p = p.next; } throw new InvalidOperationException(); }
internal override WaitBlock _WaitAnyPrologue(StParker pk, int key, ref WaitBlock hint, ref int sc) { return _TryAcquire() ? null : flock._WaitAnyPrologue(pk, key, ref hint, ref sc); }
internal bool Enqueue(WaitBlock wb) { do { WaitBlock t = tail; WaitBlock tn = t.next; // // Do the necessary consistency checks. // if (t != tail) { continue; } if (tn != null) { AdvanceTail(t, tn); continue; } // // Queue in quiescent state, try to insert the wait block. // if (t.CasNext(null, wb)) { // // Enqueue succeed; So, try to swing the tail to the // inserted wait block and return. // AdvanceTail(t, wb); return t == head; } } while (true); }
internal override WaitBlock _WaitAllPrologue(StParker pk, ref WaitBlock hint, ref int sc) { return _AllowsAcquire ? null : flock._WaitAllPrologue(pk, ref hint, ref sc); }
internal void Unlink(WaitBlock wb) { if (wb.next == wb || wb == head) { return; } // // Remove the cancelled wait nodes from *head* till *wb*. // WaitBlock n; WaitBlock pv = head; while ((n = pv.next) != wb) { if (n.parker.IsLocked) { pv.next = n.next; n.next = n; } else { pv = n; } } // // Remove the wait block *wb* and also the cancelled wait // blocks that follow it. // do { pv.next = n.next; n.next = n; } while ((n = pv.next).next != null && n.parker.IsLocked); }
internal override void _CancelAcquire(WaitBlock wb, WaitBlock ignored) { CancelAcquire(wb); }
// // Enqueue a locked enter write request in the r/w lock's queue. // void IMonitorLock.EnqueueWaiter(WaitBlock waitBlock) { waitBlock.request = LOCKED_ENTER_WRITE; queue.Enqueue(waitBlock); }
internal override WaitBlock _WaitAllPrologue(StParker pk, ref WaitBlock ignored, ref int sc) { if (_AllowsAcquire) { return null; } var wb = new WaitBlock(pk, WaitType.WaitAll, 1, StParkStatus.StateChange); sc = EnqueueAcquire(wb, 1); return wb; }
internal override WaitBlock _WaitAnyPrologue(StParker pk, int key, ref WaitBlock ignored, ref int sc) { if (TryAcquireInternal(1)) { return null; } var wb = new WaitBlock(pk, WaitType.WaitAny, 1, key); sc = EnqueueAcquire(wb, 1); return wb; }
// // Executes the prologue of the Waitable.WaitAny method. // internal override WaitBlock _WaitAnyPrologue(StParker pk, int key, ref WaitBlock ignored, ref int sc) { if (TryEnterWriteInternal()) { return null; } var wb = new WaitBlock(pk, WaitType.WaitAny, ENTER_WRITE, key); sc = EnqueueEnterWrite(wb); return wb; }
// // Cancels the specified acquire attempt. // internal override void _CancelAcquire(WaitBlock wb, WaitBlock hint) { tmrEvent._CancelAcquire(wb, hint); }
// // CASes on the *next* field. // internal bool CasNext(WaitBlock n, WaitBlock nn) { return (next == n && Interlocked.CompareExchange<WaitBlock>(ref next, nn, n) == n); }
private int EnqueueAcquire(WaitBlock wb, int acquireCount) { bool isFirst = queue.Enqueue(wb); // // If the wait block was inserted at the front of the queue and // the current thread can now acquire the requested permits, try // to lock the queue and execute the release processing. // if (isFirst && state >= acquireCount && queue.TryLock()) { ReleaseWaitersAndUnlockQueue(wb); } return isFirst ? spinCount : 0; }