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:
- Exactly one execution of exactly one of the functions (passed as
f
to the invocations in the group) is performed. It is undefined which function will be selected for execution. The selected function runs in the same thread as
thecall_once
invocation it was passed to.
- No invocation in the group returns before the abovementioned execution of the selected function is completed successfully, that is, doesn’t exit via an exception.
- If the selected function exits via exception, it is propagated to the caller. Another function is then selected and executed.
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.
- All other threads are blocked due to lock until first finish running – check!
- In case of exception other method can execute the once block – check!
- If exited properly the block would only execute once – check!
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…
It seems like the ‘OnceFlagSimple’ implementation is missed