8

I'm trying to set bit flags in a shared variable within a multithreaded .NET application, but couldn't find a parallell to the native InterlockedOr function in the managed Interlocked class. I've come up with the following code for performing a |= assignment, but the theoretical possibility of an infinite loop is making me uncomfortable:

long currentValue;
long updatedValue;

do
{
    // Spin until successful update. Value must be read using Interlocked.Read()
    // to be truly atomic on 32 bit systems (see MSDN).
    currentFlags = Interlocked.Read(ref _currentFlags);
    updatedValue = currentFlags | flag;
} while (currentFlags != Interlocked.CompareExchange(ref _currentFlags, updatedValue, currentFlags));

Can this be implemented in a safer way using only the functions built into the Interlocked class? I'd like to avoid a solution involving an explicit lock if possible.

Cryovat
  • 166
  • 6
  • 1
    Whilst there is the possibility of an infinite loop in using `CompareExchange`, it's so unlikely to be encountered that it can be ignored. The .NET Framework uses this very technique extensively, including in all its concurrent collections in order to achieve lock-free synchronization. – Paul Turner Jul 27 '15 at 15:29
  • You would get an infinite loop only if some other thread concurrently resets the flag back to zero, and is agile enough to beat your thread to it every single time, for an infinite number of tries. If this happens, you have bigger problems to solve, figuring out why the "agile" thread does what it does. I think your solution is as good as it gets with `Interlocked`. – Sergey Kalinichenko Jul 27 '15 at 15:29

1 Answers1

1

Let's assume (these restrictions are not fundamental, just to simplify illustration) that

  • at the beginning currentFlags is 0,
  • we set at most one flag in one step,
  • we do not touch the sign bit.

Note that if we set bit k to 1 in currentFlags once, we could replace or with + 1L << k. So we can use a helper array set to remember which bits are set already, and do Interlocked.Add if needed.

long currentFlags = 0;
int[] set = new int[sizeof(long) * 8];

....

int k = ...; // bit to set
if(Interlocked.Exchange(ref set[k], 1) == 0)
    Interlocked.Add(ref currentFlags, 1L << k);
AlexD
  • 32,156
  • 3
  • 71
  • 65