//SpinWait 每20次会有一次系统时间片切换 //清理数据(挂的时候数据一致性是问题,全部删掉) //然后相当于获取锁往下执行 //测试发现Count=10w时,wait时间为5s private void TryEnterLock() { SpinWait wait = new SpinWait(); int head = *_head; int tail = *_tail; int count = 0; while (Interlocked.CompareExchange(ref *_lck, 1, 0) != 0) { wait.SpinOnce(); count++; if (head != *_head || tail != *_tail) { head = *_head; tail = *_tail; count = 0; } if (count > 100000) { Console.WriteLine("ClearData"); _ms.ClearData(); break; } } }
public void WaitAnyTest() { ParallelTestHelper.Repeat (delegate { int flag = 0; int finished = 0; InitWithDelegate(delegate { int times = Interlocked.Exchange (ref flag, 1); if (times == 1) { SpinWait sw = new SpinWait (); while (finished == 0) sw.SpinOnce (); } else { Interlocked.Increment (ref finished); } }); int index = Task.WaitAny(tasks); Assert.IsTrue (flag == 1, "#1"); Assert.AreEqual (1, finished, "#2"); Assert.AreNotEqual (-1, index, "#3"); Task.WaitAll (tasks); }); }
public static void ExclusiveLock(int lockId, Action method) { try { var spinWait = new SpinWait(); while (true) { int updVal = Interlocked.CompareExchange(ref currentlyHeldLockId, lockId, 0); if (0 != updVal) break; spinWait.SpinOnce(); } exclusiveLockActive = true; if (currentlyHeldLockId == lockId) method(); } finally { exclusiveLockActive = false; if (currentlyHeldLockId == lockId) currentlyHeldLockId = 0; } }
public void PerformSpinWait() { if (File.Exists(_fileName)) { File.Delete(_fileName); } System.Threading.SpinWait spinWait = new System.Threading.SpinWait(); System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); int limit = 1000; for (int count = 0; count < 10; count++) { for (int i = 0; i <= limit; i++) { if (i == 0) { Task.Run(() => WriteToFile(ref spinWait)); } spinWait.SpinOnce(); } } stopwatch.Start(); spinWait.SpinOnce(); stopwatch.Stop(); Console.WriteLine($"Ein Spin dauerte {stopwatch.Elapsed} sekunden. Insgesamt wurden {spinWait.Count - 1} spins benötigt."); DetermineExactSpinCount(); }
public void MeasureThroughput() { const int sendMessageCount = 2000000; var senderTransport = CreateAndStartZmqTransport("Abc.Testing.Sender"); var receivedMessageCount = 0; var receiverTransport = CreateAndStartZmqTransport("Abc.Testing.Receiver", _ => ++receivedMessageCount); var receivers = new[] { new Peer(receiverTransport.PeerId, receiverTransport.InboundEndPoint) }; var transportMessage = new FakeCommand(42).ToTransportMessage(); senderTransport.Send(transportMessage, receivers); var spinWait = new SpinWait(); while (receivedMessageCount != 1) spinWait.SpinOnce(); using (Measure.Throughput(sendMessageCount)) { for (var i = 0; i < sendMessageCount; ++i) { senderTransport.Send(transportMessage, receivers); } while (receivedMessageCount != sendMessageCount + 1) spinWait.SpinOnce(); } senderTransport.Stop(); receiverTransport.Stop(); }
public void WaitUntil(Func<bool> test) { SpinWait sw = new SpinWait(); int count = 0; do { sw.SpinOnce(); if (test()) return; } while (!sw.NextSpinWillYield || ++count < 4); int? effect = null; try { try {} finally { effect = Interlocked.Increment(ref _lockers); } lock (this) { while (!test()) Monitor.Wait(this); } } finally { if (effect.HasValue) Interlocked.Decrement(ref _lockers); } }
/// <summary> /// Wait Event State to Set /// </summary> public void Wait() { if(IsDebugEnabled) log.Debug("Wait event state to SET..."); var spinWait = new System.Threading.SpinWait(); var spinCount = 0; while(_state == UN_SET) { if(spinCount++ < SpinCount) { spinWait.SpinOnce(); if(_eventObj == null) { var mre = new ManualResetEvent(_state == SET); if(Interlocked.CompareExchange(ref _eventObj, mre, null) != null) { // If someone set the flag before seeing the new event object, we must ensure it's been set. if(_state == SET) _eventObj.Set(); // Lost the race w/ another thread. Just use its event. else mre.Close(); } if(_eventObj != null) _eventObj.WaitOne(); } } } }
public object Allocate() { Interlocked.Increment(ref AllocCount); #if (NET_2_0) Core.Utils.SpinWait spin = new Core.Utils.SpinWait(); #else System.Threading.SpinWait spin = new System.Threading.SpinWait(); #endif while (Interlocked.Increment(ref _allocatorNumber) != 1) { Interlocked.Decrement(ref _allocatorNumber); spin.SpinOnce(); } object obj; var succ = _pool.TryDequeue(out obj); Interlocked.Decrement(ref _allocatorNumber); if (!succ || obj == null) { Interlocked.Increment(ref NewCount); obj = _factory.MakeObject(); } _factory.ActivateObject(obj); return(obj); }
public bool ExecuteThreadSafeOnce() { if (state == Initialized) { return false; } var inProgressByThisThread = Thread.CurrentThread.ManagedThreadId; var preexistingState = Interlocked.CompareExchange(ref state, inProgressByThisThread, NotInitialized); if (preexistingState == NotInitialized) { return true; } if (preexistingState == Initialized || preexistingState == inProgressByThisThread) { return false; } #if DOTNET40 var spinWait = new SpinWait(); while (state != Initialized) { spinWait.SpinOnce(); } #else while (state != Initialized) { Thread.SpinWait(5); } #endif return false; }
// there may be some leak while increasing cap // but it's ok... public void Free(object t) { if (t == null) { return; } _factory.DestroyObject(t); Interlocked.Increment(ref FreeCount); if (_pool.Count < _pool.Capacity - 5) { #if NET_2_0 Core.Utils.SpinWait spin = new Core.Utils.SpinWait(); #else System.Threading.SpinWait spin = new System.Threading.SpinWait(); #endif while (Interlocked.Increment(ref _freeNumber) != 1) { Interlocked.Decrement(ref _freeNumber); spin.SpinOnce(); } _pool.Enqueue(t); Interlocked.Decrement(ref _freeNumber); } else { RingBuffer <object> old = _pool; _pool = new RingBuffer <object>(old.Capacity * 2); Debug.Print("ring buffer not big enough for object pool of type {0}", _factory.MakeObject().GetType()); } }
public bool EnterWriteLock(bool block) { var start = HiResTimer.Now(); #if DEBUG if (m_lastWriteThreadID == Thread.CurrentThread.ManagedThreadId) throw new SynchronizationLockException("Write lock already owned by you"); #endif var sw = new SpinWait(); do { // If there are no readers or writers, grab the write lock. var state = m_state; if (state == 0 && Interlocked.CompareExchange(ref m_state, MASK_WRITER_BIT, state) == state) { #if DEBUG m_lastWriteThreadID = Thread.CurrentThread.ManagedThreadId; if (sw.Count > _spinThreshold) Debug.Print("EnterWriteLock: {0} spins ({1:F1}ms)", sw.Count, HiResTimer.ToTimeSpan(HiResTimer.Now() - start).TotalMilliseconds); #endif #if DEBUG_ReaderWriterSpinLock if (ID != 0) Debug.Print("{0} Enter Write on #{1} ({2})", Thread.CurrentThread.ManagedThreadId, ID, m_lastWriteThreadID); #endif return true; } sw.SpinOnce(); } while (block); return false; }
public static void RunSpinWaitTests() { SpinWait spinner = new SpinWait(); spinner.SpinOnce(); Assert.Equal(spinner.Count, 1); }
protected void InternalSend(ISocketSession session, IList<ArraySegment<byte>> segments) { if (!session.CanSend()) return; if (InternalTrySend(session, segments)) return; var sendTimeOut = m_SendTimeOut; //Don't retry, timeout directly if (sendTimeOut < 0) { throw new TimeoutException("The sending attempt timed out"); } var timeOutTime = sendTimeOut > 0 ? DateTime.Now.AddMilliseconds(sendTimeOut) : DateTime.Now; var spinWait = new SpinWait(); while (session.CanSend()) { spinWait.SpinOnce(); if (InternalTrySend(session, segments)) return; //If sendTimeOut = 0, don't have timeout check if (sendTimeOut > 0 && DateTime.Now >= timeOutTime) { throw new TimeoutException("The sending attempt timed out"); } } }
// Demonstrates: // SpinWait construction // SpinWait.SpinOnce() // SpinWait.NextSpinWillYield // SpinWait.Count static void Main(){ bool someBoolean = false; int numYields = 0; // First task: SpinWait until someBoolean is set to true Task t1 = Task.Factory.StartNew(() => { SpinWait sw = new SpinWait(); while (!someBoolean) { // The NextSpinWillYield property returns true if // calling sw.SpinOnce() will result in yielding the // processor instead of simply spinning. if (sw.NextSpinWillYield) numYields++; sw.SpinOnce(); } // As of .NET Framework 4: After some initial spinning, // SpinWait.SpinOnce() will yield every time. Console.WriteLine("SpinWait called {0} times, yielded {1} times", sw.Count, numYields); }); // Second task: Wait 100ms, then set someBoolean to true Task t2 = Task.Factory.StartNew(() => { Thread.Sleep(100); someBoolean = true; }); // Wait for tasks to complete Task.WaitAll(t1, t2); }
public SpinLock (bool trackId) { this.isThreadOwnerTrackingEnabled = trackId; this.threadWhoTookLock = 0; this.lockState = isFree; this.sw = new SpinWait(); }
/// <summary> /// Wait for the given sequence to be available with a timeout specified. /// </summary> /// <param name="sequence">sequence to be waited on.</param> /// <param name="cursor">cursor on which to wait.</param> /// <param name="dependents">dependents further back the chain that must advance first</param> /// <param name="barrier">barrier the processor is waiting on.</param> /// <param name="timeout">timeout value to abort after.</param> /// <returns>the sequence that is available which may be greater than the requested sequence.</returns> /// <exception cref="AlertException">AlertException if the status of the Disruptor has changed.</exception> public long WaitFor(long sequence, Sequence cursor, Sequence[] dependents, ISequenceBarrier barrier, TimeSpan timeout) { long availableSequence; var spinWait = new SpinWait(); var sw = Stopwatch.StartNew(); if (dependents.Length == 0) { while ((availableSequence = cursor.Value) < sequence) // volatile read { barrier.CheckAlert(); spinWait.SpinOnce(); if (sw.Elapsed > timeout) { break; } } } else { while ((availableSequence = Util.GetMinimumSequence(dependents)) < sequence) { barrier.CheckAlert(); spinWait.SpinOnce(); if (sw.Elapsed > timeout) { break; } } } return availableSequence; }
public void Enter() { //如果调用线程已拥有锁,递增递归计数并返回 Int32 threadId = Thread.CurrentThread.ManagedThreadId; if (threadId == m_owningThreadId) { m_recursion++; return; } //调用线程不拥有锁,尝试获取它 SpinWait spinwait = new SpinWait(); for (Int32 spincount = 0; spincount < m_spincount; spincount++) { //如果锁可以自由使用,这个线程就获得它:设置一些状态并返回 if (Interlocked.CompareExchange(ref m_waiters, 1, 0) == 0) goto GotLock; //Black magic:给其它线程运行的机会,希望锁会释放。 spinwait.SpinOnce(); } //自旋(Spinning)结束,锁仍然没有获得,再试一次 if (Interlocked.Increment(ref m_waiters) > 1) { //其它线程阻塞,这个线程也必须阻塞 m_waiterlock.WaitOne();//等待锁:性能损失 //等这个线程醒来时,它拥有锁:设置一些状态并返回。 } GotLock: //一个线程拥有锁时,我们记录它的ID,并 //指出线程拥有锁一次 m_owningThreadId = threadId; m_recursion = 1; }
public void StartNext(CompletionLink next) { var spinner = new SpinWait(); while (!current._RestartWith(next)) { spinner.SpinOnce(); } next._Start(); current = next; }
public void EnterWriteLock() { var w = new SpinWait(); while (0 != Interlocked.CompareExchange(ref _lock, _writerLock, 0)) { w.SpinOnce(); } }
public void Wait() { SpinWait wait = new SpinWait(); wait.SpinOnce(); while (Count > 0) Thread.Sleep(1); }
protected void WaitForStatus() { if (GetIsCompleted()) return; SpinWait wait = new SpinWait(); while (!GetIsCompleted()) wait.SpinOnce(); }
/// <summary> /// read lock must be aquired prior to calling this!!! we do not check it !!!! /// </summary> public void UpgradeToWrite() { var w = new SpinWait(); while (1 != Interlocked.CompareExchange(ref _lock, _writerLock + 1, 1)) { w.SpinOnce(); } }
public bool TryAdd(T item) { item.ShouldNotBeDefault("item"); if (IsDebugEnabled) { log.Debug("요소 추가를 시도합니다... item=[{0}]", item); } CleanArrays(); var topLayer = GetRandomLevel(ref _randomSeed); var v = @getKey.Invoke(item); while (true) { var found = FindNode(v, _preds, _succs); if (found != -1) { var nodeFound = _succs[found]; if (nodeFound.Marked == false) { var sw = new System.Threading.SpinWait(); while (nodeFound.FullyLinked == false) { sw.SpinOnce(); } return(false); } continue; } var highestLocked = -1; try { var valid = LockNodes(topLayer, ref highestLocked, (layer, pred, succ) => !pred.Marked && !succ.Marked && pred.Nexts[layer] == succ); if (!valid) { continue; } var newNode = new Node(v, item, topLayer); for (var layer = 0; layer <= topLayer; layer++) { newNode.Nexts[layer] = _succs[layer]; _preds[layer].Nexts[layer] = newNode; } newNode.FullyLinked = true; } finally { Unlock(_preds, highestLocked); } Interlocked.Increment(ref _count); return(true); } }
public long AddParticipants(int participantCount) { this.ThrowIfDisposed(); if (participantCount < 1) { throw new ArgumentOutOfRangeException("participantCount", participantCount, SR.GetString("Barrier_AddParticipants_NonPositive_ArgumentOutOfRange")); } if (participantCount > 0x7fff) { throw new ArgumentOutOfRangeException("participantCount", SR.GetString("Barrier_AddParticipants_Overflow_ArgumentOutOfRange")); } if ((this.m_actionCallerID != 0) && (Thread.CurrentThread.ManagedThreadId == this.m_actionCallerID)) { throw new InvalidOperationException(SR.GetString("Barrier_InvalidOperation_CalledFromPHA")); } SpinWait wait = new SpinWait(); long num = 0L; while (true) { int num3; int num4; bool flag; int currentTotalCount = this.m_currentTotalCount; this.GetCurrentTotal(currentTotalCount, out num4, out num3, out flag); if ((participantCount + num3) > 0x7fff) { throw new ArgumentOutOfRangeException("participantCount", SR.GetString("Barrier_AddParticipants_Overflow_ArgumentOutOfRange")); } if (this.SetCurrentTotal(currentTotalCount, num4, num3 + participantCount, flag)) { long currentPhase = this.m_currentPhase; num = (flag != ((currentPhase % 2L) == 0L)) ? (currentPhase + 1L) : currentPhase; if (num != currentPhase) { if (flag) { this.m_oddEvent.Wait(); return num; } this.m_evenEvent.Wait(); return num; } if (flag && this.m_evenEvent.IsSet) { this.m_evenEvent.Reset(); return num; } if (!flag && this.m_oddEvent.IsSet) { this.m_oddEvent.Reset(); } return num; } wait.SpinOnce(); } }
public void Spin(int x) { Benchmark.Iterate(() => { for (var spin = new SpinWait(); spin.Count < x; spin.SpinOnce()) { } }); }
private void UpdateStateAtomically(int newBits, int updateBitsMask) { SpinWait sw = new SpinWait(); do { int state = m_combinedState; int newState = (state & ~updateBitsMask) | newBits; if (Interlocked.CompareExchange(ref m_combinedState, newState, state) == state) return; sw.SpinOnce(); } while (true); }
private void EndlessLoop(CancellationToken token) { var wait = new System.Threading.SpinWait(); while (!token.IsCancellationRequested) { //do smth wait.SpinOnce(100); } }
public void EnterReadLock() { var w = new SpinWait(); var tmpLock = _lock; while (tmpLock >= _writerLock || tmpLock != Interlocked.CompareExchange(ref _lock, tmpLock + 1, tmpLock)) { w.SpinOnce(); tmpLock = _lock; } }
/// <summary> /// Increments a value whilst keeping it inside a range. /// </summary> /// <param name="value">The value.</param> /// <param name="start">The start of the range (inclusive).</param> /// <param name="end">The end of the range (inclusive).</param> /// <returns>The incremented result.</returns> public static int Increment(ref int value, int start, int end) { SpinWait spinWait = new SpinWait(); do { int v = value; if (Interlocked.CompareExchange(ref value, v >= end ? start : v + 1, v) == v) return v; spinWait.SpinOnce(); } while (true); }
public static bool SpinUntil(Func<bool> condition, int millisecondsTimeout) { SpinWait sw = new SpinWait(); Watch watch = Watch.StartNew(); while (!condition()) { if (watch.ElapsedMilliseconds > millisecondsTimeout) return false; sw.SpinOnce(); } return true; }
private bool TryCompletion(){ SpinWait sw = new SpinWait(); do { int p; if ((p = _permits) == 0) return false; if (Interlocked.CompareExchange(ref _permits, p - 1, p) == p) return true; sw.SpinOnce(); } while (true); }
public void Enter() { SpinWait sw = new SpinWait(); do { if (state == FREE && Interlocked.Exchange(ref state, BUSY) == BUSY) { return; } do { sw.SpinOnce(); } while (state == BUSY); } while (true); }
private void run(CancellationToken token) { using (var gateway = _gatewayFactory()) { var spin = new SpinWait(); try { Log.DebugFormat("[RequestProcessor] Entering mainloop. Thread Id: {0}", Thread.CurrentThread.ManagedThreadId); while (token.IsCancellationRequested == false) { bool processed = gateway.ProcessResponse(); PendingResRequest pendingResRequest; while (_buffer.TryDequeue(out pendingResRequest)) { if (pendingResRequest.ShouldDrop()) { pendingResRequest.Drop(); continue; } gateway.SendRequest(pendingResRequest); processed = true; break; } if (!processed) spin.SpinOnce(); } Log.DebugFormat("[RequestProcessor] Exiting mainloop. Thread ID: {0}", Thread.CurrentThread.ManagedThreadId); } catch (Exception e) { Log.DebugFormat("[RequestProcessor] Error in mainloop. Thread ID: {0}", e, Thread.CurrentThread.ManagedThreadId); } finally { Log.DebugFormat("[RequestProcessor] Shutting down gateway. Thread ID: {0}", Thread.CurrentThread.ManagedThreadId); gateway.Dispose(); Log.DebugFormat("[RequestProcessor] Gateway shutdown. Thread ID: {0}", Thread.CurrentThread.ManagedThreadId); } } }
public WaitGroupEventEnum WaitAny( long microsecondsToAwake ) { var result = WaitGroupEventEnum.WaitTimeout; var spinWait = new SpinWait(); while (true) { var stopRaised = Interlocked.Read(ref _stopRaised); if (stopRaised > 0L) { //stopRaised - is a 'manual' event //no decrement at all! result = WaitGroupEventEnum.Stop; break; } //restartRaised - is an 'auto' event , so we need to 'reset' the event var restartRaised = Interlocked.Exchange(ref _restartRaised, 0L); if (restartRaised > 0L) { result = WaitGroupEventEnum.Restart; break; } //если ждать не надо, сразу выходим if (microsecondsToAwake == 0) { break; } //проверяем не истек ли таймаут только когда таймаут задан if (microsecondsToAwake > 0) { if (microsecondsToAwake <= _performanceTimer.MicroSeconds) { //сработал таймаут break; } } spinWait.SpinOnce(); } return result; }
public static bool SpinUntil (Func<bool> predicate, int milliseconds) { SpinWait sw = new SpinWait (); Watch watch = Watch.StartNew (); while (!predicate ()) { if (watch.ElapsedMilliseconds > milliseconds) return false; sw.SpinOnce (); } return true; }
private IEnumerable <T> GetAllItems() { Node curr = _leftSentinel; while ((curr = curr.Nexts[0]) != _rightSentinel) { var sw = new System.Threading.SpinWait(); while (curr.FullyLinked == false) { sw.SpinOnce(); } yield return(curr.Value); } }
// there may be some leak while increasing cap // but it's ok... public void Free(object t) { if (t == null) { return; } _factory.DestroyObject(t); Interlocked.Increment(ref FreeCount); #if (NET_4_6 && UNITY_2017) System.Threading.SpinWait spin = new System.Threading.SpinWait(); #else Core.Utils.SpinWait spin = new Core.Utils.SpinWait(); #endif while (Interlocked.Increment(ref _freeNumber) != 1) { Interlocked.Decrement(ref _freeNumber); spin.SpinOnce(); } if (_pool.Count > _pool.Capacity - 5) { _old = _pool; _pool = new RingBuffer <object>(_old.Capacity * 2); _logger.InfoFormat("ring buffer not big enough for object pool of type {0}", _factory.MakeObject().GetType()); if (_old.Count > 0) { object obj; var succ = _old.TryDequeue(out obj); while (succ && obj != null) { _pool.Enqueue(obj); succ = _old.TryDequeue(out obj); } } _old = null; } { _pool.Enqueue(t); Interlocked.Decrement(ref _freeNumber); } }
// Helper function to send an entire test sequence fast (no RX, no open/close of serial) //public void Send_test_sequence_fast(byte[] tx_buf, SerialPort fpga_com_port) //{ // Open Serial Port //SerialPort fpga_com_port = setup_serial_port(); // Send Data //Send_serial_data_turbo(tx_buf, fpga_com_port); // Rx Reply //string rx_string = Read_serial_data(fpga_com_port); // Print //Debug.WriteLine("Write: 0x" + tx_string); //Debug.WriteLine("Read: 0x" + rx_string); //Debug.WriteLine("Data Equal: " + tx_string.Equals(rx_string)); // Close Serial Port //close_serial_port(fpga_com_port); //} // Helper function allow us to sleep with microsecond resolution but burns through CPU clock cycles as spin-locks rather than sleeps // Needs this as Windows scheduler only works at the 13ms resolution // This function shall allow us to specify a microsecond and achieve timing with max error of about 30 uS overshoot (worst-case seen in practice) // Perfect for our application where we want timing accurate to about 0.1mS public void high_resolution_sleep_us(UInt32 wait_time_in_microseconds) { //// Make sure we are using the High Performance Stopwatch //if (Stopwatch.IsHighResolution) //{ // Console.WriteLine("Operations timed using the system's high-resolution performance counter."); //} //else //{ // Console.WriteLine("Operations timed using the DateTime class."); //} // Where we are now Stopwatch my_stopwatch = Stopwatch.StartNew(); //long starting_timestamp = Stopwatch.GetTimestamp(); // Where we want to get to, we count this using ticks rather than timestamps as more precise and its how the API works long nano_sec_per_tick = (1000L * 1000L * 1000L) / Stopwatch.Frequency; // Ony my PC, observed that this was 100 nanosec/tick => Our timer has a 10 MHz tick rate, nice, plenty for microsecond resolution long ticks_to_wait = (wait_time_in_microseconds * 1000L) / nano_sec_per_tick; long starting_ticks = my_stopwatch.ElapsedTicks; long target_ticks = starting_ticks + ticks_to_wait; // Use a spin-lock structure for no-ops to get our timing - .Net way of doing this SpinWait spin_locker = new System.Threading.SpinWait(); long tick_count = my_stopwatch.ElapsedTicks; while (tick_count < target_ticks) { // Spin-Lock until we have reached our tips target tick_count = my_stopwatch.ElapsedTicks; spin_locker.SpinOnce(); // Want't good timing, so reset spin_locker before it goes into a longer sleep if (spin_locker.NextSpinWillYield) { spin_locker.Reset(); } //Console.WriteLine("Ticks: " + tick_count); } //spin_locker( )1 // Now check how long it took for debugging long ending_ticks = my_stopwatch.ElapsedTicks; long elapsed_ticks = ending_ticks - starting_ticks; //double ElapsedSeconds = elapsed_ticks * (1.0 / Stopwatch.Frequency); double ElapsedMicroSeconds = elapsed_ticks * (nano_sec_per_tick / 1000.0); // ORdered like this to not lose precision due to truncations //Console.WriteLine("Elapsed Microseconds: " + ElapsedMicroSeconds); }
/// <summary> /// 최상위 요소를 스택에서 꺼냅니다. /// </summary> /// <returns></returns> public T Pop() { if (IsDebugEnabled) { log.Debug("최상위 요소를 스택에서 꺼냅니다..."); } StackNode <T> current; var spinWait = new System.Threading.SpinWait(); while (true) { StackNode <T> next; do { current = _head; if (current == null) { break; } next = current.Next; #pragma warning disable 0420 } while(_head != current || Interlocked.CompareExchange(ref _head, next, current) != current); #pragma warning restore 0420 if (current != null) { break; } spinWait.SpinOnce(); } if (IsDebugEnabled) { log.Debug("스택에서 요소를 꺼냈습니다. item=[{0}]", current.Item); } return(current.Item); }
private void DetermineExactSpinCount() { System.Threading.SpinWait spin = new System.Threading.SpinWait(); int limit = 0; while (true) { try { for (int count = 0; count < 100; count++) { for (int i = 0; i <= limit; i++) { if (i == 0) { Task.Run(() => { try { WriteToFile(ref spin); } catch (IOException) { limit++; } }); } spin.SpinOnce(); } } } catch (IOException) { limit++; } break; } Console.WriteLine($"Es braucht min {spin.Count} spins um eine IOException zu verhindern."); }
/// <summary> /// Try acquire the lock with long path, this is usually called after the first path in Enter and /// TryEnter failed The reason for short path is to make it inline in the run time which improves the /// performance. This method assumed that the parameter are validated in Enter ir TryENter method /// </summary> /// <param name="millisecondsTimeout">The timeout milliseconds</param> /// <param name="lockTaken">The lockTaken param</param> private void ContinueTryEnter(int millisecondsTimeout, ref bool lockTaken) { // The fast path doesn't throw any exception, so we have to validate the parameters here if (lockTaken) { lockTaken = false; throw new System.ArgumentException(SR.SpinLock_TryReliableEnter_ArgumentException); } if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException( "millisecondsTimeout", millisecondsTimeout, SR.SpinLock_TryEnter_ArgumentOutOfRange); } uint startTime = 0; if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout != 0) { startTime = TimeoutHelper.GetTime(); } if (CdsSyncEtwBCLProvider.Log.IsEnabled()) { CdsSyncEtwBCLProvider.Log.SpinLock_FastPathFailed(m_owner); } if (IsThreadOwnerTrackingEnabled) { // Slow path for enabled thread tracking mode ContinueTryEnterWithThreadTracking(millisecondsTimeout, startTime, ref lockTaken); return; } // then thread tracking is disabled // In this case there are three ways to acquire the lock // 1- the first way the thread either tries to get the lock if it's free or updates the waiters, if the turn >= the processors count then go to 3 else go to 2 // 2- In this step the waiter threads spins and tries to acquire the lock, the number of spin iterations and spin count is dependent on the thread turn // the late the thread arrives the more it spins and less frequent it check the lock avilability // Also the spins count is increases each iteration // If the spins iterations finished and failed to acquire the lock, go to step 3 // 3- This is the yielding step, there are two ways of yielding Thread.Yield and Sleep(1) // If the timeout is expired in after step 1, we need to decrement the waiters count before returning int observedOwner; int turn = int.MaxValue; //***Step 1, take the lock or update the waiters // try to acquire the lock directly if possible or update the waiters count observedOwner = m_owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { if (CompareExchange(ref m_owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner) { return; } } else //failed to acquire the lock,then try to update the waiters. If the waiters count reached the maximum, jsut break the loop to avoid overflow { if ((observedOwner & WAITERS_MASK) != MAXIMUM_WAITERS) { turn = (Interlocked.Add(ref m_owner, 2) & WAITERS_MASK) >> 1; } } // Check the timeout. if (millisecondsTimeout == 0 || (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0)) { DecrementWaiters(); return; } //***Step 2. Spinning //lock acquired failed and waiters updated int processorCount = PlatformHelper.ProcessorCount; if (turn < processorCount) { int processFactor = 1; for (int i = 1; i <= turn * SPINNING_FACTOR; i++) { SpinWait.Spin((turn + i) * SPINNING_FACTOR * processFactor); if (processFactor < processorCount) { processFactor++; } observedOwner = m_owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero observedOwner | 1 // don't decrement it. just set the lock bit, it is zzero because a previous call of Exit(false) ehich corrupted the waiters : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit Contract.Assert((newOwner & WAITERS_MASK) >= 0); if (CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) { return; } } } } // Check the timeout. if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0) { DecrementWaiters(); return; } //*** Step 3, Yielding //Sleep(1) every 50 yields int yieldsoFar = 0; while (true) { observedOwner = m_owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero observedOwner | 1 // don't decrement it. just set the lock bit, it is zzero because a previous call of Exit(false) ehich corrupted the waiters : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit Contract.Assert((newOwner & WAITERS_MASK) >= 0); if (CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) { return; } } if (yieldsoFar % SLEEP_ONE_FREQUENCY == 0) { Helpers.Sleep(1); } else if (yieldsoFar % SLEEP_ZERO_FREQUENCY == 0) { Helpers.Sleep(0); } else { SpinWait.Yield(); } if (yieldsoFar % TIMEOUT_CHECK_FREQUENCY == 0) { //Check the timeout. if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0) { DecrementWaiters(); return; } } yieldsoFar++; } }
private ManualResetEvent GetOrCreateWaitHandle() { // At the end of this method: _status will be (int)Status.HandleCreated or ObjectDisposedException is thrown var spinWait = new SpinWait(); while (true) { var status = (Status)Volatile.Read(ref _status); switch (status) { case Status.Disposed: // Disposed throw new ObjectDisposedException(nameof(ManualResetEventSlim)); case Status.NotSet: // Indicate we will be creating the handle status = (Status)Interlocked.CompareExchange(ref _status, (int)Status.HandleRequestedNotSet, (int)status); if (status == Status.NotSet) { // Create the handle var created = new ManualResetEvent(false); // Set the handle Volatile.Write(ref _handle, created); // Notify that the handle is ready Volatile.Write(ref _status, (int)Status.HandleReadyNotSet); // Return the handle we created return(created); } // Must has been disposed, or another thread is creating the handle break; case Status.Set: // Indicate we will be creating the handle status = (Status)Interlocked.CompareExchange(ref _status, (int)Status.HandleRequestedSet, (int)status); if (status == Status.Set) { // Create the handle var created = new ManualResetEvent(true); // Set the handle Volatile.Write(ref _handle, created); // Notify that the handle is ready Volatile.Write(ref _status, (int)Status.HandleReadySet); // Return the handle we created return(created); } // Must has been disposed, or another thread is creating the handle break; case Status.HandleRequestedNotSet: case Status.HandleRequestedSet: // Another thread is creating the wait handle // SpinWait break; case Status.HandleReadyNotSet: case Status.HandleReadySet: // The handle already exists // Get the handle that is already created var handle = Volatile.Read(ref _handle); if (handle != null) { // Return it return(handle); } // Probably Disposed break; default: // Should not happen break; } spinWait.SpinOnce(); } }
public void Set() { var spinWait = new SpinWait(); while (true) { var status = (Status)Volatile.Read(ref _status); switch (status) { case Status.Disposed: // Disposed // Fail saliently return; case Status.NotSet: // Set if Reset status = (Status)Interlocked.CompareExchange(ref _status, (int)Status.Set, (int)Status.NotSet); if (status == Status.NotSet || status == Status.Set) { // We Set it or it was already Set // Either way, we are done return; } // Must has been disposed, or the wait handle requested break; case Status.HandleRequestedNotSet: // Another thread is creating the wait handle // SpinWait break; case Status.HandleReadyNotSet: // Set if Reset status = (Status)Interlocked.CompareExchange(ref _status, (int)Status.HandleReadySet, (int)Status.HandleReadyNotSet); switch (status) { case Status.HandleReadyNotSet: { // We set it // Update the wait handle var handle = Volatile.Read(ref _handle); if (handle != null) { // Reset it handle.Set(); // Done return; } break; } case Status.HandleReadySet: // Another thread set it // we are done return; default: break; } // Probably Disposed break; case Status.Set: case Status.HandleRequestedSet: case Status.HandleReadySet: // Nothing to do return; default: // Should not happen break; } spinWait.SpinOnce(); } }
private void ContinueTryEnter(int millisecondsTimeout, ref bool lockTaken) { int owner; long startTicks = 0L; if ((millisecondsTimeout != -1) && (millisecondsTimeout != 0)) { startTicks = DateTime.UtcNow.Ticks; } if (CdsSyncEtwBCLProvider.Log.IsEnabled()) { CdsSyncEtwBCLProvider.Log.SpinLock_FastPathFailed(this.m_owner); } if (this.IsThreadOwnerTrackingEnabled) { this.ContinueTryEnterWithThreadTracking(millisecondsTimeout, startTicks, ref lockTaken); return; } SpinWait wait = new SpinWait(); while (true) { owner = this.m_owner; if ((owner & 1) == 0) { Thread.BeginCriticalRegion(); if (Interlocked.CompareExchange(ref this.m_owner, owner | 1, owner, ref lockTaken) == owner) { return; } Thread.EndCriticalRegion(); } else if (((owner & 0x7ffffffe) == MAXIMUM_WAITERS) || (Interlocked.CompareExchange(ref this.m_owner, owner + 2, owner) == owner)) { break; } wait.SpinOnce(); } if ((millisecondsTimeout == 0) || ((millisecondsTimeout != -1) && TimeoutExpired(startTicks, millisecondsTimeout))) { this.DecrementWaiters(); return; } int num3 = ((owner + 2) & 0x7ffffffe) / 2; int processorCount = PlatformHelper.ProcessorCount; if (num3 < processorCount) { int num5 = 1; for (int i = 1; i <= (num3 * 100); i++) { Thread.SpinWait(((num3 + i) * 100) * num5); if (num5 < processorCount) { num5++; } owner = this.m_owner; if ((owner & 1) == 0) { Thread.BeginCriticalRegion(); int num7 = ((owner & 0x7ffffffe) == 0) ? (owner | 1) : ((owner - 2) | 1); if (Interlocked.CompareExchange(ref this.m_owner, num7, owner, ref lockTaken) == owner) { return; } Thread.EndCriticalRegion(); } } } if ((millisecondsTimeout != -1) && TimeoutExpired(startTicks, millisecondsTimeout)) { this.DecrementWaiters(); return; } int num8 = 0; Label_015F: owner = this.m_owner; if ((owner & 1) == 0) { Thread.BeginCriticalRegion(); int num9 = ((owner & 0x7ffffffe) == 0) ? (owner | 1) : ((owner - 2) | 1); if (Interlocked.CompareExchange(ref this.m_owner, num9, owner, ref lockTaken) == owner) { return; } Thread.EndCriticalRegion(); } if ((num8 % 40) == 0) { Thread.Sleep(1); } else if ((num8 % 10) == 0) { Thread.Sleep(0); } else { Thread.Yield(); } if ((((num8 % 10) == 0) && (millisecondsTimeout != -1)) && TimeoutExpired(startTicks, millisecondsTimeout)) { this.DecrementWaiters(); } else { num8++; goto Label_015F; } }
/// <summary> /// Notifies the <see cref="Barrier"/> that there will be additional participants. /// </summary> /// <param name="participantCount">The number of additional participants to add to the /// barrier.</param> /// <returns>The phase number of the barrier in which the new participants will first /// participate.</returns> /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="participantCount"/> is less than /// 0.</exception> /// <exception cref="T:System.ArgumentOutOfRangeException">Adding <paramref name="participantCount"/> participants would cause the /// barrier's participant count to exceed <see cref="T:System.Int16.MaxValue"/>.</exception> /// <exception cref="T:System.InvalidOperationException"> /// The method was invoked from within a post-phase action. /// </exception> /// <exception cref="T:System.ObjectDisposedException">The current instance has already been /// disposed.</exception> public long AddParticipants(int participantCount) { // check dispose ThrowIfDisposed(); if (participantCount < 1) { throw new ArgumentOutOfRangeException("participantCount", participantCount, "The participantCount argument must be a positive value."); } else if (participantCount > Max_Participants) //overflow { throw new ArgumentOutOfRangeException("participantCount", "Adding participantCount participants would result in the number of participants exceeding the maximum number allowed."); } // in case of this is called from the PHA if (_actionCallerId != 0 && Thread.CurrentThread.ManagedThreadId == _actionCallerId) { throw new InvalidOperationException("This method may not be called from within the postPhaseAction."); } var spinner = new SpinWait(); long newPhase; while (true) { var currentTotal = Thread.VolatileRead(ref _currentTotalCount); int total; int current; bool sense; GetCurrentTotal(currentTotal, out current, out total, out sense); if (participantCount + total > Max_Participants) //overflow { throw new ArgumentOutOfRangeException("participantCount", "Adding participantCount participants would result in the number of participants exceeding the maximum number allowed."); } if (SetCurrentTotal(currentTotal, current, total + participantCount, sense)) { // Calculating the first phase for that participant, if the current phase already finished return the nextphase else return the current phase // To know that the current phase is the sense doesn't match the // phase odd even, so that means it didn't yet change the phase count, so currentPhase +1 is returned, otherwise currentPhase is returned var currPhase = CurrentPhaseNumber; newPhase = (sense != (currPhase % 2 == 0)) ? currPhase + 1 : currPhase; // If this participant is going to join the next phase, which means the postPhaseAction is being running, this participants must wait until this done // and its event is reset. // Without that, if the postPhaseAction takes long time, this means the event ehich the current participant is goint to wait on is still set // (FinishPPhase didn't reset it yet) so it should wait until it reset if (newPhase != currPhase) { // Wait on the opposite event if (sense) { _oddEvent.Wait(); } else { _evenEvent.Wait(); } } //This else to fix the racing where the current phase has been finished, m_currentPhase has been updated but the events have not been set/reset yet // otherwise when this participant calls SignalAndWait it will wait on a set event however all other participants have not arrived yet. else { if (sense && _evenEvent.IsSet) { _evenEvent.Reset(); } else if (!sense && _oddEvent.IsSet) { _oddEvent.Reset(); } } break; } spinner.SpinOnce(); } return(newPhase); }
/// <summary> /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>, /// using a 32-bit signed integer to measure the time interval, /// while observing a <see cref="T:System.Threading.CancellationToken"/>. /// </summary> /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="Timeout.Infinite"/>(-1) to /// wait indefinitely.</param> /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to observe.</param> /// <returns>true if the current thread successfully entered the <see cref="SemaphoreSlim"/>; otherwise, false.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1, /// which represents an infinite time-out.</exception> /// <exception cref="System.OperationCanceledException"><paramref name="cancellationToken"/> was canceled.</exception> public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { CheckDispose(); // Validate input if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException( nameof(millisecondsTimeout), millisecondsTimeout, SR.SemaphoreSlim_Wait_TimeoutWrong); } cancellationToken.ThrowIfCancellationRequested(); // Perf: Check the stack timeout parameter before checking the volatile count if (millisecondsTimeout == 0 && m_currentCount == 0) { // Pessimistic fail fast, check volatile count outside lock (only when timeout is zero!) return(false); } uint startTime = 0; if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout > 0) { startTime = TimeoutHelper.GetTime(); } bool waitSuccessful = false; Task <bool> asyncWaitTask = null; bool lockTaken = false; //Register for cancellation outside of the main lock. //NOTE: Register/deregister inside the lock can deadlock as different lock acquisition orders could // occur for (1)this.m_lockObj and (2)cts.internalLock CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.InternalRegisterWithoutEC(s_cancellationTokenCanceledEventHandler, this); try { // Perf: first spin wait for the count to be positive, but only up to the first planned yield. // This additional amount of spinwaiting in addition // to Monitor.Enter()’s spinwaiting has shown measurable perf gains in test scenarios. // SpinWait spin = new SpinWait(); while (m_currentCount == 0 && !spin.NextSpinWillYield) { spin.SpinOnce(); } // entering the lock and incrementing waiters must not suffer a thread-abort, else we cannot // clean up m_waitCount correctly, which may lead to deadlock due to non-woken waiters. try { } finally { m_lock.Acquire(); lockTaken = true; if (lockTaken) { m_waitCount++; } } // If there are any async waiters, for fairness we'll get in line behind // then by translating our synchronous wait into an asynchronous one that we // then block on (once we've released the lock). if (m_asyncHead != null) { Debug.Assert(m_asyncTail != null, "tail should not be null if head isn't"); asyncWaitTask = WaitAsync(millisecondsTimeout, cancellationToken); } // There are no async waiters, so we can proceed with normal synchronous waiting. else { // If the count > 0 we are good to move on. // If not, then wait if we were given allowed some wait duration OperationCanceledException oce = null; if (m_currentCount == 0) { if (millisecondsTimeout == 0) { return(false); } // Prepare for the main wait... // wait until the count become greater than zero or the timeout is expired try { waitSuccessful = WaitUntilCountOrTimeout(millisecondsTimeout, startTime, cancellationToken); } catch (OperationCanceledException e) { oce = e; } } // Now try to acquire. We prioritize acquisition over cancellation/timeout so that we don't // lose any counts when there are asynchronous waiters in the mix. Asynchronous waiters // defer to synchronous waiters in priority, which means that if it's possible an asynchronous // waiter didn't get released because a synchronous waiter was present, we need to ensure // that synchronous waiter succeeds so that they have a chance to release. Debug.Assert(!waitSuccessful || m_currentCount > 0, "If the wait was successful, there should be count available."); if (m_currentCount > 0) { waitSuccessful = true; m_currentCount--; } else if (oce != null) { throw oce; } // Exposing wait handle which is lazily initialized if needed if (m_waitHandle != null && m_currentCount == 0) { m_waitHandle.Reset(); } } } finally { // Release the lock if (lockTaken) { m_waitCount--; m_lock.Release(); } // Unregister the cancellation callback. cancellationTokenRegistration.Dispose(); } // If we had to fall back to asynchronous waiting, block on it // here now that we've released the lock, and return its // result when available. Otherwise, this was a synchronous // wait, and whether we successfully acquired the semaphore is // stored in waitSuccessful. return((asyncWaitTask != null) ? asyncWaitTask.GetAwaiter().GetResult() : waitSuccessful); }
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { CheckState(); if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException("millisecondsTimeout", "millisecondsTimeout is a negative number other than -1"); } Watch sw = Watch.StartNew(); Func <bool> stopCondition = () => millisecondsTimeout >= 0 && sw.ElapsedMilliseconds > millisecondsTimeout; do { bool shouldWait; int result; do { cancellationToken.ThrowIfCancellationRequested(); if (stopCondition()) { return(false); } shouldWait = true; result = currCount; if (result > 0) { shouldWait = false; } else { break; } } while (Interlocked.CompareExchange(ref currCount, result - 1, result) != result); if (!shouldWait) { if (result == 1) { handle.Reset(); } break; } SpinWait wait = new SpinWait(); while (Thread.VolatileRead(ref currCount) <= 0) { cancellationToken.ThrowIfCancellationRequested(); if (stopCondition()) { return(false); } if (wait.Count > spinCount) { handle.WaitOne(Math.Min(Math.Max(millisecondsTimeout - (int)sw.ElapsedMilliseconds, 1), deepSleepTime)); } else { wait.SpinOnce(); } } } while (true); return(true); }
/// <summary> /// Blocks the current thread until the current <see cref="ManualResetEventSlim"/> is set, using a /// 32-bit signed integer to measure the time interval, while observing a <see /// cref="T:System.Threading.CancellationToken"/>. /// </summary> /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see /// cref="Timeout.Infinite"/>(-1) to wait indefinitely.</param> /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to /// observe.</param> /// <returns>true if the <see cref="System.Threading.ManualResetEventSlim"/> was set; otherwise, /// false.</returns> /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a /// negative number other than -1, which represents an infinite time-out.</exception> /// <exception cref="T:System.InvalidOperationException"> /// The maximum number of waiters has been exceeded. /// </exception> /// <exception cref="T:System.Threading.OperationCanceledException"><paramref /// name="cancellationToken"/> was canceled.</exception> public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { ThrowIfDisposed(); cancellationToken.ThrowIfCancellationRequested(); // an early convenience check if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout)); } if (!IsSet) { if (millisecondsTimeout == 0) { // For 0-timeouts, we just return immediately. return(false); } // We spin briefly before falling back to allocating and/or waiting on a true event. uint startTime = 0; bool bNeedTimeoutAdjustment = false; int realMillisecondsTimeout = millisecondsTimeout; //this will be adjusted if necessary. if (millisecondsTimeout != Timeout.Infinite) { // We will account for time spent spinning, so that we can decrement it from our // timeout. In most cases the time spent in this section will be negligible. But // we can't discount the possibility of our thread being switched out for a lengthy // period of time. The timeout adjustments only take effect when and if we actually // decide to block in the kernel below. startTime = TimeoutHelper.GetTime(); bNeedTimeoutAdjustment = true; } // Spin int spinCount = SpinCount; var spinner = new SpinWait(); while (spinner.Count < spinCount) { spinner.SpinOnce(SpinWait.Sleep1ThresholdForLongSpinBeforeWait); if (IsSet) { return(true); } if (spinner.Count >= 100 && spinner.Count % 10 == 0) // check the cancellation token if the user passed a very large spin count { cancellationToken.ThrowIfCancellationRequested(); } } // Now enter the lock and wait. EnsureLockObjectCreated(); // We must register and unregister the token outside of the lock, to avoid deadlocks. using (cancellationToken.InternalRegisterWithoutEC(s_cancellationTokenCallback, this)) { lock (m_lock) { // Loop to cope with spurious wakeups from other waits being canceled while (!IsSet) { // If our token was canceled, we must throw and exit. cancellationToken.ThrowIfCancellationRequested(); //update timeout (delays in wait commencement are due to spinning and/or spurious wakeups from other waits being canceled) if (bNeedTimeoutAdjustment) { realMillisecondsTimeout = TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout); if (realMillisecondsTimeout <= 0) { return(false); } } // There is a race condition that Set will fail to see that there are waiters as Set does not take the lock, // so after updating waiters, we must check IsSet again. // Also, we must ensure there cannot be any reordering of the assignment to Waiters and the // read from IsSet. This is guaranteed as Waiters{set;} involves an Interlocked.CompareExchange // operation which provides a full memory barrier. // If we see IsSet=false, then we are guaranteed that Set() will see that we are // waiting and will pulse the monitor correctly. Waiters = Waiters + 1; if (IsSet) //This check must occur after updating Waiters. { Waiters--; //revert the increment. return(true); } // Now finally perform the wait. try { // ** the actual wait ** if (!Monitor.Wait(m_lock, realMillisecondsTimeout)) { return(false); //return immediately if the timeout has expired. } } finally { // Clean up: we're done waiting. Waiters = Waiters - 1; } // Now just loop back around, and the right thing will happen. Either: // 1. We had a spurious wake-up due to some other wait being canceled via a different cancellationToken (rewait) // or 2. the wait was successful. (the loop will break) } } } } // automatically disposes (and unregisters) the callback return(true); //done. The wait was satisfied. }
/// <summary> /// Try acquire the lock with long path, this is usually called after the first path in Enter and /// TryEnter failed The reason for short path is to make it inline in the run time which improves the /// performance. This method assumed that the parameter are validated in Enter or TryEnter method. /// </summary> /// <param name="millisecondsTimeout">The timeout milliseconds</param> /// <param name="lockTaken">The lockTaken param</param> private void ContinueTryEnter(int millisecondsTimeout, ref bool lockTaken) { // The fast path doesn't throw any exception, so we have to validate the parameters here if (lockTaken) { lockTaken = false; throw new ArgumentException(SR.SpinLock_TryReliableEnter_ArgumentException); } if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException( nameof(millisecondsTimeout), millisecondsTimeout, SR.SpinLock_TryEnter_ArgumentOutOfRange); } uint startTime = 0; if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout != 0) { startTime = TimeoutHelper.GetTime(); } if (IsThreadOwnerTrackingEnabled) { // Slow path for enabled thread tracking mode ContinueTryEnterWithThreadTracking(millisecondsTimeout, startTime, ref lockTaken); return; } // then thread tracking is disabled // In this case there are three ways to acquire the lock // 1- the first way the thread either tries to get the lock if it's free or updates the waiters, if the turn >= the processors count then go to 3 else go to 2 // 2- In this step the waiter threads spins and tries to acquire the lock, the number of spin iterations and spin count is dependent on the thread turn // the late the thread arrives the more it spins and less frequent it check the lock availability // Also the spins count is increases each iteration // If the spins iterations finished and failed to acquire the lock, go to step 3 // 3- This is the yielding step, there are two ways of yielding Thread.Yield and Sleep(1) // If the timeout is expired in after step 1, we need to decrement the waiters count before returning int observedOwner; int turn = int.MaxValue; //***Step 1, take the lock or update the waiters // try to acquire the lock directly if possible or update the waiters count observedOwner = _owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { if (CompareExchange(ref _owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner) { // Acquired lock return; } if (millisecondsTimeout == 0) { // Did not acquire lock in CompareExchange and timeout is 0 so fail fast return; } } else if (millisecondsTimeout == 0) { // Did not acquire lock as owned and timeout is 0 so fail fast return; } else //failed to acquire the lock, then try to update the waiters. If the waiters count reached the maximum, just break the loop to avoid overflow { if ((observedOwner & WAITERS_MASK) != MAXIMUM_WAITERS) { // This can still overflow, but maybe there will never be that many waiters turn = (Interlocked.Add(ref _owner, 2) & WAITERS_MASK) >> 1; } } // lock acquired failed and waiters updated //*** Step 2, Spinning and Yielding var spinner = new SpinWait(); if (turn > PlatformHelper.ProcessorCount) { spinner.Count = SpinWait.YieldThreshold; } while (true) { spinner.SpinOnce(SLEEP_ONE_FREQUENCY); observedOwner = _owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero observedOwner | 1 // don't decrement it. just set the lock bit, it is zero because a previous call of Exit(false) which corrupted the waiters : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit Debug.Assert((newOwner & WAITERS_MASK) >= 0); if (CompareExchange(ref _owner, newOwner, observedOwner, ref lockTaken) == observedOwner) { return; } } if (spinner.Count % TIMEOUT_CHECK_FREQUENCY == 0) { // Check the timeout. if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0) { DecrementWaiters(); return; } } } }
/// <summary> /// Notifies the <see cref="Barrier"/> that there will be additional participants. /// </summary> /// <param name="participantCount">The number of additional participants to add to the /// barrier.</param> /// <returns>The phase number of the barrier in which the new participants will first /// participate.</returns> /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="participantCount"/> is less than /// 0.</exception> /// <exception cref="T:System.ArgumentOutOfRangeException">Adding <paramref name="participantCount"/> participants would cause the /// barrier's participant count to exceed <see cref="T:System.Int16.MaxValue"/>.</exception> /// <exception cref="T:System.InvalidOperationException"> /// The method was invoked from within a post-phase action. /// </exception> /// <exception cref="T:System.ObjectDisposedException">The current instance has already been /// disposed.</exception> public long AddParticipants(int participantCount) { // check dispose ThrowIfDisposed(); if (participantCount < 1) { throw new ArgumentOutOfRangeException(nameof(participantCount), participantCount, SR.Barrier_AddParticipants_NonPositive_ArgumentOutOfRange); } else if (participantCount > MAX_PARTICIPANTS) //overflow { throw new ArgumentOutOfRangeException(nameof(participantCount), SR.Barrier_AddParticipants_Overflow_ArgumentOutOfRange); } // in case of this is called from the PHA if (_actionCallerID != 0 && Environment.CurrentManagedThreadId == _actionCallerID) { throw new InvalidOperationException(SR.Barrier_InvalidOperation_CalledFromPHA); } SpinWait spinner = new SpinWait(); long newPhase = 0; while (true) { int currentTotal = _currentTotalCount; int total; int current; bool sense; GetCurrentTotal(currentTotal, out current, out total, out sense); if (participantCount + total > MAX_PARTICIPANTS) //overflow { throw new ArgumentOutOfRangeException(nameof(participantCount), SR.Barrier_AddParticipants_Overflow_ArgumentOutOfRange); } if (SetCurrentTotal(currentTotal, current, total + participantCount, sense)) { // Calculating the first phase for that participant, if the current phase already finished return the nextphase else return the current phase // To know that the current phase is the sense doesn't match the // phase odd even, so that means it didn't yet change the phase count, so currentPhase +1 is returned, otherwise currentPhase is returned long currPhase = CurrentPhaseNumber; newPhase = (sense != (currPhase % 2 == 0)) ? currPhase + 1 : currPhase; // If this participant is going to join the next phase, which means the postPhaseAction is being running, this participants must wait until this done // and its event is reset. // Without that, if the postPhaseAction takes long time, this means the event ehich the current participant is goint to wait on is still set // (FinishPPhase didn't reset it yet) so it should wait until it reset if (newPhase != currPhase) { // Wait on the opposite event if (sense) { _oddEvent.Wait(); } else { _evenEvent.Wait(); } } //This else to fix the racing where the current phase has been finished, m_currentPhase has been updated but the events have not been set/reset yet // otherwise when this participant calls SignalAndWait it will wait on a set event however all other participants have not arrived yet. else { if (sense && _evenEvent.IsSet) { _evenEvent.Reset(); } else if (!sense && _oddEvent.IsSet) { _oddEvent.Reset(); } } break; } spinner.SpinOnce(); } return(newPhase); }
/// <summary> /// Signals that a participant has reached the barrier and waits for all other participants to reach /// the barrier as well, using a /// 32-bit signed integer to measure the time interval, while observing a <see /// cref="T:System.Threading.CancellationToken"/>. /// </summary> /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see /// cref="Timeout.Infinite"/>(-1) to wait indefinitely.</param> /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to /// observe.</param> /// <returns>true if all other participants reached the barrier; otherwise, false.</returns> /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a /// negative number other than -1, which represents an infinite time-out.</exception> /// <exception cref="T:System.InvalidOperationException"> /// The method was invoked from within a post-phase action, the barrier currently has 0 participants, /// or the barrier is being used by more threads than are registered as participants. /// </exception> /// <exception cref="T:System.OperationCanceledException"><paramref name="cancellationToken"/> has been /// canceled.</exception> /// <exception cref="T:System.ObjectDisposedException">The current instance has already been /// disposed.</exception> public bool SignalAndWait(int millisecondsTimeout, CancellationToken cancellationToken) { ThrowIfDisposed(); cancellationToken.ThrowIfCancellationRequested(); if (millisecondsTimeout < -1) { throw new System.ArgumentOutOfRangeException(nameof(millisecondsTimeout), millisecondsTimeout, SR.Barrier_SignalAndWait_ArgumentOutOfRange); } // in case of this is called from the PHA if (_actionCallerID != 0 && Environment.CurrentManagedThreadId == _actionCallerID) { throw new InvalidOperationException(SR.Barrier_InvalidOperation_CalledFromPHA); } // local variables to extract the basic barrier variable and update them // The are declared here instead of inside the loop body because the will be used outside the loop bool sense; // The sense of the barrier *before* the phase associated with this SignalAndWait call completes int total; int current; int currentTotal; long phase; SpinWait spinner = new SpinWait(); while (true) { currentTotal = _currentTotalCount; GetCurrentTotal(currentTotal, out current, out total, out sense); phase = CurrentPhaseNumber; // throw if zero participants if (total == 0) { throw new InvalidOperationException(SR.Barrier_SignalAndWait_InvalidOperation_ZeroTotal); } // Try to detect if the number of threads for this phase exceeded the total number of participants or not // This can be detected if the current is zero which means all participants for that phase has arrived and the phase number is not changed yet if (current == 0 && sense != (CurrentPhaseNumber % 2 == 0)) { throw new InvalidOperationException(SR.Barrier_SignalAndWait_InvalidOperation_ThreadsExceeded); } //This is the last thread, finish the phase if (current + 1 == total) { if (SetCurrentTotal(currentTotal, 0, total, !sense)) { if (CdsSyncEtwBCLProvider.Log.IsEnabled()) { CdsSyncEtwBCLProvider.Log.Barrier_PhaseFinished(sense, CurrentPhaseNumber); } FinishPhase(sense); return(true); } } else if (SetCurrentTotal(currentTotal, current + 1, total, sense)) { break; } spinner.SpinOnce(); } // ** Perform the real wait ** // select the correct event to wait on, based on the current sense. ManualResetEventSlim eventToWaitOn = (sense) ? _evenEvent : _oddEvent; bool waitWasCanceled = false; bool waitResult = false; try { waitResult = DiscontinuousWait(eventToWaitOn, millisecondsTimeout, cancellationToken, phase); } catch (OperationCanceledException) { waitWasCanceled = true; } catch (ObjectDisposedException)// in case a race happen where one of the thread returned from SignalAndWait and the current thread calls Wait on a disposed event { // make sure the current phase for this thread is already finished, otherwise propagate the exception if (phase < CurrentPhaseNumber) { waitResult = true; } else { throw; } } if (!waitResult) { //reset the spinLock to prepare it for the next loop spinner.Reset(); //If the wait timeout expired and all other thread didn't reach the barrier yet, update the current count back while (true) { bool newSense; currentTotal = _currentTotalCount; GetCurrentTotal(currentTotal, out current, out total, out newSense); // If the timeout expired and the phase has just finished, return true and this is considered as succeeded SignalAndWait //otherwise the timeout expired and the current phase has not been finished yet, return false //The phase is finished if the phase member variable is changed (incremented) or the sense has been changed // we have to use the statements in the comparison below for two cases: // 1- The sense is changed but the last thread didn't update the phase yet // 2- The phase is already incremented but the sense flipped twice due to the termination of the next phase if (phase < CurrentPhaseNumber || sense != newSense) { // The current phase has been finished, but we shouldn't return before the events are set/reset otherwise this thread could start // next phase and the appropriate event has not reset yet which could make it return immediately from the next phase SignalAndWait // before waiting other threads WaitCurrentPhase(eventToWaitOn, phase); Debug.Assert(phase < CurrentPhaseNumber); break; } //The phase has not been finished yet, try to update the current count. if (SetCurrentTotal(currentTotal, current - 1, total, sense)) { //if here, then the attempt to backout was successful. //throw (a fresh) oce if cancellation woke the wait //or return false if it was the timeout that woke the wait. // if (waitWasCanceled) { throw new OperationCanceledException(SR.Common_OperationCanceled, cancellationToken); } else { return(false); } } spinner.SpinOnce(); } } if (_exception != null) { throw new BarrierPostPhaseException(_exception); } return(true); }
private void WriteToFile(ref System.Threading.SpinWait spinWait) { File.AppendAllText(_fileName, $"{nameof(SpinWait)}: {DateTime.Now:hh:mm:ss:ffff} {Environment.NewLine}"); }
public bool SignalAndWait(int millisecondsTimeout, CancellationToken cancellationToken) { bool flag; int num; int num2; int currentTotalCount; this.ThrowIfDisposed(); cancellationToken.ThrowIfCancellationRequested(); if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException("millisecondsTimeout", millisecondsTimeout, SR.GetString("Barrier_SignalAndWait_ArgumentOutOfRange")); } if ((this.m_actionCallerID != 0) && (Thread.CurrentThread.ManagedThreadId == this.m_actionCallerID)) { throw new InvalidOperationException(SR.GetString("Barrier_InvalidOperation_CalledFromPHA")); } SpinWait wait = new SpinWait(); while (true) { currentTotalCount = this.m_currentTotalCount; this.GetCurrentTotal(currentTotalCount, out num2, out num, out flag); if (num == 0) { throw new InvalidOperationException(SR.GetString("Barrier_SignalAndWait_InvalidOperation_ZeroTotal")); } if ((num2 == 0) && (flag != ((this.m_currentPhase % 2L) == 0L))) { throw new InvalidOperationException(SR.GetString("Barrier_SignalAndWait_InvalidOperation_ThreadsExceeded")); } if ((num2 + 1) == num) { if (this.SetCurrentTotal(currentTotalCount, 0, num, !flag)) { if (CdsSyncEtwBCLProvider.Log.IsEnabled()) { CdsSyncEtwBCLProvider.Log.Barrier_PhaseFinished(flag, this.m_currentPhase); } this.FinishPhase(flag); return(true); } } else if (this.SetCurrentTotal(currentTotalCount, num2 + 1, num, flag)) { break; } wait.SpinOnce(); } long currentPhase = this.m_currentPhase; ManualResetEventSlim slim = flag ? this.m_evenEvent : this.m_oddEvent; bool flag2 = false; bool flag3 = false; try { flag3 = slim.Wait(millisecondsTimeout, cancellationToken); } catch (OperationCanceledException) { flag2 = true; } if (!flag3) { wait.Reset(); while (true) { bool flag4; currentTotalCount = this.m_currentTotalCount; this.GetCurrentTotal(currentTotalCount, out num2, out num, out flag4); if ((currentPhase != this.m_currentPhase) || (flag != flag4)) { slim.Wait(); break; } if (this.SetCurrentTotal(currentTotalCount, num2 - 1, num, flag)) { if (flag2) { throw new OperationCanceledException(SR.GetString("Common_OperationCanceled"), cancellationToken); } return(false); } wait.SpinOnce(); } } if (this.m_exception != null) { throw new BarrierPostPhaseException(this.m_exception); } return(true); }
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { CheckDispose(); if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException( nameof(millisecondsTimeout), millisecondsTimeout, SR.SemaphoreSlim_Wait_TimeoutWrong); } cancellationToken.ThrowIfCancellationRequested(); // Perf: Check the stack timeout parameter before checking the volatile count if (millisecondsTimeout == 0 && m_currentCount == 0) { // Pessimistic fail fast, check volatile count outside lock (only when timeout is zero!) return(false); } uint startTime = 0; if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout > 0) { startTime = TimeoutHelper.GetTime(); } bool waitSuccessful = false; Task <bool>?asyncWaitTask = null; bool lockTaken = false; // Register for cancellation outside of the main lock. // NOTE: Register/unregister inside the lock can deadlock as different lock acquisition orders could // occur for (1)this.m_lockObjAndDisposed and (2)cts.internalLock CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.UnsafeRegister(s_cancellationTokenCanceledEventHandler, this); try { // Perf: first spin wait for the count to be positive. // This additional amount of spinwaiting in addition // to Monitor.Enter()'s spinwaiting has shown measurable perf gains in test scenarios. if (m_currentCount == 0) { // Monitor.Enter followed by Monitor.Wait is much more expensive than waiting on an event as it involves another // spin, contention, etc. The usual number of spin iterations that would otherwise be used here is increased to // lessen that extra expense of doing a proper wait. int spinCount = SpinWait.SpinCountforSpinBeforeWait * 4; SpinWait spinner = default; while (spinner.Count < spinCount) { spinner.SpinOnce(sleep1Threshold: -1); if (m_currentCount != 0) { break; } } } Monitor.Enter(m_lockObjAndDisposed, ref lockTaken); m_waitCount++; // If there are any async waiters, for fairness we'll get in line behind // then by translating our synchronous wait into an asynchronous one that we // then block on (once we've released the lock). if (m_asyncHead is not null) { Debug.Assert(m_asyncTail is not null, "tail should not be null if head isn't"); asyncWaitTask = WaitAsync(millisecondsTimeout, cancellationToken); } // There are no async waiters, so we can proceed with normal synchronous waiting. else { // If the count > 0 we are good to move on. // If not, then wait if we were given allowed some wait duration OperationCanceledException?oce = null; if (m_currentCount == 0) { if (millisecondsTimeout == 0) { return(false); } // Prepare for the main wait... // wait until the count become greater than zero or the timeout is expired try { waitSuccessful = WaitUntilCountOrTimeout(millisecondsTimeout, startTime, cancellationToken); } catch (OperationCanceledException e) { oce = e; } } // Now try to acquire. We prioritize acquisition over cancellation/timeout so that we don't // lose any counts when there are asynchronous waiters in the mix. Asynchronous waiters // defer to synchronous waiters in priority, which means that if it's possible an asynchronous // waiter didn't get released because a synchronous waiter was present, we need to ensure // that synchronous waiter succeeds so that they have a chance to release. Debug.Assert(!waitSuccessful || m_currentCount > 0, "If the wait was successful, there should be count available."); if (m_currentCount > 0) { waitSuccessful = true; m_currentCount--; } else if (oce is not null) { throw oce; } // Exposing wait handle which is lazily initialized if needed if (m_waitHandle is not null && m_currentCount == 0) { m_waitHandle.Reset(); } } } finally { // Release the lock if (lockTaken) { m_waitCount--; Monitor.Exit(m_lockObjAndDisposed); } // Unregister the cancellation callback. cancellationTokenRegistration.Dispose(); } // If we had to fall back to asynchronous waiting, block on it // here now that we've released the lock, and return its // result when available. Otherwise, this was a synchronous // wait, and whether we successfully acquired the semaphore is // stored in waitSuccessful. return((asyncWaitTask is not null) ? asyncWaitTask.GetAwaiter().GetResult() : waitSuccessful); }