private static StWaitable[] CheckArray (WaitHandle[] waitHandles, bool waitAll) { if (waitHandles == null) { throw new ArgumentNullException ("waitHandles"); } /* int length = handles.Length; if (length > 64) { throw new NotSupportedException ("Too many handles"); } */ if (waitHandles.Length == 0) { // MS throws different exceptions from the different methods. if (waitAll) { throw new ArgumentNullException ("waitHandles"); } throw new ArgumentException (); } #if false // // Although we should thrown an exception if this is an STA thread, // Mono does not know anything about STA threads, and just makes // things like Paint.NET not even possible to work. // // See bug #78455 for the bug this is supposed to fix. // if (waitAll && length > 1 && IsSTAThread) throw new NotSupportedException ("WaitAll for multiple handles is not allowed on an STA thread."); #endif /* * FIXME: What happens when another derived class is in the array? */ var waitables = new StWaitable[waitHandles.Length]; for (int i = 0; i < waitables.Length; ++i) { var w = waitHandles [i]; if (w == null) { throw new ArgumentNullException ("waitHandles", "null handle"); } w.CheckCanWait (); waitables [i] = waitHandles [i].waitable; } return waitables; }
protected virtual void Dispose (bool explicitDisposing) { if (explicitDisposing && safe_wait_handle != DISPOSED) { lock (locker) { var swhandle = safe_wait_handle; safe_wait_handle = DISPOSED; if (swhandle != null && swhandle != DISPOSED) { swhandle.Close (); } if (waitable != null) { waitable = null; } } } }
internal WaitHandle (StWaitable waitable) { this.waitable = waitable; }
internal static void SignalAndWait (StWaitable tos, StWaitable tow) { SignalAndWait (tos, tow, StCancelArgs.None); }
internal EventWaitHandle (StWaitable waitable) : base (waitable) { }
internal static void WaitAll (StWaitable[] ws) { WaitAll (ws, StCancelArgs.None); }
internal static bool SignalAndWait (StWaitable tos, StWaitable tow, StCancelArgs cargs) { if (tos == tow) { return true; } var pk = new StParker (); StWaitBlock hint = null; int sc = 0; StWaitBlock wb = tow._WaitAnyPrologue (pk, StParkStatus.Success, ref hint, ref sc); if (!tos._Release ()) { if (wb != null && pk.TryCancel ()) { tow._CancelAcquire (wb, hint); } else { if (wb != null) { pk.Park (); } tow._UndoAcquire (); } throw tos._SignalException; } int ws; if (hint == INFLATED || (ws = pk.Park (sc, cargs)) == StParkStatus.Inflated) { return tow.InflatedWait (cargs); } if (ws == StParkStatus.Success) { tow._WaitEpilogue (); return true; } tow._CancelAcquire (wb, hint); cargs.ThrowIfException (ws); return false; }
/* * Sorts the waitable array by the waitable id and, at the same time, * check if all waitables allow an immediate acquire operation. * * NOTE: The notification events are not sorted, because they don't * have an acquire side-effect. The notification events are * grouped at the end of the sorted array. */ private static int SortAndCheckAllowAcquire (StWaitable[] ws, StWaitable[] sws, out int nevts) { int i; StWaitable w; bool acqAll = true; int len = ws.Length; /* * Find the first waitable that isn't a notification event, * in order to start insertion sort. */ nevts = len; for (i = 0; i < len; i++) { w = ws [i]; acqAll &= w._AllowsAcquire; /* * If the current waitable is a notification event, insert * it at the end of the ordered array; otherwise, insert it * at the begin of the array and break the loop. */ if (w.id == NOTIFICATION_EVENT_ID) { sws [--nevts] = w; } else { sws [0] = w; break; } } /* * If all synchronizers are notification events, return. */ if (nevts == 0) { return acqAll ? 1 : 0; } /* * Sort the remaining synchronizers using the insertion sort * algorithm but only with the non-notification event waitables. */ int k = 1; for (i++; i < len; i++, k++) { w = ws [i]; acqAll &= w._AllowsAcquire; if (w.id == NOTIFICATION_EVENT_ID) { sws [--nevts] = w; } else { sws [k] = w; int j = k - 1; while (j >= 0 && sws [j].Id > w.Id) { sws [j + 1] = sws [j]; j--; } sws [j + 1] = w; if (sws [k - 1] == sws [k]) { return -1; } } } return acqAll ? 1 : 0; }
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); }
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 int WaitAny (StWaitable[] ws) { return WaitAny (ws, StCancelArgs.None); }
private static int InflatedWaitMultiple (StWaitable[] ws, bool waitAll, int inflatedIndexes, int inflatedCount, StCancelArgs cargs) { var handles = new SafeWaitHandle [inflatedCount]; var refs = new bool [inflatedCount]; #if NET_4_0 || MOBILE var reg = default (CancellationTokenRegistration); #endif try { for (int i = 0, handleIdx = 0; i < ws.Length; ++i) { if ((inflatedIndexes & (1 << i)) != 0) { var swhandle = ws [i].swhandle; swhandle.DangerousAddRef (ref refs [handleIdx]); handles [handleIdx++] = swhandle; } } #if NET_4_0 || MOBILE reg = cargs.CancellationToken.RegisterInternal (t => ((Thread) t).Interrupt (), Thread.CurrentThread); #endif int res = StInternalMethods.WaitMultiple_internal (IntPtr.Zero, handles, waitAll, cargs.Timeout); return res == WaitHandle.WaitTimeout ? StParkStatus.Success : StParkStatus.Timeout; #if NET_4_0 || MOBILE } catch (ThreadInterruptedException) { return cargs.CancellationToken.IsCancellationRequested ? StParkStatus.Cancelled : StParkStatus.Interrupted; #endif } finally { for (int i = 0; i < handles.Length; ++i) { if (refs [i]) { handles [i].DangerousRelease (); } } #if NET_4_0 || MOBILE reg.Dispose (); #endif } }
private int ParkMultiple (StWaitable[] waitables, int inflatedIndexes, int inflatedCount, StCancelArgs cargs) { var handles = new SafeWaitHandle[inflatedCount]; var refs = new bool[inflatedCount]; try { for (int i = 0, handleIdx = 0; i < waitables.Length; ++i) { if ((inflatedIndexes & (1 << i)) != 0) { var swhandle = waitables [i].swhandle; swhandle.DangerousAddRef (ref refs [handleIdx]); handles [handleIdx++] = swhandle; } } int res = StInternalMethods.WaitMultiple_internal (parkSpot, handles, false, cargs.Timeout); return res == WaitHandle.WaitTimeout ? StParkStatus.Success : StParkStatus.Timeout; } finally { for (int i = 0; i < handles.Length; ++i) { if (refs [i]) { handles [i].DangerousRelease (); } } } }
internal int Park (int spinCount, StWaitable[] waitables, int inflatedIndexes, int inflatedCount, StCancelArgs cargs) { #if NET_4_0 var spinWait = new SpinWait (); #endif do { if (state == 0) { return waitStatus; } #if NET_4_0 if (cargs.CancellationToken.IsCancellationRequested && TryCancel ()) { return StParkStatus.Cancelled; } #endif if (spinCount-- <= 0) { break; } #if NET_4_0 spinWait.SpinOnce (); #else Thread.SpinWait (1); #endif } while (true); RuntimeHelpers.PrepareConstrainedRegions (); try { StInternalMethods.Alloc_internal (ref parkSpot); if (parkSpot == IntPtr.Zero) { /* * FIXME: What's the right thing to do here? */ throw new OutOfMemoryException (); } if (!TestAndClearInProgress ()) { return waitStatus; } bool interrupted = false; #if NET_4_0 || MOBILE bool unregister = false; if (cargs.CancellationToken.CanBeCanceled && !(unregister = cargs.CancellationToken.cts.RegisterParker (this))) { if (TryCancel ()) { return StParkStatus.Cancelled; } cargs = StCancelArgs.None; } #endif int ws = inflatedCount == 0 ? ParkSingle (cargs, ref interrupted) : ParkMultiple (waitables, inflatedIndexes, inflatedCount, cargs); if (ws != StParkStatus.Success) { if (TryCancel ()) { UnparkSelf(ws > StParkStatus.Success ? GetWaitAnyIndex (inflatedIndexes, ws) : ws); } else { if (ws > StParkStatus.Success) { waitables[GetWaitAnyIndex (inflatedIndexes, ws)]._UndoAcquire (); } do { try { StInternalMethods.WaitForParkSpot_internal (parkSpot, Timeout.Infinite); break; } catch (Exception) { } } while (true); } } #if NET_4_0 if (unregister) { cargs.CancellationToken.cts.DeregisterParker (this); } #endif return waitStatus; } finally { if (parkSpot != IntPtr.Zero) { StInternalMethods.Free_internal (parkSpot); } } }