I'm working on a huge system based on Asp.net MVC 3.0 and working on Mono-2.10.8 (Windows 7).
Everything was fine until a moment couple of days ago.
Inside my API I have several utility classes using dictionaries. For example, like this one:
public static class KeyUtility
{
static KeyUtility() {
Alphabet = new[] {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S',
'T', 'U', 'V', 'X', 'Y', 'Z', '0', '1',
'2', '3', '4', '5', '6', '7', '8', '9'
};
ReverseAlphabet = Alphabet
.Select((c, i) => new { Char = c, Value = i })
.ToDictionary(k => k.Char, v => (byte) v.Value);
}
internal static char[] Alphabet;
private static IDictionary<char, byte> ReverseAlphabet;
public static string ToKey(byte[] key, int groupSize)
{
//Accessing Alphabet to generate string key from bytes
}
public static byte[] FromKey(string key)
{
//Accessing ReverseAlphabet to get bytes from string key
}
}
And randomly I get exceptions like this:
System.IndexOutOfRangeException: Array index is out of range.
at System.Collections.Generic.Dictionary`2<char, byte>.TryGetValue (char,byte&) <0x000a1>
at MyAPI.KeyUtility.FromKey (string) <0x0009a>
at MyApp.Controllers.AboutController.Index () <0x002df>
at (wrapper dynamic-method) object.lambda_method (System.Runtime.CompilerServices.Closure,System.Web.Mvc.ControllerBase,object[]) <0x0002f>
at System.Web.Mvc.ActionMethodDispatcher.Execute (System.Web.Mvc.ControllerBase,object[]) <0x0001b>
at System.Web.Mvc.ReflectedActionDescriptor.Execute (System.Web.Mvc.ControllerContext,System.Collections.Generic.IDictionary`2<string, object>) <0x000ff>
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod (System.Web.Mvc.ControllerContext,System.Web.Mvc.ActionDescriptor,System.Collections.Generic.IDictionary`2<string, object>) <0x00019>
at System.Web.Mvc.ControllerActionInvoker/<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12 () <0x00066>
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter (System.Web.Mvc.IActionFilter,System.Web.Mvc.ActionExecutingContext,System.Func`1<System.Web.Mvc.ActionExecutedContext>) <0x000b8>
Most of the time everything is fine and KeyUtility
works correct, but on rare occasions I get such an exception.
Despite it looks like thread safety issue, the dictionary ReverseAlphabet
is always accessed for reading only and never for writing. Once it's created in static constructor it's only accessed with TryGetValue
. As I understand from MSDN article it should be thread safe in this case.
Moreover, I haven't seen this problem before.
What should I look at? I even can't create a reproduction as I have completely no idea what is wrong.
Mono-2.10.8 and older versions proved to be stable with dictionaries. I've been using it for a couple of years intensively and have never seen this sort of exception before.
How to fix this?
UPD:
I've remembered that near the time of the begining of troubles what i've done is statically linked mono with my executable (i'm embedding mono into my application). I simply downloaded sources of mono. Compilled it without any changes except i set up libmono's output to static library. I've also linked with libeay32 and sqlite3. All multithread (MT). Maybe this change could affect an application? Unfortunately i can't check this under standalone mono. Before this i was linking all libraries dynamically and everything was fine.
UPD2: Here is the link to the complete sources: http://pastebin.com/RU4RNCki
The Dictionary<TKey, TValue> Class in C# is a collection of Keys and Values. It is a generic collection class in the System. Collections. Generic namespace. The Dictionary <TKey, TValue> generic class provides a mapping from a set of keys to a set of values.
In C#, Dictionary is a generic collection which is generally used to store key/value pairs. The working of Dictionary is quite similar to the non-generic hashtable. The advantage of Dictionary is, it is generic type. Dictionary is defined under System. Collection.
It is a class hence it is a Reference Type.
what i've done is statically linked mono with my executable
You already know the problem I think, that certainly would break mono. The most important thing that doesn't happen anymore when you link it statically is the DllMain() callback that Windows makes whenever a new thread starts executing in the process. That's how the CLR becomes aware of threads that may execute managed code. Static constructors are indeed a likely failure mode, a thread must be blocked from executing any code in the class until the cctor() finished executing. The CLR cannot do this if it doesn't know about the thread.
If you want to make this work then you'll at least need to provide an alternative for the mono_thread_info_attach() call in your own DllMain() function. In general, I'd say this was one optimization too many.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With