/// <summary> /// Update the current thread gate status, without changing the /// active count. /// </summary> /// <remarks> /// The caller must hold synchronization on the ThreadGate. /// </remarks> /// <param name="status"> /// The new status. /// </param> /// <returns> /// The old status. /// </returns> protected virtual ThreadGateState UpdateStatus(ThreadGateState status) { AtomicCounter atomicState = m_atomicState; long offsettedStatus = ((long)status) << STATUS_OFFSET; while (true) { long current = atomicState.GetCount(); long newValue = offsettedStatus | (current & ACTIVE_COUNT_MASK); if (atomicState.SetCount(current, newValue)) { return((ThreadGateState)(NumberUtils.URShift(current, STATUS_OFFSET))); } } }
public void CounterTest() { AtomicCounter counter = AtomicCounter.NewAtomicCounter(); Assert.AreEqual(0, counter.GetCount()); counter.Increment(); Assert.AreEqual(1, counter.GetCount()); counter.Increment(9); Assert.AreEqual(10, counter.GetCount()); counter.Decrement(); Assert.AreEqual(9, counter.GetCount()); counter.Decrement(4); Assert.AreEqual(5, counter.GetCount()); Assert.AreEqual(counter.PostDecrement(), 5); Assert.AreEqual(4, counter.GetCount()); Assert.AreEqual(4, counter.PostIncrement()); Assert.AreEqual(5, counter.GetCount()); counter = AtomicCounter.NewAtomicCounter(2006); Assert.AreEqual(2006, counter.SetCount(2007)); Assert.IsTrue(counter.SetCount(2007, 2006)); Assert.IsFalse(counter.SetCount(2000, 2008)); Assert.AreEqual(2006.ToString(), counter.ToString()); }
/// <summary> /// Enter the thread gate. /// </summary> /// <remarks> /// A thread uses this method to obtain non-exclusive access to the /// resource represented by the thread gate. Each invocation of this /// method must ultimately have a corresponding invocation of the /// Exit method. /// </remarks> /// <param name="millis"> /// Maximum number of milliseconds to wait; pass -1 for forever or 0 /// for no wait. /// </param> /// <returns> /// <b>true</b> iff the calling thread successfully entered the gate. /// </returns> public virtual bool Enter(long millis) { AtomicCounter atomicState = m_atomicState; if (IncrementThreadLocalCount(m_slotThreadEnterCount) > 1 || ClosingThread == Thread.CurrentThread) { // we were already in the gate, or are the one which has closed it // thus we must get in regardless of the state if ((atomicState.Increment() & ACTIVE_COUNT_MASK) > int.MaxValue) { // the gate has been entered way too many times, we must // have a cut-off somewhere, it is here as this could only // be possible if a thread keeps reentering the gate atomicState.Decrement(); DecrementThreadLocalCount(m_slotThreadEnterCount); throw new InvalidOperationException("The ThreadGate is full."); } // no need to check m_slotThreadEnterVersion, to get here we must be up to date return(true); } bool isSuccess = false; try { while (true) { long status = atomicState.GetCount(); switch ((ThreadGateState)(NumberUtils.URShift(status, STATUS_OFFSET))) { case ThreadGateState.Open: if (atomicState.SetCount(status, status + 1)) { // atomic set succeeded confirming that the gate // remained open and that we made it in long version = Version; if (version > GetThreadLocalCount(m_slotThreadEnterVersion)) { // the gate has been closed/opened since we // last entered, flush to get up to date Thread.MemoryBarrier(); SetThreadLocalCount(m_slotThreadEnterVersion, version); } return(isSuccess = true); } // we failed to atomically enter an open gate, which // can happen if either the gate closed just as we entered // or if another thread entered at the same time break; // retry case ThreadGateState.Closing: case ThreadGateState.Closed: // we know that we were not already in the gate, and are // not the one closing the gate; wait for it to open lock (this) { ThreadGateState state = Status; if (state == ThreadGateState.Closing || state == ThreadGateState.Closed) { // wait for the gate to open millis = (int)DoWait(millis); if (millis == 0L) { return(false); } } // version must be set from within sync since // we have not yet entered the gate. SetThreadLocalCount(m_slotThreadEnterVersion, Version); } break; // retry case ThreadGateState.Destroyed: DecrementThreadLocalCount(m_slotThreadEnterCount); throw new InvalidOperationException("ThreadGate.Enter: ThreadGate has been destroyed."); default: DecrementThreadLocalCount(m_slotThreadEnterCount); throw new InvalidOperationException("ThreadGate.Enter: ThreadGate has an invalid status. " + this); } } } finally { if (!isSuccess) { DecrementThreadLocalCount(m_slotThreadEnterCount); } } }
/// <summary> /// Close the thread gate. /// </summary> /// <remarks> /// A thread uses this method to obtain exclusive access to the /// resource represented by the thread gate. Each invocation of this /// method must ultimately have a corresponding invocation of the /// Open method. /// </remarks> /// <param name="millis"> /// Maximum number of milliseconds to wait; pass -1 for forever or 0 /// for no wait. /// </param> /// <returns> /// <b>true</b> iff entry into the thread gate was successfully /// barred by the calling thread and no other threads remain in the /// gate. /// </returns> public virtual bool Close(long millis) { Thread thread = Thread.CurrentThread; if (ClosingThread == thread && Status == ThreadGateState.Closed) { // we've already closed the gate CloseCount = CloseCount + 1; return(true); } AtomicCounter atomicState = m_atomicState; long enterCount = GetThreadLocalCount(m_slotThreadEnterCount); long statusReq = EMPTY_GATE_OPEN | enterCount; long statusEnd = EMPTY_GATE_CLOSED | enterCount; bool reenter = false; bool reopen = false; lock (this) { try { if (ClosingThread == thread) { statusReq = EMPTY_GATE_CLOSING; // if we've also "entered" we need to temporarily // decrement the counter so that the last thread to // exit the gate will know to notify us if (enterCount > 0) { reenter = true; atomicState.Decrement(enterCount); } } while (true) { if (atomicState.SetCount(statusReq, statusEnd)) { // we've closed the gate CloseCount = CloseCount + 1; ClosingThread = thread; // in case we bypassed ThreadGateState.Closing reenter = reopen = false; return(true); } else if (ClosingThread == null) { // transition to Closing state if (UpdateStatus(ThreadGateState.Closing) == ThreadGateState.Destroyed) { // oops gate was destroyed while we were waiting UpdateStatus(ThreadGateState.Destroyed); throw new InvalidOperationException("ThreadGate.Close: ThreadGate has been destroyed."); } ClosingThread = thread; statusReq = EMPTY_GATE_CLOSING; reopen = true; // reopen if we fail // if we've also "entered" we need to temporarily // decrement the counter so that the last thread to // exit the gate will know to notify us if (enterCount > 0) { reenter = true; atomicState.Decrement(enterCount); } // as we've just transititioned to CLOSING we must // retest the active count since exiting threads only // notify if they when in the state is CLOSING, thus // we can't go to DoWait without retesting continue; } // gate is closed or closing, wait for notification millis = (int)DoWait(millis); if (millis == 0) { return(false); } } } finally { // if we transititioned to closing but didn't make it to // closed; re-open the gate if (reenter) { atomicState.Increment(enterCount); // undo temporary decrement } if (reopen) { ClosingThread = null; UpdateStatus(ThreadGateState.Open); Monitor.PulseAll(this); } } } }