private LockCookie EnterSlow() { // Attempt to steal the ownership. Take a regular lock to ensure that only one thread is trying to steal it at a time. lock (this) { // We are the new fast thread now! var oldEntry = _current; _current = new LockCookie(Environment.CurrentManagedThreadId); // After MemoryBarrierProcessWide, we can be sure that the Volatile.Read done by the fast thread will see that it is not a fast // thread anymore, and thus it will not attempt to enter the lock. Interlocked.MemoryBarrierProcessWide(); // Keep looping as long as the lock is taken by other thread SpinWait sw = new SpinWait(); while (oldEntry.Taken) { sw.SpinOnce(); } // We have seen that the other thread released the lock by setting Taken to false. // However, on platforms with weak memory ordering (ex: ARM32, ARM64) observing that does not guarantee that the writes executed by that // thread prior to releasing the lock are all committed to the shared memory. // We could fix that by doing the release via Volatile.Write, but we do not want to add expense to every release on the fast path. // Instead we will do another MemoryBarrierProcessWide here. // NOTE: not needed on x86/x64 Interlocked.MemoryBarrierProcessWide(); _current.Taken = true; return(_current); } }
private LockCookie EnterSlow() { // Attempt to steal the ownership. Take a regular lock to ensure that only one thread is trying to steal it at a time. lock (this) { // We are the new fast thread now! var oldEntry = _current; _current = new LockCookie(Environment.CurrentManagedThreadId); // MemoryBarrierProcessWide ensures, process-wide, that our write to _current becomes visible // to every thread, and all writes by other threads become visible to us before we can continue. // As a result any other thread that sets Taken to true either: // a) made it past the read of _current and owns the lock OR // b) will see that _current has changed and will revert Taken without taking the lock // Thus we only need to wait for 'Taken' to become false and claim the lock for ourselves. // 'Taken' may yet switch to true after that, but that cannot result in other thread owning the lock. Interlocked.MemoryBarrierProcessWide(); // Keep looping as long as the lock is taken by other thread SpinWait sw = new SpinWait(); while (oldEntry.Taken) { sw.SpinOnce(); } _current.Taken = true; return(_current); } }
public async Task HandleDisconnectedAsync(MqttClientDisconnectedEventArgs eventArgs) { _connected = false; Interlocked.MemoryBarrierProcessWide(); if (!(_client is null) && eventArgs.ReasonCode != MqttClientDisconnectReason.AdministrativeAction && !_connected) { _logger.LogWarning(eventArgs.Exception, "MQTT client has disconnected, reason: {0}, trying to reconnect.", eventArgs.ReasonCode); await DoConnectAsync(_client, CancellationToken.None); } }
static void Test1() { x = 1; // CPU has the ability to reorder the code instructions, if the codes have no dependencies // like there Test1 and Test has no dependencies, so CPU can run Test1 and Test2 in any order // use MemoryBarrierProcessWide to block any CPU // or use Interlocked.MemoryBarrier() in both Test 1 and Test2 Interlocked.MemoryBarrierProcessWide(); a = y; }
void Update(ILogger newRoot, ILogger newCached, bool frozen) { _root = newRoot; _cached = newCached; _frozen = frozen; // https://github.com/dotnet/runtime/issues/20500#issuecomment-284774431 // Publish `_cached` and `_frozen`. This is useful here because it means that once the logger is frozen - which // we always expect - reads don't require any synchronization/interlocked instructions. Interlocked.MemoryBarrierProcessWide(); }
public Logger Freeze() { lock (_sync) { if (_frozen) { throw new InvalidOperationException("The logger is already frozen."); } _frozen = true; // https://github.com/dotnet/runtime/issues/20500#issuecomment-284774431 // Publish `_logger` and `_frozen`. This is useful here because it means that once the logger is frozen - which // we always expect - reads don't require any synchronization/interlocked instructions. Interlocked.MemoryBarrierProcessWide(); return(_logger); } }
private static void InterlockedOperations() { //_counter becomes 1 Interlocked.Increment(ref _counter); // _counter becomes 0 Interlocked.Decrement(ref _counter); // Add: _counter becomes 2 Interlocked.Add(ref _counter, 2); //Subtract: _counter becomes 0 Interlocked.Add(ref _counter, -2); // Reads 64 bit field Console.WriteLine(Interlocked.Read(ref _counter)); // Swaps _counter value with 10 Console.WriteLine(Interlocked.Exchange(ref _counter, 10)); //Checks if _counter is 10 and if yes replace with 100 Console.WriteLine(Interlocked.CompareExchange(ref _counter, 100, 10)); // _counter becomes 100 Interlocked.MemoryBarrier(); Interlocked.MemoryBarrierProcessWide(); }
private LockCookie EnterSlow() { // Attempt to steal the ownership. Take a regular lock to ensure that only one thread is trying to steal it at a time. lock (this) { // We are the new fast thread now! var oldEntry = _current; _current = new LockCookie(Environment.CurrentManagedThreadId); // After MemoryBarrierProcessWide, we can be sure that the Volatile.Read done by the fast thread will see that it is not a fast // thread anymore, and thus it will not attempt to enter the lock. Interlocked.MemoryBarrierProcessWide(); // Keep looping as long as the lock is taken by other thread SpinWait sw = new SpinWait(); while (oldEntry.Taken) { sw.SpinOnce(); } _current.Taken = true; return(_current); } }
static void Test1() { x = 1; Interlocked.MemoryBarrierProcessWide(); a = y; }
private static void BarrierUsingInterlockedProcessWideBarrier() { b = c; Interlocked.MemoryBarrierProcessWide(); a = 1; }