// // Adds a raw timer to the timer list. // private static void SetRawTimerWorker(RawTimer timer, int delay) { // // Set the next fire time. // timer.fireTime = baseTime + delay; // // Find the insertion position for the new timer. // RawTimer t = timerListHead.next; while (t != timerListHead) { if (delay < t.delay) { break; } delay -= t.delay; t = t.next; } // // Insert the new timer in the list, adjust the next timer and // redefine the delay sentinel. // timer.delay = delay; InsertTailTimerList(t, timer); if (t != timerListHead) { t.delay -= delay; } // // If the timer was inserted at front of the timer list we need // to wake the timer thread if it is blocked on its parker. // bool wake = (timerListHead.next == timer && parker.TryLock()); // // Release the timer list lock and unpark the timer thread, if needed. // _lock.Exit(); if (wake) { parker.Unpark(StParkStatus.Success); } }
// // Exits the lock. // public void Exit() { // // Since that atomic operations on references are more // expensive than on integers, we optimize the release when // the spin lock's wait queue is empty. However, when the queue // seems empty before the lock is released, but it's seen // non-empty after the lock is released, our algorithm resorts // to two atomic instructions. // if (top == null) { Interlocked.Exchange(ref state, FREE); if (top == null) { return; } } else { state = FREE; } // // Unpark all waiting threads. // // NOTE: Since that the spin lock's queue is implemented with // a stack, we build another stack in order to unpark the // waiting thread according to its arrival order. // StParker p = Interlocked.Exchange <StParker>(ref top, null); StParker ws = null, n; while (p != null) { n = p.pnext; p.pnext = ws; ws = p; p = n; } while (ws != null) { n = ws.pnext; ws.Unpark(StParkStatus.Success); ws = n; } }
// // Tries to unregister the callback. This method is thread-safe. // public bool Unregister() { StParker p = parker; if (p == null) { return(false); } parker = null; if (p.TryCancel()) { p.Unpark(StParkStatus.WaitCancelled); return(true); } return(false); }
// // Frees the lock and selects a candidate owner from the queue // of waiting threads. // public void Exit() { WaitBlock wb = head; bool unpark = false; StParker pk = null; while ((wb = wb.next) != null && wb.request != CANDIDATE) { pk = wb.parker; if (pk.TryLock()) { wb.request = CANDIDATE; unpark = true; break; } } state = FREE; if (unpark) { pk.Unpark(StParkStatus.Success); } }
// // 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); }
// // Tries to add immediatelly a data item to the queue. // public override bool TryAdd(T di) { // // If the queue is full, return failure immediatelly. // if (count == length) { return(false); } // // The queue seems non-full; so, acquire the queue's lock. // qlock.Enter(); // // When the queue's buffer has free slots, it can't have threads // blocked by the add operation; so, if there are waiters, they // were blocked by the take operation. // if (count < length) { if (!waitQueue.IsEmpty) { // // Try to deliver the data item directly to a waiting thread. // do { WaitNode w = waitQueue.Dequeue(); StParker pk = w.parker; if (pk.TryLock()) { // // Release the queue's lock, pass the data item through // the wait node, unpark the waiter thread and return // success. // qlock.Exit(); w.channel = di; pk.Unpark(w.waitKey); return(true); } } while (!waitQueue.IsEmpty); } // // There is at least a free slot on the queue; So, copy the // data item to the queue's buffer, unlock the queue and // return success. // items[tail] = di; if (++tail == length) { tail = 0; } count++; qlock.Exit(); return(true); } // // The queue's buffer is full, so return false. // qlock.Exit(); return(false); }
// // Executes the prologue of the BlockingQueue<T>.TryTake method. // internal override WaitNode TryTakePrologue(StParker pk, int key, out T di, ref WaitNode ignored) { // // Acquire the queue's lock and check if the queue's is empty. // qlock.Enter(); if (count != 0) { // // The queue isn't empty; so, ... // if (!pk.TryLock()) { qlock.Exit(); di = default(T); return(null); } pk.UnparkSelf(key); // // Retrieve a data item from the queue. // di = items[head]; if (++head == length) { head = 0; } count--; // // If the wait queue isn't empty, try to use the freed // slot to release one of the waiter threads. // if (!waitQueue.IsEmpty) { do { WaitNode w = waitQueue.Dequeue(); StParker pk2 = w.parker; if (pk2.TryLock()) { items[tail] = w.channel; if (++tail == length) { tail = 0; } count++; qlock.Exit(); pk2.Unpark(w.waitKey); return(null); } } while (!waitQueue.IsEmpty); } // // Release the queue's lock and return null to signal that the // take operation was accomplished. // qlock.Exit(); return(null); } // // The queue's buffer is empty; so, create ... // WaitNode wn = new WaitNode(pk, key); if (lifoQueue) { waitQueue.EnqueueHead(wn); } else { waitQueue.Enqueue(wn); } // // Release the queue's lock and return the wait node // inserted in the wait queue. // qlock.Exit(); di = default(T); return(wn); }
// // Tries to take immediately a data item from the queue. // public override bool TryTake(out T di) { // // If the queue seems empty, return failure. // if (count == 0) { di = default(T); return(false); } // // The queue seems non-empty; so, acquire the queue's lock. // qlock.Enter(); // // If the queue is now empty, release the queue's lock // and return failure. If it is actually empty, return failure. // if (count == 0) { qlock.Exit(); di = default(T); return(false); } // // Retrieve the next data item from the queue's buffer. // di = items[head]; if (++head == length) { head = 0; } count--; // // If the wait queue is empty, release the queue's lock and // return success. // if (waitQueue.IsEmpty) { qlock.Exit(); return(true); } // // There are threads blocked by the add operation. So, try to use // the freed buffer slot to release one of the waiter threads. // do { WaitNode w = waitQueue.Dequeue(); StParker pk = w.parker; if (pk.TryLock()) { items[tail] = w.channel; if (++tail == length) { tail = 0; } count++; qlock.Exit(); pk.Unpark(w.waitKey); return(true); } } while (!waitQueue.IsEmpty); // // Release the queue's lock and return success. // qlock.Exit(); return(true); }
// // Executes the prologue of the BlockingQueue<T>.TryAddXxx method. // internal override WaitNode TryAddPrologue(StParker pk, int key, T di, ref WaitNode ignored) { // // Acquire the queue's lock. // qlock.Enter(); // // ... // if (count < length) { if (!pk.TryLock()) { qlock.Exit(); return(null); } pk.UnparkSelf(key); // // If the wait queue isn't empty, try to deliver the data item // directly to a waiting thread. // if (!waitQueue.IsEmpty) { do { WaitNode w = waitQueue.Dequeue(); StParker wpk = w.parker; if (wpk.TryLock()) { // // Release the queue's lock, pass the data item through // the wait node, unpark the waiter thread and return // success. // qlock.Exit(); w.channel = di; wpk.Unpark(w.waitKey); return(null); } } while (!waitQueue.IsEmpty); } // // Add the data item to the non-full queue and return null. // items[tail] = di; if (++tail == length) { tail = 0; } count++; qlock.Exit(); return(null); } // // The queue's buffer is full, so, ... // WaitNode wn; waitQueue.Enqueue(wn = new WaitNode(pk, key, di)); // // Release the queue's lock and return the inserted wait block. // qlock.Exit(); return(wn); }
// // Releases the approprite waiters and unlocks the // r/w lock's queue. // private void ReleaseWaitersAndUnlockQueue() { do { WaitBlock qh = queue.head; WaitBlock w; while ((w = qh.next) != null) { StParker pk = w.parker; if (w.waitType == WaitType.WaitAny) { int r = (w.request & WaitBlock.MAX_REQUEST); if (r == ENTER_WRITE) { // // The next waiter is a writer, so try to enter the // write lock. // if (!TryEnterWriteQueuedInternal()) { break; } // // Try to lock the associated parker and, if succeed, unpark // its owner thread. // if (pk.TryLock() || w.request < 0) { pk.Unpark(w.waitKey); // // Since that no more waiters can be released, // advance the local queue's head and exit the // inner loop. // qh.next = qh; qh = w; break; } else { // // The acquire attempt was cancelled, so undo the // previous acquire. // UndoEnterWrite(); } } else { // // The next waiter is a reader, so try to acquire the // read lock. // if (!TryEnterReadQueuedInternal()) { break; } // // Try to lock the associated parker and, if succeed, unpark // its owner thread. // if (pk.TryLock() || w.request < 0) { pk.Unpark(w.waitKey); } else { // // The acquire attempt was cancelled, so undo the // previous acquire. // UndoEnterRead(); } } } else { // // WaitOne-all. // If the write lock is free, lock the parker and, if this is // the last cooperative release, unpark its owner thread. // if (state == 0) { if (pk.TryLock()) { pk.Unpark(w.waitKey); } } else { // // The write lock is busy, so exit the inner loop. // break; } } // // Advance the local queue's head, marking the wait block // referenced by the previous head as unlinked. // qh.next = qh; qh = w; } // // It seems that no more waiters can be released; so, set the // new queue's head and unlock the queue. // queue.SetHeadAndUnlock(qh); // // After release the queue's lock, if it seems that more waiters // can released, repeate the release processing. // if (!IsReleasePending) { return; } } 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); }
// // Only one thread act as a releaser at any given time. // private void ReleaseWaitersAndUnlockQueue(WaitBlock self) { do { WaitBlock qh = queue.head; WaitBlock 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); } } // // Remove the wait block from the semaphore's queue, // marking the previous head as unlinked, and advance // the local queues's head. // qh.next = qh; qh = w; } // // It seems that no more waiters can be released; so, // set the new semaphore queue's head and unlock it. // queue.SetHeadAndUnlock(qh); // // If after the semaphore's queue is unlocked, it seems that // more waiters can be released, repeat the release processing. // if (!IsReleasePending) { return; } } while (true); }
// // 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); }