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.IsValueType42 ? 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.