Here is the code for Parallel Bug #1:
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Reflection;
5 using System.Threading.Tasks;
6 using BF = System.Reflection.BindingFlags;
7
8 #region A Framework For "Extended Enums"
9
10 public abstract class BaseEnum<T, TEnum>
11 where T : BaseEnum<T, TEnum> {
12 public TEnum Enum { get; private set; }
13 public string Abbr { get; private set; }
14 public string Name { get; private set; }
15
16 private readonly static IDictionary<TEnum, T> _repository
17 = new SortedDictionary<TEnum, T>();
18
19 protected BaseEnum(TEnum @enum, string abbr, string name) {
20 Enum = @enum;
21 Abbr = abbr;
22 Name = name;
23 _repository.Add(Enum, (T)this);
24 }
25
26 private static void EnsureInstantiation() {
27 // Attempt at double checked locking ...
28 if (_repository.Count == 0) {
29 lock (_repository) {
30 if (_repository.Count == 0) {
31 ForceStaticConstructorCall();
32 }
33 }
34 }
35 }
36
37 private static void ForceStaticConstructorCall() {
38 ConstructorInfo constructor = typeof(T)
39 .GetConstructors(BF.Public | BF.NonPublic | BF.Instance)[0];
40 object[] pars = constructor.GetParameters()
41 .Select(p => p.ParameterType.IsValueType
42 ? Activator.CreateInstance(p.ParameterType)
43 : null)
44 .ToArray();
45 try {
46 T dummy = (T) constructor.Invoke(pars);
47 // If we get here, we DID add the dummy to the _repository -
48 // probably because 0 is not in the enum.
49 _repository.Remove(dummy.Enum);
50 } catch {
51 // ignored - we only trigger the static constructor here.
52 }
53 }
54
55 public static IEnumerable<T> GetAll() {
56 EnsureInstantiation();
57 return _repository.Values;
58 }
59
60 public static T Get(TEnum e) {
61 EnsureInstantiation();
62 return _repository[e];
63 }
64 }
65
66 #endregion A Framework For "Extended Enums"
67
68 #region An Application
69
70 public enum Units { km, m, cm, mm, g, kg }
71
72 public class Unit : BaseEnum<Unit, Units> {
73 public Unit BaseUnit { get; private set; }
74 public decimal FactorToBaseUnit { get; private set; }
75
76 private Unit(Units @enum, string abbrev, string longName,
77 Unit baseUnit = null, decimal factor = 1.0m)
78 : base(@enum, abbrev, longName) {
79 BaseUnit = baseUnit ?? this;
80 FactorToBaseUnit = factor;
81 }
82
83 static Unit() {
84 var m = new Unit(Units.m, "m", "meter");
85 new Unit(Units.cm, "cm", "centimeter", m, 100);
86 new Unit(Units.mm, "mm", "millimeter", m, 1000);
87 new Unit(Units.km, "km", "kilometer", m, 0.001m);
88 var kg = new Unit(Units.kg, "kg", "kilogram");
89 new Unit(Units.g, "g", "gram", kg, 1000);
90 }
91 }
92
93 class Program {
94 static void Main() {
95 var t1 = new Task<Unit>(() => Unit.Get(Units.cm));
96 var t2 = new Task<Unit>(() => Unit.Get(Units.mm));
97 t1.Start();
98 t2.Start();
99 Console.WriteLine(t1.Result.FactorToBaseUnit / t2.Result.FactorToBaseUnit);
100 }
101 }
102
103 #endregion An Application
If you run the program, you see as output 0.1. However, this output is not reliable: The program can throw an exception if one of the tasks runs EnsureInstantiation while the other is in the middle of the static constructor - then, only part of _repository will be filled, and Get might not find a key. The bug is, of course, that in the double checked locking code, Count == 0 is not a sufficient condition to check that the initialization is complete.