private void Deactivate () { state = INACTIVE; waitObject = null; waitBlock = null; hint = null; callback = null; cbparker = null; cbState = null; WaitHandle no; if ((no = notificationObject) != null) { notificationObject = null; no.Signal (); } }
internal override StWaitBlock _WaitAllPrologue (StParker pk, ref StWaitBlock hint, ref int sc) { hint = INFLATED; return null; }
internal override void _CancelAcquire (StWaitBlock wb, StWaitBlock hint) { }
internal abstract void _CancelAcquire (StWaitBlock wb, StWaitBlock hint);
protected override bool EnqueueWaiter (StWaitBlock wb, out StWaitBlock pred) { wb.request = Thread.CurrentThreadId; do { StWaitBlock 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); }
internal override StWaitBlock _WaitAllPrologue (StParker pk, ref StWaitBlock hint, ref int sc) { return WaitWithParker (pk, WaitType.WaitAll, StParkStatus.StateChange, ref hint, ref sc); }
internal abstract StWaitBlock _WaitAllPrologue (StParker pk, ref StWaitBlock hint, ref int sc);
internal static int WaitAny (StWaitable[] ws, StCancelArgs cargs) { if (ws == null) { throw new ArgumentNullException ("ws"); } int len = ws.Length; for (int i = 0; i < len; i++) { if (ws [i]._TryAcquire ()) { return StParkStatus.Success + i; } } if (cargs.Timeout == 0) { return StParkStatus.Timeout; } retry: /* * Create a parker and execute the WaitAny prologue on all * waitables. We stop executing prologues as soon as we detect * that the acquire operation was accomplished. */ var pk = new StParker (1); int inflated = 0; int inflatedCount = 0; var wbs = new StWaitBlock [len]; var hints = new StWaitBlock [len]; int lv = -1; int gsc = 0; for (int i = 0; !pk.IsLocked && i < len; i++) { StWaitable w = ws [i]; int sc = 0; if ((wbs [i] = w._WaitAnyPrologue (pk, i, ref hints [i], ref sc)) == null) { if (hints [i] == INFLATED) { inflated |= 1 << i; inflatedCount += 1; } else { if (pk.TryLock ()) { pk.UnparkSelf (i); } else { w._UndoAcquire (); } break; } } else if (gsc < sc) { gsc = sc; } lv = i; } int wst = inflatedCount == len ? InflatedWaitMultiple (ws, false, inflated, inflatedCount, cargs) : inflatedCount > 0 ? pk.Park (gsc, ws, inflated, inflatedCount, cargs) : pk.Park (gsc, cargs); StWaitable acq = wst >= StParkStatus.Success ? ws [wst] : null; /* * Cancel the acquire attempt on all waitables where we executed the WaitAny * prologue, except the one we acquired and the ones that are inflated. */ for (int i = 0; i <= lv; i++) { StWaitable w = ws [i]; StWaitBlock wb = wbs [i]; if (w != acq && wb != null) { w._CancelAcquire (wb, hints [i]); } } if (acq != null) { try { acq._WaitEpilogue (); } catch (AbandonedMutexException e) { e.MutexIndex = wst; throw; } return wst; } if (wst == StParkStatus.Inflated) { goto retry; } cargs.ThrowIfException (wst); return StParkStatus.Timeout; }
internal static bool WaitAll (StWaitable[] ws, StCancelArgs cargs) { if (ws == null) { throw new ArgumentNullException ("ws"); } int nevts; int len = ws.Length; var sws = new StWaitable [len]; int waitHint = SortAndCheckAllowAcquire (ws, sws, out nevts); if (waitHint < 0) { throw new DuplicateWaitObjectException (); } /* * Return success if all synchronizers are notification events and are set. */ if (waitHint != 0) { if (nevts == 0) { return true; } } else if (cargs.Timeout == 0) { return false; } /* * If a timeout was specified, get the current time in order * to adjust the timeout value later, if we re-wait. */ int lastTime = (cargs.Timeout != Timeout.Infinite) ? Environment.TickCount : 0; StWaitBlock[] wbs = null; StWaitBlock[] hints = null; do { int inflated = 0; AbandonedMutexException ame = null; if (waitHint == 0) { if (wbs == null) { wbs = new StWaitBlock [len]; hints = new StWaitBlock [len]; } /* * Create a parker for cooperative release, specifying as many * releasers as the number of waitables. The parker is not reused * because other threads may have references to it. */ var pk = new StParker (len); int inflatedCount = 0; int gsc = 1; int sc = 0; for (int i = 0; i < len; i++) { if ((wbs [i] = sws [i]._WaitAllPrologue (pk, ref hints [i], ref sc)) == null) { if (hints [i] == INFLATED) { inflated |= 1 << i; inflatedCount += 1; } else if (pk.TryLock ()) { pk.UnparkSelf (StParkStatus.StateChange); } } else if (gsc != 0) { if (sc == 0) { gsc = 0; } else if (sc > gsc) { gsc = sc; } } } if (inflatedCount > 0 && pk.TryLock (inflatedCount)) { pk.UnparkSelf (StParkStatus.StateChange); } int wst = pk.Park (gsc, cargs); /* * We opt for a less efficient but simpler implementation instead * of using the same approach as the WaitAny operation, because: * - When parking, the thread would have to call into the park spot * even if it was already unparked, since we have to wait for the * other handles as well; * - We would have to deal with cancellation in a different way, * relying on interrupts instead of the TryCancel/Unpark pair; * - The Unpark operation might not wake the target thread, which * could lead to bugs. */ if (wst == StParkStatus.StateChange && inflatedCount > 0) { if (!cargs.AdjustTimeout (ref lastTime)) { return false; } wst = InflatedWaitMultiple (ws, true, inflated, inflatedCount, cargs); } if (wst != StParkStatus.StateChange) { for (int i = 0; i < len; i++) { StWaitBlock wb = wbs [i]; if (wb != null) { sws [i]._CancelAcquire (wb, hints [i]); } } if (wst == StParkStatus.Inflated) { waitHint = 0; continue; } cargs.ThrowIfException (wst); return false; } } /* * All waitables where we inserted wait blocks seem to allow an * immediate acquire operation; so, try to acquire all non-inflated * waitables that are not notification events. */ int idx; for (idx = 0; idx < nevts; idx++) { try { if ((inflated & (1 << idx)) == 0 && !sws[idx]._TryAcquire()) { break; } } catch (AbandonedMutexException e) { ame = e; ame.MutexIndex = idx; } } if (idx == nevts) { if (ame != null) { throw ame; } return true; } /* * We failed to acquire all waitables, so undo the acquires * that we did above. */ for (int i = idx + 1; i < nevts; ++i) { if ((inflated & (1 << idx)) != 0) { sws[i]._UndoAcquire (); } } while (--idx >= 0) { sws[idx]._UndoAcquire (); } if (!cargs.AdjustTimeout (ref lastTime)) { return false; } waitHint = 0; } while (true); }
private void SlowUnlink (StWaitBlock wb) { StWaitBlock next; if ((next = wb.next) != null && next.parker.IsLocked) { next = next.next; } StWaitBlock p = state; StWaitBlock s; while (p != null && p != next && (s = state) != null && s != SET && s != INFLATED) { StWaitBlock n; if ((n = p.next) != null && n.parker.IsLocked) { p.CasNext (n, n.next); } else { p = n; } } }
internal StNotificationEventBase (bool initialState, int sc) { id = NOTIFICATION_EVENT_ID; state = initialState ? SET : null; spinCount = Environment.ProcessorCount > 0 ? sc : 0; }
internal void Unlink (StWaitBlock wb) { StWaitBlock s; if ((s = state) == SET || s == null || s == INFLATED || (wb.next == null && s == wb && Interlocked.CompareExchange (ref state, null, s) == s)) { return; } SlowUnlink (wb); }
internal override void _CancelAcquire (StWaitBlock wb, StWaitBlock ignored) { Unlink (wb); }
private StWaitBlock WaitWithParker (StParker pk, WaitType type, int key, ref StWaitBlock hint, ref int sc) { StWaitBlock wb = null; do { StWaitBlock s; if ((s = state) == SET) { return null; } if (s == INFLATED) { hint = INFLATED; return null; } if (wb == null) { wb = new StWaitBlock (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 RegisteredWaitHandle (WaitHandle waitObject, WaitOrTimerCallback callback, object cbState, int timeout, bool executeOnlyOnce) { this.waitObject = waitObject; this.callback = callback; this.cbState = cbState; this.timeout = timeout; this.executeOnlyOnce = executeOnlyOnce; StWaitable waitable; if ((waitable = waitObject.waitable) == null) { /* * Either we're dealing with a disposed wait handle * or with some other derived class. */ UnparkCallback (StParkStatus.Inflated); return; } cbparker = new CbParker (UnparkCallback, false, true); int ignored = 0; waitBlock = waitable._WaitAnyPrologue (cbparker, StParkStatus.Success, ref hint, ref ignored); state = ACTIVE; int ws = cbparker.EnableCallback (timeout); if (ws != StParkStatus.Pending) { UnparkCallback (ws); } }
internal abstract StWaitBlock _WaitAnyPrologue (StParker pk, int key, ref StWaitBlock hint, ref int sc);
private void UnparkCallback (int ws) { var waitable = waitObject.waitable; if (ws == StParkStatus.Cancelled) { waitable._CancelAcquire (waitBlock, hint); return; } ThreadPool.QueueUserWorkItem (_ => { do { if (ws == StParkStatus.Inflated) { InflatedWait (); return; } if (ws != StParkStatus.Success) { waitable._CancelAcquire (waitBlock, hint); } if (ws != StParkStatus.Cancelled) { cbtid = Thread.CurrentThread.ManagedThreadId; callback (cbState, ws == StParkStatus.Timeout); cbtid = 0; } if (executeOnlyOnce || ws == StParkStatus.Cancelled) { FinishExecution (); return; } /* * We must re-register with the waitable. So, initialize the * parker and execute the WaitAny prologue. */ cbparker.Reset (1); Thread.MemoryBarrier (); if (state != ACTIVE) { if (cbparker.TryCancel ()) { cbparker.Unpark (StParkStatus.Cancelled); } WaitUntilUnregistered (); Deactivate (); return; } int ignored = 0; waitBlock = waitable._WaitAnyPrologue (cbparker, StParkStatus.Success, ref hint, ref ignored); ws = cbparker.EnableCallback (timeout); if (ws == StParkStatus.Pending) { return; } /* * The waitable was already signalled. So, execute the unpark * callback inline. */ } while (true); }); }
internal override StWaitBlock _WaitAnyPrologue (StParker pk, int key, ref StWaitBlock hint, ref int sc) { return WaitWithParker (pk, WaitType.WaitAny, key, ref hint, ref sc); }