// // Constructor: registers a take with a blocking queue. // internal StRegisteredTake(StBlockingQueue <T> queue, StTakeCallback <T> callback, object cbState, int timeout, bool executeOnce) { // // Validate the arguments. // if (timeout == 0) { throw new ArgumentOutOfRangeException("\"timeout\" can not be zero"); } if (callback == null) { throw new ArgumentOutOfRangeException("\"callback\" must be specified"); } // // Initialize the registered take fields. // this.queue = queue; cbparker = new CbParker(UnparkCallback); if ((this.timeout = timeout) != Timeout.Infinite) { toTimer = new RawTimer(cbparker); } this.executeOnce = executeOnce; this.callback = callback; this.cbState = cbState; // // Execute the TryTakePrologue prologue on the queue. // waitNode = queue.TryTakePrologue(cbparker, StParkStatus.Success, out dataItem, ref hint); // // Set the state to active and enable the unpark callback. // state = ACTIVE; int ws; if ((ws = cbparker.EnableCallback(timeout, toTimer)) != StParkStatus.Pending) { // // The take operation was already accomplished. To prevent // uncontrolled reentrancy, the unpark callback is executed inline. // UnparkCallback(ws); } }
// // Exchanges a data item asynchronously. The specified callback gets called // when the exchange completes and is passed the received data item. // // TODO: Prevent unbounded reentrancy (hacked by queueing to the pool). // public ExchangeRegistration RegisterExchange(T myData, ExchangeCallback <T> callback, object state, int timeout) { if (timeout == 0) { throw new ArgumentOutOfRangeException("timeout", "The timeout can't be zero"); } if (callback == null) { throw new ArgumentNullException("callback"); } T yourData; if (TryExchange(myData, out yourData)) { ThreadPool.QueueUserWorkItem(_ => callback(state, yourData, false)); return(new ExchangeRegistration()); } state = state ?? this; WaitNode wn = null; var cbparker = new CbParker(ws => { if (ws != StParkStatus.Success && xchgPoint == wn) { Interlocked.CompareExchange(ref xchgPoint, null, wn); } if (ws != StParkStatus.WaitCancelled) { callback(state, wn.Channel, ws == StParkStatus.Timeout); } }); wn = new WaitNode(cbparker, myData); if (TryExchange(wn, myData, out yourData)) { ThreadPool.QueueUserWorkItem(_ => callback(state, yourData, false)); return(new ExchangeRegistration()); } var timer = timeout != Timeout.Infinite ? new RawTimer(cbparker) : null; int waitStatus = cbparker.EnableCallback(timeout, timer); if (waitStatus != StParkStatus.Pending) { ThreadPool.QueueUserWorkItem(_ => callback(state, wn.Channel, waitStatus == StParkStatus.Timeout)); return(new ExchangeRegistration()); } return(new ExchangeRegistration(cbparker)); }
// // Executes the unpark callback. // private void UnparkCallback(int ws) { Debug.Assert(ws == StParkStatus.Success || ws == StParkStatus.Timeout || ws == StParkStatus.TakeCancelled); // // If the registered take was cancelled, cancel the take attempt // and return immediately. // if (ws == StParkStatus.TakeCancelled) { queue.CancelTakeAttempt(waitNode, hint); return; } // // Set state to *our busy*, grabing the current state. // StParker myBusy = new SentinelParker(); StParker oldState = Interlocked.Exchange <StParker>(ref state, myBusy); do { // // If the take operation succeeded, execute the take epilogue; // otherwise, cancel the take attempt. // if (ws == StParkStatus.Success) { queue.TakeEpilogue(); } else { queue.CancelTakeAttempt(waitNode, hint); } // // Execute the user callback routine. // cbtid = Thread.CurrentThread.ManagedThreadId; callback(cbState, waitNode == null ? dataItem : waitNode.channel, ws == StParkStatus.Timeout); cbtid = 0; // // If the registered take 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 queue. // So, initialize the parker and execute the TakeAny prologue. // cbparker.Reset(); waitNode = queue.TryTakePrologue(cbparker, StParkStatus.Success, out dataItem, ref hint); // // Enable the unpark callback. // if ((ws = cbparker.EnableCallback(timeout, toTimer)) == StParkStatus.Pending) { // // If the *state* field is still *my busy* set it to ACTIVE; // anyway, return. // if (state == myBusy) { Interlocked.CompareExchange <StParker>(ref state, ACTIVE, myBusy); } return; } // // The take was already accomplished; so, to prevent uncontrolled // reentrancy execute the unpark callback inline. // } while (true); }
// // 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); } }
// // Executes the timer callback // internal void TimerCallback(int ws) { Debug.Assert(ws == StParkStatus.Timeout || ws == StParkStatus.TimerCancelled); // // If the timer was cancelled, return immediately. // if (ws == StParkStatus.TimerCancelled) { return; } // // Set timer state to *our busy* state, grabing the current state and // execute the timer callback processing. // SentinelParker myBusy = new SentinelParker(); StParker oldState = Interlocked.Exchange <StParker>(ref state, myBusy); do { // // Signals the timer's event. // tmrEvent.Signal(); // // Call the user-defined callback, if specified. // if (callback != null) { cbtid = Thread.CurrentThread.ManagedThreadId; callback(cbState, true); cbtid = 0; } // // If the timer isn't periodic or if someone is trying to // cancel it, process cancellation. // if (period == 0 || !(oldState is SentinelParker)) { if (!(oldState is SentinelParker)) { oldState.Unpark(StParkStatus.Success); } else { state = INACTIVE; } return; } // // Initialize the timer's parker. // cbparker.Reset(); // // Compute the timer delay and enable the unpark callback. // int timeout; if (useDueTime) { timeout = dueTime; useDueTime = false; } else { timeout = period | (1 << 31); } if ((ws = cbparker.EnableCallback(timeout, timer)) == StParkStatus.Pending) { if (state == myBusy) { Interlocked.CompareExchange <StParker>(ref state, ACTIVE, myBusy); } return; } // // The timer already expired. So, execute the timer // callback inline. // } while (true); }