System.Lazy <T> with various thread protection mode
.NET 4.0 The System.Lazy <T> class offers three Thread-per-en protection modes through the enum LazyThreadSafetyMode , which I will summarize as:
- LazyThreadSafetyMode.None . Insecure thread.
- LazyThreadSafetyMode.ExecutionAndPublication . Only one parallel thread will attempt to create a base value. Upon successful creation, all pending threads will receive the same value. If an unhandled exception occurs during creation, it will be re-selected for each waiting thread, cached, and re-thrown each time it tries to access the base value.
- LazyThreadSafetyMode.PublicationOnly . Several parallel threads will try to create a base value, but the first will determine the value passed to all threads for success. If an unhandled exception occurs during creation, it will not be cached in parallel, and subsequent attempts to access the base value will repeat the creation attempt and may succeed.
I would like to have a lazily initialized value that follows several other thread safety rules, namely:
Only one parallel thread will attempt to create a base value. Upon successful creation, all pending threads will receive the same value. If an unhandled exception occurs during creation, it will be re-selected for each waiting thread, but it will not be cached, and subsequent attempts to access the base value will repeat the creation attempt and may succeed.
Thus, the key is different in that LazyThreadSafetyMode.ExecutionAndPublication is that if the "first transition" is not performed during creation, it can be repeated later.
Is there an existing (.NET 4.0) class that offers this semantics, or will I have to collapse myself? If I refuse my own, is there a sensible way to reuse an existing Lazy <T> within an implementation to avoid explicit blocking / synchronization?
NB In the case of use, assume that the "creation" is potentially expensive and is prone to intermittent errors, including, for example, receiving a large amount of data from a remote server. I would not want to make several parallel attempts to get the data, as they are most likely to fail or everything will succeed. However, if they fail, I would like to try again later.
My attempt to Darin's version of the updated answer , in which there is no race condition that I indicated ... a warning, I'm not entirely sure that this is finally completely free of race conditions.
private static int waiters = 0; private static volatile Lazy<object> lazy = new Lazy<object>(GetValueFromSomewhere); public static object Value { get { Lazy<object> currLazy = lazy; if (currLazy.IsValueCreated) return currLazy.Value; Interlocked.Increment(ref waiters); try { return lazy.Value; // just leave "waiters" at whatever it is... no harm in it. } catch { if (Interlocked.Decrement(ref waiters) == 0) lazy = new Lazy<object>(GetValueFromSomewhere); throw; } } } Update: I thought I found a race condition after posting this. The behavior should be really acceptable if you are okay with the supposedly rare case when some thread throws an exception that it observed from a slow Lazy<T> after another thread has already returned from a successful fast Lazy<T> (future requests everything will be successful).
waiters= 0- t1: comes on the run until just before
Interlocked.Decrement(waiters= 1) - t2: enters and starts immediately before
Interlocked.Increment(waiters= 1) - t1: makes it
Interlocked.Decrementand prepares to overwrite (waiters= 0) - t2: works until
Interlocked.Decrement(waiters= 1) - t1: overwrite
lazynew (name itlazy1) (waiters= 1) - t3: enters and blocks on
lazy1(waiters= 2) - t2: does it
Interlocked.Decrement(waiters= 1) - t3: gets and returns the value from
lazy1(waitersnow doesn't matter) - t2: restores an exception
I can’t come up with a sequence of events that will lead to something worse than "this thread threw an exception after another thread gave a successful result."
Update2: declared lazy as volatile to ensure that guarded rewriting will be visible to all readers immediately. Some people (including me) see volatile and immediately think “well, that they are probably misused”, and they are usually right. That's why I used it here: in the sequence of events from the above example, t3 could still read the old lazy instead of lazy1 if it was placed immediately before reading lazy.Value moment t1 was changed lazy contain lazy1 . volatile protects against this, so the next attempt can start immediately.
I also reminded myself why I had this in the back of my head, saying: "Low blocking of simultaneous programming is difficult, just use the C # lock operator !!!" all the time when I wrote the original answer.
Update3: just changed some text in Update2, indicating the actual circumstance that makes volatile necessary - the Interlocked operations used here, apparently, are implemented in full-function mode on important processor architectures today, and not half the fence. I was initially just supposed , so volatile protects a much narrower section than I originally thought.
Only one parallel thread will attempt to create a base value. Upon successful creation, all pending threads will receive the same value. If an unhandled exception occurs during creation, it will be thrown repeatedly for each waiting thread, but it will not be cached and subsequent attempts to access the base value will repeat the creation and may succeed.
Since Lazy does not support this, you can try to minimize it yourself:
private static object syncRoot = new object(); private static object value = null; public static object Value { get { if (value == null) { lock (syncRoot) { if (value == null) { // Only one concurrent thread will attempt to create the underlying value. // And if `GetTheValueFromSomewhere` throws an exception, then the value field // will not be assigned to anything and later access // to the Value property will retry. As far as the exception // is concerned it will obviously be propagated // to the consumer of the Value getter value = GetTheValueFromSomewhere(); } } } return value; } } UPDATE:
To satisfy your requirements for the same exception that apply to all pending read threads:
private static Lazy<object> lazy = new Lazy<object>(GetTheValueFromSomewhere); public static object Value { get { try { return lazy.Value; } catch { // We recreate the lazy field so that subsequent readers // don't just get a cached exception but rather attempt // to call the GetTheValueFromSomewhere() expensive method // in order to calculate the value again lazy = new Lazy<object>(GetTheValueFromSomewhere); // Re-throw the exception so that all blocked reader threads // will get this exact same exception thrown. throw; } } } Lazy does not support this. This is a design problem with Lazy, because the exception “caching” means that this lazy instance will not be of real value forever. This can cause applications to run continuously due to temporary errors, such as network problems. Human intervention is usually required.
I am sure that this land mine exists in quite a few .NET applications ...
You need to write your lazy to do this. Or open a CoreFx Github issue for it.
Partly inspired by Darin's answer , but trying to get this “queue of waiting threads that were thrown by an exception”, and the “try again” functions work:
private static Task<object> _fetcher = null; private static object _value = null; public static object Value { get { if (_value != null) return _value; //We're "locking" then var tcs = new TaskCompletionSource<object>(); var tsk = Interlocked.CompareExchange(ref _fetcher, tcs.Task, null); if (tsk == null) //We won the race to set up the task { try { var result = new object(); //Whatever the real, expensive operation is tcs.SetResult(result); _value = result; return result; } catch (Exception ex) { Interlocked.Exchange(ref _fetcher, null); //We failed. Let someone else try again in the future tcs.SetException(ex); throw; } } tsk.Wait(); //Someone else is doing the work return tsk.Result; } } I'm a little worried that - can anyone see any obvious races here where it will fail in an unobvious form?
Something like this might help:
using System; using System.Threading; namespace ADifferentLazy { /// <summary> /// Basically the same as Lazy with LazyThreadSafetyMode of ExecutionAndPublication, BUT exceptions are not cached /// </summary> public class LazyWithNoExceptionCaching<T> { private Func<T> valueFactory; private T value = default(T); private readonly object lockObject = new object(); private bool initialized = false; private static readonly Func<T> ALREADY_INVOKED_SENTINEL = () => default(T); public LazyWithNoExceptionCaching(Func<T> valueFactory) { this.valueFactory = valueFactory; } public bool IsValueCreated { get { return initialized; } } public T Value { get { //Mimic LazyInitializer.EnsureInitialized() double-checked locking, whilst allowing control flow to clear valueFactory on successful initialisation if (Volatile.Read(ref initialized)) return value; lock (lockObject) { if (Volatile.Read(ref initialized)) return value; value = valueFactory(); Volatile.Write(ref initialized, true); } valueFactory = ALREADY_INVOKED_SENTINEL; return value; } } } }