Skip to content

Lazy (and AsyncLazy) don't handle exceptions very well #73

@mariusGundersen

Description

@mariusGundersen

Lazy has 3 modes of operation, but it is missing the mode that would be most useful in LazyCache, namely supporting multi-threading and not caching exceptions. Since that mode is missing the current implementation will cache the exception thrown by the factory function and rethrow it for every access to the lazy. That's not a good thing when the Lazy itself is cached, as it means that every access of the cached value results in a thrown exception! More information about the problem is here: https://github.com/dotnet/corefx/issues/32337

There is a simple replacement for Lazy that can be used instead:

public class AtomicLazy<T>
{
    private readonly Func<T> _factory;

    private T _value;

    private bool _initialized;

    private object _lock;

    public AtomicLazy(Func<T> factory)
    {
        _factory = factory;
    }

    public T Value => LazyInitializer.EnsureInitialized(ref _value, ref _initialized, ref _lock, _factory);
}

This uses the same underlying LazyInitializer as Lazy, but it only supports one mode, the one that is missing (and, imo, most useful).

And for AsyncLazy:

public class AsyncLazy<T>
{
    private readonly Func<Task<T>> _factory;
    
    private Task<T> _task;

    private bool _initialized;

    private object _lock;

    public AsyncLazy(Func<Task<T>> factory)
    {
        _factory = factory;
    }

    public async Task<T> Value()
    {
        try
        {
            return await LazyInitializer.EnsureInitialized(ref _task, ref _initialized, ref _lock, _factory);
        }
        catch
        {
            Volatile.Write(ref _initialized, false);
            throw;
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions