1 1. This monitor is a model of a database with multiple readers and 2 writers. The high−level goal here is (a) to give a writer exclusive 3 access (a single active writer means there should be no other writers 4 and no readers) while (b) allowing multiple readers. Like the previous 5 example, this one is expressed in pseudocode. 6 7 // assume that these variables are initialized in a constructor 8 state variables: 9 AR = 0; // # active readers 10 AW = 0; // # active writers 11 WR = 0; // # waiting readers 12 WW = 0; // # waiting writers 13 14 Condition okToRead = NIL; 15 Condition okToWrite = NIL; 16 Mutex mutex = FREE; 17 18 Database::read() { 19 startRead(); // first, check self into the system 20 Access Data 21 doneRead(); // check self out of system 22 } 23 24 Database::startRead() { 25 acquire(&mutex); 26 while((AW + WW) > 0){ 27 WR++; 28 wait(&okToRead, &mutex); 29 WR−−; 30 } 31 AR++; 32 release(&mutex); 33 } 34 35 Database::doneRead() { 36 acquire(&mutex); 37 AR−−; 38 if (AR == 0 && WW > 0) { // if no other readers still 39 signal(&okToWrite, &mutex); // active, wake up writer 40 } 41 release(&mutex); 42 } 43 44 Database::write(){ // symmetrical 45 startWrite(); // check in 46 Access Data 47 doneWrite(); // check out 48 } 49 50 Database::startWrite() { 51 acquire(&mutex); 52 while ((AW + AR) > 0) { // check if safe to write. 53 // if any readers or writers, wait 54 WW++; 55 wait(&okToWrite, &mutex); 56 WW−−; 57 } 58 AW++; 59 release(&mutex); 60 } 61 62 Database::doneWrite() { 63 acquire(&mutex); 64 AW−−; 65 if (WW > 0) { 66 signal(&okToWrite, &mutex); // give priority to writers 67 } else if (WR > 0) { 68 broadcast(&okToRead, &mutex); 69 } 70 release(&mutex); 71 } 72 73 NOTE: what is the starvation problem here? 74 75 2. Implementation of spinlocks 76 77 struct Lock { 78 int locked; 79 } 80 81 void acquire(Lock *lock) { 82 while (1) { 83 if (lock−>locked == 0) { // A 84 lock−>locked = 1; // B 85 break; 86 } 87 } 88 } 89 90 void release (Lock *lock) { 91 lock−>locked = 0; 92 } 93 94 Does the above work? If yes, why? Similarly, if not, why? 95 96 3. Another implementation of a spinlock 97 98 99 Relies on atomic hardware instruction. For example, on the x86, 100 doing 101 102 "xchg addr, %eax" does the following: 103 104 (i) freeze all CPUs' memory activity for address addr 105 (ii) temp = *addr 106 (iii) *addr = %eax 107 (iv) %eax = temp 108 (v) un−freeze memory activity 109 110 111 /* pseudocode */ 112 int xchg_val(addr, value) { 113 %eax = value; 114 xchg (*addr), %eax 115 } 116 117 /* bare−bones version of acquire */ 118 void acquire (Lock *lock) { 119 while (xchg_val(&lock−>locked, 1) == 1); 120 } 121 122 void release(Lock *lock){ 123 xchg_val(&lock−>locked, 0); 124 } 125 126 4. Mutex implementation 127 128 The intent of a mutex is to avoid busy waiting: if the lock is not 129 available, the locking thread is put to sleep, and tracked by a 130 queue in the mutex. 131 132 struct Mutex { 133 bool is_held; /* true if mutex held */ 134 thread_id owner; /* thread holding mutex, if locked */ 135 thread_list waiters; /* queue of thread TCBs */ 136 Lock wait_lock; /* as in item 2, above */ 137 } 138 139 The implementation of mutex_acquire() and mutex_release() would 140 be something like: 141 142 void mutex_acquire(Mutex *m) { 143 144 acquire(&m−>wait_lock); /* we spin to acquire wait_lock */ 145 146 while (m−>is_held) { /* someone else has the mutex */ 147 148 m−>waiters.insert(current_thread) 149 release(&m−>wait_lock); 150 151 /* 152 * NOTE! Right here, mutex_release() could execute. To 153 * avoid "losing the wakeup", we check whether we are 154 * on the scheduler’s ready list. If we are, we 155 * shouldn’t yield(). 156 */ 157 158 yield_if_we_are_not_ready(); 159 160 acquire(&m−>wait_lock); /* we spin again */ 161 m−>waiters.remove(current_thread) 162 163 } 164 165 m−>is_held = true; /* we now hold the mutex */ 166 m−>owner = self; 167 168 release(&m−>wait_lock); 169 } 170 171 void mutex_release(Mutex *m) { 172 173 acquire(&m−>wait_lock); /* we spin to acquire wait_lock */ 174 175 m−>is_held = false; 176 m−>owner = 0; 177 178 /* tell scheduler to run a waiter */ 179 place_a_waiter_on_ready_list(m−>waiters); 180 181 release(&m−>wait_lock); 182 183 }