Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enum.TryParse - is it thread safe?

I'm trying to figure out if .NET 4.0's Enum.TryParse is thread safe.

The source code (decompiled) is:

[SecuritySafeCritical]
public static bool TryParse<TEnum>(string value, bool ignoreCase, out TEnum result) where TEnum : struct
{
    result = default(TEnum);   /// (*)
    Enum.EnumResult enumResult = default(Enum.EnumResult);
    enumResult.Init(false);
    bool result2;
    if (result2 = Enum.TryParseEnum(typeof(TEnum), value, ignoreCase, ref enumResult))
    {
        result = (TEnum)enumResult.parsedEnum;
    }
    return result2;
}

What seems problematic to me is this line:

result = default(TEnum);   /// (*)

What if another thread accessed result right after it's been set to the default value, and before it is set to the parsed value?

[EDIT] Following Zoidberg's answer, I'd like to rephrase the question a bit.

The question is, I guess, if Enum.TryParse is "transactional" (or atomic).

Say I have a static field, and pass it into Enum.TryParse:

public static SomeEnum MyField;
....
Enum.TryParse("Value", out MyField);

Now, when TryParse is being executed, another thread accesses MyField. TryParse will change MyField's value to SomeEnum's default value for a while, and only then will set it to the parsed value.

This is not necessarily a bug in my code. I would expect Enum.TryParse to either set MyField to the parsed value or not touch it at all, not use it as its temporary field.

like image 911
abieganski Avatar asked Mar 02 '12 13:03

abieganski


4 Answers

result, as with every other local variable and parameter, is per-call. The thread-safety of a by-ref parameter is a little more complex to describe, but: in every sane usage - this will not be a problem. I could force a scenario where it was at-risk (due to by-ref passing), but it would be a contrived example.

Typical usage:

SomeEnumType foo;
if(Enum.TryParse(s, true, out foo)) {...}

Is perfectly safe.

The following is a bit more complex:

var objWithField = new SomeType();
// thread 1:
{
    Enum.TryParse(x, true, out objWithField.SomeField));
}
// thread 2:
{
    Enum.TryParse(y, true, out objWithField.SomeField));
}

and is not thread-safe, but for much more subtle reasons than those you are describing in the question.

like image 89
Marc Gravell Avatar answered Sep 29 '22 08:09

Marc Gravell


Yes - it is.

There is no shared state at all in the method you have decompiled.

If another thread calls the same method, it gets it's own copy of all the locals and result is passed in by the caller.

If result had been a class level variable then this would have been an issue, but this is fine.

like image 21
Rob Levine Avatar answered Sep 29 '22 09:09

Rob Levine


result (and thus the variable it refers to) can become default(T), although the string value holds a different enum value if it's that what you mean.

Try the following program:

public enum FooBar
{
    Foo = 0,
    Bar
}

internal class Program
{
    private static FooBar fb = FooBar.Bar;

    private static void Main()
    {
        new Thread(() =>
                       {
                           while (true)
                           {
                               if (Program.fb == FooBar.Foo) // or try default(FooBar), which is the same
                               {
                                   throw new Exception("Not threadsafe");
                               }
                           }
                       }).Start();

        while (true)
        {
            if (!Enum.TryParse("Bar", true, out fb) || fb == FooBar.Foo)
            {
                throw new Exception("Parse error");
            }
        }
    }
}

It will sooner or later (probably sooner) throw the "Not threadsafe" exception.

like image 34
Andre Loker Avatar answered Sep 29 '22 09:09

Andre Loker


It really isn't a question of whether TryParse is threadsafe, but whether the code using it (your code) is thread safe. If you pass the result variable in, and then let it be accessed by another thread without some mutual exclusion protection, then you could have a problem, but the problem would be in your code.

like image 20
Zoidberg Avatar answered Sep 29 '22 09:09

Zoidberg