internal bool SlowEnter (StCancelArgs cargs) { int lastTime = (cargs.Timeout != Timeout.Infinite) ? Environment.TickCount : 0; StWaitBlock wb = null; do { int sc = spinCount; #if NET_4_0 var spinWait = new SpinWait (); #endif do { if (state == FREE && Interlocked.CompareExchange (ref state, BUSY, FREE) == FREE) { return true; } if (top != null || sc-- <= 0) { break; } #if NET_4_0 spinWait.SpinOnce (); #else Thread.SpinWait (1); #endif } while (true); if (wb == null) { wb = new StWaitBlock (1); } else { wb.parker.Reset (); } do { StWaitBlock t; wb.next = t = top; if (Interlocked.CompareExchange (ref top, wb, t) == t) { break; } } while (true); if (TryEnter ()) { wb.parker.SelfCancel (); return true; } int ws = wb.parker.Park (cargs); if (ws != StParkStatus.Success) { cargs.ThrowIfException (ws); return false; } if (TryEnter ()) { return true; } if (!cargs.AdjustTimeout (ref lastTime)) { return false; } } while (true); }
internal StMutant (bool initialState, int sc) { head = tail = new StWaitBlock (); if (initialState) { head.next = SET; } spinCount = Environment.ProcessorCount > 0 ? sc : 0; }
internal override StWaitBlock _WaitAnyPrologue (StParker pk, int key, ref StWaitBlock hint, ref int sc) { StWaitBlock wb = null; do { if (_TryAcquire ()) { return null; } if (wb == null) { wb = new StWaitBlock (pk, WaitType.WaitAny, 1, key); } if (EnqueueWaiter (wb, out hint)) { sc = hint == head ? spinCount : 0; return wb; } if (head.next == INFLATED) { return null; } } while (true); }
internal void Enqueue(StWaitBlock wb) { if (head == null) { head = wb; } else { tail.next = wb; } tail = wb; }
internal bool CasNext (StWaitBlock n, StWaitBlock nn) { return (next == n && Interlocked.CompareExchange (ref next, nn, n) == n); }
internal override StWaitBlock _WaitAllPrologue (StParker pk, ref StWaitBlock hint, ref int sc) { if (_AllowsAcquire) { return null; } var wb = new StWaitBlock (pk, WaitType.WaitAll, 1, StParkStatus.StateChange); if (EnqueueWaiter (wb, out hint)) { sc = hint == head ? spinCount : 0; return wb; } return null; }
internal override void _CancelAcquire (StWaitBlock wb, StWaitBlock ignored) { CancelAcquire (wb); }
private void AdvanceTail (StWaitBlock t, StWaitBlock nt) { if (tail == t) { Interlocked.CompareExchange (ref tail, nt, t); } }
internal void SetHeadAndUnlock (StWaitBlock nh) { do { StWaitBlock w; if ((w = nh.next) == null || !w.parker.IsLocked || w.request < 0) { break; } nh.next = nh; // Mark old head's wait block as unlinked. nh = w; } while (true); head = nh; Interlocked.Exchange (ref qlock, FREE); }
internal void Init () { head = tail = new StWaitBlock (); }
protected void AdvanceTail (StWaitBlock t, StWaitBlock nt) { if (t == tail) { Interlocked.CompareExchange (ref tail, nt, t); } }
internal override void _CancelAcquire (StWaitBlock wb, StWaitBlock hint) { while (hint.next == wb) { /* * Remove the cancelled wait blocks that are at the front * of the queue. */ StWaitBlock h; StWaitBlock hn = (h = head).next; if (hn == INFLATED) { return; } if (hn != null && hn != SET && hn.parker.IsLocked) { TryAdvanceHead (h, hn); continue; } /* * If the queue is empty, return. */ StWaitBlock t, tn; if ((t = tail) == h) { return; } /* * Do the necessary consistency checks before trying to * unlink the wait block. */ if ((tn = t.next) != null) { AdvanceTail (t, tn); continue; } /* * If the wait block is not at the tail of the queue, try * to unlink it. */ if (wb != t) { StWaitBlock wbn; if ((wbn = wb.next) == wb || hint.CasNext (wb, wbn)) { return; } } /* * The wait block is at the tail of the queue; so, take * into account the *toUnlink* wait block. */ StWaitBlock dp; if ((dp = toUnlink) != null) { StWaitBlock d, dn; if ((d = dp.next) == dp || ((dn = d.next) != null && dp.CasNext (d, dn))) { CasToUnlink (dp, null); } if (dp == hint) { return; // *wb* is an already the saved node. } } else if (CasToUnlink (null, hint)) { return; } } }
protected abstract bool EnqueueWaiter (StWaitBlock wb, out StWaitBlock pred);
/* * No need to worry about inflating as this is not called by Semaphore. */ internal bool TryWait (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 StWaitBlock (WaitType.WaitAny, acquireCount); int sc = EnqueueAcquire (wb, acquireCount); int ws = wb.parker.Park (sc, cargs); if (ws == StParkStatus.Success) { return true; } CancelAcquire (wb); cargs.ThrowIfException (ws); return false; }
private void CancelAcquire (StWaitBlock wb) { /* * If the wait block is still linked and it isn't the last wait block * of the queue and the queue's lock is free unlink the wait block. */ StWaitBlock wbn; if ((wbn = wb.next) != wb && wbn != null && queue.TryLock ()) { queue.Unlink (wb); ReleaseWaitersAndUnlockQueue (null); } }
private int EnqueueAcquire (StWaitBlock 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; }
private void ReleaseWaitersAndUnlockQueue(StWaitBlock self) { do { StWaitBlock qh = queue.head; StWaitBlock 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); } } qh.next = qh; qh = w; } queue.SetHeadAndUnlock (qh); } while (IsReleasePending); }
internal StWaitBlock Dequeue() { StWaitBlock wb; if ((wb = head) == null) { return null; } if ((head = wb.next) == null) { tail = null; } return wb; }
internal void Remove(StWaitBlock wb) { if (wb.next == wb) { return; } StWaitBlock p = head; StWaitBlock 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(); }
protected bool TryAdvanceHead (StWaitBlock h, StWaitBlock nh) { if (h == head && Interlocked.CompareExchange (ref head, nh, h) == h) { h.next = h; // Mark the old head as unlinked. return true; } return false; }
internal bool Enqueue (StWaitBlock wb) { do { StWaitBlock t = tail; if (t == null) { wb.next = wb; return false; // Useful for the inflate operation. } StWaitBlock tn = t.next; if (tn != null) { AdvanceTail (t, tn); continue; } if (t.CasNext (null, wb)) { AdvanceTail (t, wb); return t == head; } } while (true); }
private bool CasToUnlink (StWaitBlock tu, StWaitBlock ntu) { return toUnlink == tu && Interlocked.CompareExchange (ref toUnlink, ntu, tu) == tu; }
internal void Unlink (StWaitBlock wb) { if (wb.next == wb || wb == head) { return; } StWaitBlock n; StWaitBlock pv = head; while ((n = pv.next) != wb) { if (n.parker.IsLocked) { pv.next = n.next; n.next = n; } else { pv = n; } } do { pv.next = n.next; n.next = n; } while ((n = pv.next).next != null && n.parker.IsLocked); }
internal override StWaitBlock _WaitAllPrologue (StParker pk, ref StWaitBlock hint, ref int sc) { return _AllowsAcquire ? null : WaitPrologue(pk, WaitType.WaitAll, StParkStatus.StateChange, ref hint, ref sc); }
internal override StWaitBlock _WaitAnyPrologue (StParker pk, int key, ref StWaitBlock hint, ref int sc) { return TryAcquireInternal (1) ? null : WaitPrologue(pk, WaitType.WaitAny, key, ref hint, ref sc); }
private StWaitBlock WaitPrologue (StParker pk, WaitType type, int key, ref StWaitBlock hint, ref int sc) { if (state == SEM_INFLATED) { hint = INFLATED; return null; } var wb = new StWaitBlock (pk, type, 1, key); sc = EnqueueAcquire (wb, 1); if (state == SEM_INFLATED && pk.TryCancel ()) { pk.UnparkSelf (StParkStatus.Inflated); hint = INFLATED; return null; } return wb; }