Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is the "switch" statement evaluation thread-safe?

Consider the following sample code:

class MyClass
{
    public long x;

    public void DoWork()
    {
        switch (x)
        {
            case 0xFF00000000L:
                // do whatever...
                break;

            case 0xFFL:
                // do whatever...
                break;

            default:
                //notify that something going wrong
                throw new Exception();
        }
    }
}

Forget the uselessness of the snippet: my doubt is about the behavior of the switch statement.

Suppose that the x field could have only two values: 0xFF00000000L or 0xFFL. The switch above should not fall into the "default" option.

Now imagine that one thread is executing the switch with "x" equal to 0xFFL, thus the first condition won't match. At the same time, another thread modifies the "x" variable to 0xFF00000000L. We know a 64-bit operation is not atomic, so that the variable will have the lower dword zeroed first, then the upper set afterward (or vice versa).

If the second condition in the switch will be done when the "x" is zero (i.e. during the new assignment), will we fall into the undesired "default" case?

like image 434
Mario Vernari Avatar asked Jul 11 '11 08:07

Mario Vernari


People also ask

How do you ensure thread-safety?

Using Atomic Variable Using an atomic variable is another way to achieve thread-safety in java. When variables are shared by multiple threads, the atomic variable ensures that threads don't crash into each other.

Which of the following are thread-safe in Java?

The collection classes that are thread-safe in Java are Stack, Vector, Properties, Hashtable, etc.

Is local object reference thread-safe?

Local Object References All objects are stored in the shared heap. If an object created locally never escapes the method it was created in, it is thread safe. In fact you can also pass it on to other methods and objects as long as none of these methods or objects make the passed object available to other threads.

Is heap memory thread-safe in Java?

Java Memory model includes Heap and Stack. Data that is storing in the Heap can be shared between threads, which means it is not a thread-safe data. Data stored in a Stack memory is not shared between the threads, which means it is a thread-safe data.


4 Answers

Yes, the switch statement itself, as shown in your question, is thread-safe. The value of field x is loaded once into a (hidden) local variable and that local is used for the switch block.

What isn't safe is the initial load of the field x into a local variable. 64-bit reads aren't guaranteed to be atomic, so you could be getting stale and/or torn reads at that point. This could easily be resolved by using Interlocked.Read, or similar, to explicitly read the field value into the local in a thread-safe way:

long y = Interlocked.Read(ref x); switch (y) {     // ... } 
like image 73
LukeH Avatar answered Oct 14 '22 10:10

LukeH


You're actually posting two questions.

Is it threadsafe?

Well, obviously it is not, another thread might change the value of X while the first thread is going into the switch. Since there's no lock and the variable is not volatile you'll switch based on the wrong value.

Would you ever hit the default state of the switch?

Theoretically you might, as updating a 64 bits is not an atomic operation and thus you could theoretically jump in the middle of the assignment and get a mingled result for x as you point out. This statistically won't happen often but it WILL happen eventually.

But the switch itself is threadsafe, what's not threadsafe is read and writes over 64 bit variables (in a 32 bit OS).

Imagine instead of switch(x) you have the following code:

long myLocal = x;
switch(myLocal)
{
}

now the switch is made over a local variable, and thus, it's completely threadsafe. The problem, of course, is in the myLocal = x read and its conflict with other assignments.

like image 45
Jorge Córdoba Avatar answered Oct 14 '22 08:10

Jorge Córdoba


C#'s switch statement isn't evaluated as a series of if conditions (as VB's can be). C# effectively builds up a hashtable of labels to jump to based on the value of the object and jumps straight to the correct label, rather than iterating through each condition in turn and evaluating it.

This is why a C# switch statement doesn't deteriorate in terms of speed as you increase the number of cases. And it's also why C# is more restrictive with what you can compare to in the switch cases than VB, in which you can do ranges of values, for example.

Therefore you don't have the potential race condition that you've stated, where a comparison is made, the value changes, the second comparison is made, etc, because there is only one check performed. As for whether it's totally threadsafe - I wouldn't assume so.

Have a dig with reflector looking through a C# switch statement in IL and you'll see what's happening. Compare it to a switch statement from VB which includes ranges in the values and you'll see a difference.

It's been quite a few years since I looked at it, so things may have changed slightly...

See more detail about switch statement behaviour here: Is there any significant difference between using if/else and switch-case in C#?

like image 28
Niall Connaughton Avatar answered Oct 14 '22 08:10

Niall Connaughton


As you already assumed, the switch statement is not thread-safe and might fail in certain scenarios.

Furthermore, using lock on your instance variable won't work neither, because the lock statement expects an object causing your instance variable to be boxed. Every time the instance variable is boxed, a new boxed variable will be created, making the lock effectively useless.

In my opinion you have several options solving this issue.

  1. Use lock on a private instance variable of any reference type (object will do the job)
  2. Use ReaderWriterLockSlim to let multiple threads read the instance variable but only one thread write the instance variable at a time.
  3. Atomically store the value of the instance variable to a local variable in your method (e.g. using Interlocked.Read or Interlocked.Exchange) and perform the switch on the local variable. Note that this way you might use the old value for the switch. You have to decide if this can cause problems in your concrete use-case.
like image 44
Florian Greinacher Avatar answered Oct 14 '22 08:10

Florian Greinacher