Add
[mono.git] / docs / thread-safety.txt
1
2 1. Thread safety of metadata structures
3 ----------------------------------------
4
5 1.1 Synchronization of read-only data
6 -------------------------------------
7
8 Read-only data is data which is not modified after creation, like the
9 actual binary metadata in the metadata tables.
10
11 There are three kinds of threads with regards to read-only data:
12 - readers
13 - the creator of the data
14 - the destroyer of the data
15
16 Most threads are readers.
17
18 - synchronization between readers is not neccesary
19 - synchronization between the writers is done using locks.
20 - synchronization between the readers and the creator is done by not exposing
21   the data to readers before it is fully constructed.
22 - synchronization between the readers and the destroyer: TBD.
23
24 1.2 Deadlock prevention plan
25 ----------------------------
26
27 Hold locks for the shortest time possible. Avoid calling functions inside 
28 locks which might obtain global locks (i.e. locks known outside this module).
29
30 1.3 Locks
31 ----------
32
33 1.3.1 Simple locks
34 ------------------
35
36  There are a lot of global data structures which can be protected by a 'simple' lock. Simple means:
37     - the lock protects only this data structure or it only protects the data structures in a given C module.
38       An example would be the appdomains list in domain.c
39     - the lock is only held for a short amount of time, and no other lock is acquired inside this simple lock. Thus there is
40       no possibility of deadlock.
41
42 1.3.2 The class loader lock
43 ---------------------------
44
45 This locks is held by the class loading routines in class.c and loader.c. It
46 protects the various caches inside MonoImage which are used by these modules.
47
48 1.3.3 The domain lock
49 ---------------------
50
51 Each appdomain has a lock which protects the per-domain data structures.
52
53 1.3.4 The locking hierarchy
54 ---------------------------
55
56 It is useful to model locks by a locking hierarchy, which is a relation between locks, which is reflexive, transitive,
57 and antisymmetric, in other words, a lattice. If a thread wants to acquire a lock B, while already holding A, it can only
58 do it if A < B. If all threads work this way, then no deadlocks can occur.
59
60 Our locking hierarchy so far looks like this:
61     <DOMAIN LOCK>
62         \
63         <CLASS LOADER LOCK>
64                 \                       \
65         <SIMPLE LOCK 1>         <SIMPLE LOCK 2>
66
67 1.4 Notes
68 ----------
69
70 Some common scenarios:
71 - if a function needs to access a data structure, then it should lock it itself, and do not count on its caller locking it.
72   So for example, the image->class_cache hash table would be locked by mono_class_get().
73
74 - there are lots of places where a runtime data structure is created and stored in a cache. In these places, care must be 
75   taken to avoid multiple threads creating the same runtime structure, for example, two threads might call mono_class_get () 
76   with the same class name. There are two choices here:
77
78         <enter mutex>
79         <check that item is created>
80         if (created) {
81                 <leave mutex>
82                 return item
83         }
84         <create item>
85         <store it in cache>
86         <leave mutex>
87
88       This is the easiest solution, but it requires holding the lock for the whole time which might create a scalability        problem, and could also lead to deadlock.
89
90         <enter mutex>
91         <check that item is created>
92         <leave mutex>
93         if (created) {
94                 return item
95         }
96         <create item>
97         <enter mutex>
98         <check that item is created>
99         if (created) {
100                 /* Another thread already created and stored the same item */
101                 <free our item>
102                 <leave mutex>
103                 return orig item
104         }
105         else {
106                 <store item in cache>
107                 <leave mutex>
108                 return item
109         }
110
111         This solution does not present scalability problems, but the created item might be hard to destroy (like a MonoClass).
112
113 - lazy initialization of hashtables etc. is not thread safe