I've been looking at the source code of System.Reactive (here), and it's taken me down a rabbit hole to this place, where there is a Volatile.Read
followed by Interlocked.CompareExchange
, on the same variable:
if (Volatile.Read(ref _runDrainOnce) == 0
&& Interlocked.CompareExchange(ref _runDrainOnce, 1, 0) == 0)
{
//do something
}
As I read it, the logic of this is "if runDrainOnce is 0 and, if it was zero before I changed it to 1". Is there something subtle here? Why is the first check not redundant?
(Even more mind-boggling, there is a lock
and a Monitor.Pulse
in the same function. Was this the result of a bet? :))
The whole function:
private void Schedule()
{
// Schedule the suspending drain once
if (Volatile.Read(ref _runDrainOnce) == 0
&& Interlocked.CompareExchange(ref _runDrainOnce, 1, 0) == 0)
{
_drainTask.Disposable = _scheduler.ScheduleLongRunning(this, DrainLongRunning);
}
// Indicate more work is to be done by the drain loop
if (Interlocked.Increment(ref _wip) == 1L)
{
// resume the drain loop waiting on the guard
lock (_suspendGuard)
{
Monitor.Pulse(_suspendGuard);
}
}
}