call_once for C#

One of the useful gems that made it into C++11 Standard Template Libraries (STD) is call_once, this nifty little method makes sure that specific code is called only once (duh) and it follows these 3 rules:
I needed something similar – I had a method that should only be called once (initialize) and I wanted to implement something similar to the call_once I’ve been using for my C++ development.
My first object was to try and make it as preferment as possible and so I’ve looked for a solution that does not involve locks:
public static class Call
{
    public static void Once(OnceFlag flag,  Action action)
    {
        if (flag.CheckIfNotCalledAndSet)
        {
            action.Invoke();            
        }
    }
}

since I was  trying to mimic the C++ code I wrote two objects Call (above) and OnceFlag which has all of the magic inside using Interlocked:

public class OnceFlag
{
    private const int NotCalled = 0;
    private const int Called = 1;
    private int _state = NotCalled;
    internal bool CheckIfCalledAndSet
    {
        get
        {
            var prev = Interlocked.Exchange(ref _state, Called);
            
            return prev == NotCalled;
        }
    }

    internal void Reset()
    {
        Interlocked.Exchange(ref _state, NotCalled);
    }
}

I’m using Interlocked as a thread-safe way to check & set the value making sure that only once it would return true – try it:

class Program
{
    static OnceFlag _flag = new OnceFlag();
    static void Main(string[] args)
    {
        var t1 = new Thread(() => DoOnce(1));
        var t2 = new Thread(() => DoOnce(2));
        var t3 = new Thread(() => DoOnce(3));
        var t4 = new Thread(() => DoOnce(4));

        t1.Start();
        t2.Start();
        t3.Start();
        t4.Start();

        t1.Join();
        t2.Join();
        t3.Join();
        t4.Join();
    }

    private static void DoOnce(int index)
    {
        Call.Once(_flag, () => Console.WriteLine("Callled (" + index + ")"));
    }
}

It’s very simple solution unfortunately not entirely correct –  the method used will only be called once, but requirements 2 & 3 were not implemented. Luckily for me I didn’t need to make sure that exception enable another call to pass through nor did I need to block other calls until the first call finishes.

But I wanted to try and write a proper implementation, unfortunately not as preferment due to the use of locks:

public static void Once(OnceFlagSimple flag, Action action)
{
    lock (flag)
    {        
        if (flag.CheckIfNotCalled)
        {
            action.Invoke();
    
            flag.Set();            
        }
    }
}

It works, and since I’m already using lock I can split the check and Set methods and use a bool value inside the flag instead of Interlocked.
But not very good performance due to locking on ever time the method gets called.

I’m still looking for a better way to implement call_once – it’s a good exercise in threading and I might find a cool new ways to use the classes under Threading or Task namespaces.

please let me know if you have a better implementation – that’s what the comments are for…

Labels: , , ,