New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
InvalidOperationException: ValueFactory attempted to access the Value property #5
Comments
I didn't ask for thread-safety for lazy initialization because I didn't need it. However, reading the I've only run this through my head and writing out my theory loud to bounce it off you. Let me know if you think I've gone completely off a cliff. I'll try and test out my theory tomorrow with some code & then make a fix. |
I've taken a look at Lazy sources you pointed and it seems like you're right. I've made small test program in linqpad and it shows the issue(if change LazyThreadSafetyMode from None - issue is gone): void Main()
{
var lazy = new Lazy<int>(() => { Thread.Sleep(1000 * 10); return 1; }, LazyThreadSafetyMode.None);
var tasks = Enumerable
.Range(0, 10)
.Select(_ => { Thread.Sleep(100); return Task.Run(() => lazy.Value); })
.Cast<Task>()
.ToArray();
Task.WaitAll(tasks);
} |
@kirill-gerasimenko Thanks for sharing your results. I've also written a test to try out the theory and reaching the same conclusion and will share it shortly. |
Following is a test console program I wrote to exercise namespace LazyTest
{
#region Imports
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading;
using static System.Console;
#endregion
static class ThreadStatics
{
[ThreadStatic] public static int Id;
}
class Program
{
public static LazyThreadSafetyMode LazyThreadSafetyMode = LazyThreadSafetyMode.None;
static void Main(string[] args)
{
var debuggable = (DebuggableAttribute)
Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(),
typeof(DebuggableAttribute));
WriteLine($"DebuggableAttribute.IsJITOptimizerDisabled = {debuggable?.IsJITOptimizerDisabled}");
var options = new
{
Threads =
args.Where(s => Regex.IsMatch(s, "^([0-9]+)$"))
.Select(s => (int?)int.Parse(s))
.FirstOrDefault() ?? 20,
LazyThreadSafetyMode =
args.Where(s => Enum.GetNames(typeof(LazyThreadSafetyMode))
.Any(n => n.Equals(s, StringComparison.OrdinalIgnoreCase)))
.Select(s => (LazyThreadSafetyMode?)
Enum.Parse(typeof(LazyThreadSafetyMode), s, ignoreCase: true))
.FirstOrDefault() ?? LazyThreadSafetyMode.None,
};
WriteLine($"Threads = {options.Threads}, LazyThreadSafetyMode = {options.LazyThreadSafetyMode}");
WriteLine("* = Thread ran value factory");
LazyThreadSafetyMode = options.LazyThreadSafetyMode;
using (var ready = new ManualResetEvent(initialState: false))
{
var workerz =
from readee in new[] { ready }
from id in Enumerable.Range(1, options.Threads)
let bar = new int?[1] // by-reference
let error = new Exception[1] // by-reference
select new
{
Id = id,
Bar = bar,
Error = error,
Thread = new Thread(() =>
{
ThreadStatics.Id = id;
readee.WaitOne();
try { bar[0] = Foo.Bar; }
catch (Exception e) { error[0] = e; }
})
};
var workers = workerz.ToArray();
if (workers.Length < options.Threads)
WriteLine($"Warning! Using max threads of {workers.Length}.");
Array.ForEach(workers, t => t.Thread.Start());
ready.Set(); // go!
Array.ForEach(workers, w => w.Thread.Join());
foreach (var w in workers)
{
var initialized = FooBarInitializers.Contains(w.Thread) ? '*' : ' ';
WriteLine($"{w.Id,3} {initialized} : Bar = {w.Bar[0],3}, Error = {w.Error[0]?.GetBaseException().Message}");
}
}
}
static readonly List<Thread> FooBarInitializers = new List<Thread>();
public static void OnFooBarInitializing()
{
lock (FooBarInitializers)
FooBarInitializers.Add(Thread.CurrentThread);
}
}
static class Foo
{
public static int Bar => LazyBar.Value;
static readonly Lazy<int> LazyBar = new Lazy<int>(CreateBar, Program.LazyThreadSafetyMode);
static int CreateBar()
{
Program.OnFooBarInitializing();
Thread.Yield(); // entropy
return ThreadStatics.Id;
}
}
} I used good old threads instead of tasks because I didn't want any pooling or task-inlining to come into effect & interfere with the observed results. The program starts by printing whether or not JIT optimizations are in effect (debug vs. release build). It then parses the command line arguments, which are allowed to be specified in any order. If an argument is a number then it's taken as the number of threads to exercise with, with the default being 20. If it's one of the The threads are created and wait until each one has had a chance to initialize a logical and stable thread ID ( The results of each thread's observations, including any error, are printed at the end. So with all that said and done, here are the results (using 10 threads):
Clearly, it's a mess. With
And finally using
|
This is great! Thanks a lot for a quick fix! |
Hi,
I'm using Bootstrapper from nuget package elmah.bootstrapper.1.0.0-beta5 and from time to time I've got the following exception (couldn't find the stable way to reproduce though).
Usually it happens when I rebuild the project with IISExpress running, trying to F5 gives error, after another F5 in browser - all works fine. If site is running under IIS - i need to recycle the app pool.
If you need more details I can provide them, thanks!
The text was updated successfully, but these errors were encountered: