Rev 11 | Rev 43 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
9 | bondari | 1 | <?xml version="1.0" encoding="UTF-8"?> |
41 | jermar | 2 | <chapter id="sync"> |
3 | <?dbhtml filename="sync.html"?> |
||
9 | bondari | 4 | |
41 | jermar | 5 | <title>Mutual exclusion and synchronization</title> |
9 | bondari | 6 | |
41 | jermar | 7 | <section> |
8 | <title>Introduction</title> |
||
9 | bondari | 9 | |
41 | jermar | 10 | <para>The HelenOS operating system is designed to make use of parallelism |
11 | offered by hardware and to exploit concurrency of both the kernel and |
||
12 | userspace tasks. This is achieved through multiprocessor support and |
||
13 | several levels of multiprogramming (i.e. multitasking, multithreading and |
||
14 | through userspace pseudo threads). However, such a highly concurrent |
||
15 | environment needs safe and efficient ways to handle mutual exclusion and |
||
16 | synchronization of many execution flows.</para> |
||
17 | </section> |
||
18 | |||
19 | <section> |
||
20 | <title>Active kernel primitives</title> |
||
21 | |||
9 | bondari | 22 | <section> |
41 | jermar | 23 | <title>Spinlocks</title> |
9 | bondari | 24 | |
41 | jermar | 25 | <para>The basic mutual exclusion primitive is the spinlock. Spinlock |
26 | implements busy waiting for an availability of a memory lock (i.e. |
||
27 | simple variable) in a multiprocessor-safe manner. This safety is |
||
28 | achieved through the use of a specialized, architecture-dependent, |
||
29 | atomic test-and-set operation which either locks the spinlock (i.e. sets |
||
30 | the variable) or, provided that it is already locked, leaves it |
||
31 | unaltered. In any case, the test-and-set operation returns a value, thus |
||
32 | signalling either success (i.e. zero return value) or failure (i.e. |
||
33 | non-zero value) in acquiring the lock. Note that this makes the |
||
34 | fundamental difference between the naive algorithm that doesn't use the |
||
35 | atomic operation and the spinlock algortihm. While the naive algorithm |
||
36 | is prone to race conditions on SMP configuratinos and thus is completely |
||
37 | SMP-unsafe, the spinlock algorithm eliminates the possibility of race |
||
38 | conditions and is suitable for mutual exclusion use.</para> |
||
9 | bondari | 39 | |
41 | jermar | 40 | <para></para> |
9 | bondari | 41 | |
41 | jermar | 42 | <para>The semantics of the test-and-set operation is that the spinlock |
43 | remains unavailable until this operation called on the respective |
||
44 | spinlock returns zero. HelenOS builds two functions on top of |
||
45 | test-and-set operation. The first is the unconditional attempt to |
||
46 | acquire the spinlock and is called <emphasis>spinlock_lock</emphasis>. |
||
47 | It simply loops until test-and-set returns zero. The other operation, |
||
48 | <emphasis>spinlock_trylock</emphasis>, is the conditional lock operation |
||
49 | and calls the test-and-set only once to find out wheter it managed to |
||
50 | acquire the spinlock or not. The conditional operation is useful in |
||
51 | situations in which an algorithm cannot acquire more spinlocks in the |
||
52 | proper order and a deadlock cannot be avoided. In such a case, the |
||
53 | algorithm would detect the danger and instead of possibly deadlocking |
||
54 | the system it would simply release some spinlocks it already holds and |
||
55 | retry the whole operation with the hope that it will succeed next time. |
||
56 | The unlock operation, <emphasis>spinlock_unlock</emphasis>, is quite |
||
57 | easy - it merely clears the spinlock variable.</para> |
||
9 | bondari | 58 | |
41 | jermar | 59 | <para></para> |
9 | bondari | 60 | |
41 | jermar | 61 | <para>Nevertheless, there is a special issue related to hardware |
62 | optimizations that modern processors implement. Particularily |
||
63 | problematic is the out-of-order execution of instructions within the |
||
64 | critical section protected by a spinlock. The processors are always |
||
65 | self-consistent so that they can carry out speculatively executed |
||
66 | instructions in the right order with regard to dependencies among those |
||
67 | instructions. However, the dependency between instructions inside the |
||
68 | critical section and those that implement locking and unlocking of the |
||
69 | respective spinlock is not implicit on some processor architectures and |
||
70 | the processor needs to be explicitly told about each occurrence of such |
||
71 | a dependency. Therefore, HelenOS adds architecture-specific hooks to all |
||
72 | <emphasis>spinlock_lock</emphasis>, |
||
73 | <emphasis>spinlock_trylock</emphasis> and |
||
74 | <emphasis>spinlock_unlock</emphasis> to prevent the instructions inside |
||
75 | the critical section from bleeding out. On some architectures, these |
||
76 | hooks can be a no-op because the dependencies are implicitly there |
||
77 | because of the special properties of locking and unlocking instructions. |
||
78 | However, other architectures need to instrument these hooks with |
||
79 | different memory barriers, depending on what operations can bleed |
||
80 | out.</para> |
||
9 | bondari | 81 | |
41 | jermar | 82 | <para></para> |
9 | bondari | 83 | |
41 | jermar | 84 | <para>Spinlocks have one significant drawback: when held for longer time |
85 | periods, they harm both parallelism and concurrency. Processor executing |
||
86 | <emphasis>spinlock_lock</emphasis> does not do any fruitful work and is |
||
87 | effectively halted until it can grab the lock and proceed. Similarily, |
||
88 | other threads cannot execute on the processor that holds the spinlock |
||
89 | because the kernel disables preemption on that processor when a spinlock |
||
90 | is held. The reason behind disabling preemption is priority inversion |
||
91 | problem avoidance. For the same reason, threads are strongly discouraged |
||
92 | from sleeping when they hold a spinlock.</para> |
||
9 | bondari | 93 | |
41 | jermar | 94 | <para></para> |
9 | bondari | 95 | |
41 | jermar | 96 | <para>To summarize, spinlocks represent very simple and essential mutual |
97 | exclusion primitive for SMP systems. On the other hand, spinlocks scale |
||
98 | poorly because of the active loop they are based on. Therefore, |
||
99 | spinlocks are used in HelenOS only for a short-time mutual exclusion and |
||
100 | in cases where the mutual exclusion is required out of thread context. |
||
101 | Lastly, spinlocks are used in the construction of passive |
||
102 | synchronization primitives.</para> |
||
103 | </section> |
||
104 | </section> |
||
9 | bondari | 105 | |
41 | jermar | 106 | <section> |
107 | <title>Passive kernel synchronization</title> |
||
9 | bondari | 108 | |
41 | jermar | 109 | <section> |
110 | <title>Mutexes</title> |
||
9 | bondari | 111 | |
41 | jermar | 112 | <para>Mutex explanations</para> |
113 | </section> |
||
9 | bondari | 114 | |
41 | jermar | 115 | <section> |
116 | <title>Semaphores</title> |
||
9 | bondari | 117 | |
41 | jermar | 118 | <para>Semaphore explanations</para> |
119 | </section> |
||
9 | bondari | 120 | |
41 | jermar | 121 | <section> |
122 | <title>Read/Write Locks</title> |
||
9 | bondari | 123 | |
41 | jermar | 124 | <para>RWLocks explanation</para> |
125 | </section> |
||
9 | bondari | 126 | |
41 | jermar | 127 | <section> |
128 | <title>Wait queues</title> |
||
9 | bondari | 129 | |
41 | jermar | 130 | <para>Wait queue explanation</para> |
131 | </section> |
||
9 | bondari | 132 | |
133 | <section> |
||
41 | jermar | 134 | <title>Condition variables</title> |
9 | bondari | 135 | |
41 | jermar | 136 | <para>Condvars explanation</para> |
9 | bondari | 137 | </section> |
41 | jermar | 138 | </section> |
9 | bondari | 139 | |
41 | jermar | 140 | <section> |
141 | <title>Userspace synchronization</title> |
||
9 | bondari | 142 | |
41 | jermar | 143 | <section> |
144 | <title>Futexes</title> |
||
145 | |||
146 | <para></para> |
||
147 | </section> |
||
148 | </section> |
||
149 | </chapter> |