Esempio n. 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);
		}
Esempio n. 2
0
		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;
		}