1 /* 2 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <locks.h> 8 9 #include <OS.h> 10 11 #include <syscalls.h> 12 #include <user_thread.h> 13 14 15 typedef struct rw_lock_waiter { 16 rw_lock_waiter * next; 17 thread_id thread; 18 bool writer; 19 } rw_lock_waiter; 20 21 22 static status_t 23 rw_lock_wait(rw_lock *lock, bool writer) 24 { 25 rw_lock_waiter waiter; 26 waiter.thread = find_thread(NULL); 27 waiter.next = NULL; 28 waiter.writer = writer; 29 30 if (lock->waiters != NULL) 31 lock->last_waiter->next = &waiter; 32 else 33 lock->waiters = &waiter; 34 35 lock->last_waiter = &waiter; 36 37 // the rw_lock is locked when entering, release it before blocking 38 get_user_thread()->wait_status = 1; 39 mutex_unlock(&lock->lock); 40 41 status_t result; 42 do { 43 result = _kern_block_thread(0, 0); 44 } while (result == B_INTERRUPTED); 45 46 // and lock it again before returning 47 mutex_lock(&lock->lock); 48 return result; 49 } 50 51 52 static void 53 rw_lock_unblock(rw_lock *lock) 54 { 55 // this is called locked 56 if (lock->holder >= 0) 57 return; 58 59 rw_lock_waiter *waiter = lock->waiters; 60 if (waiter == NULL) 61 return; 62 63 if (waiter->writer) { 64 if (lock->reader_count > 0) 65 return; 66 67 lock->waiters = waiter->next; 68 lock->holder = waiter->thread; 69 _kern_unblock_thread(waiter->thread, B_OK); 70 return; 71 } 72 73 while (waiter != NULL && !waiter->writer) { 74 lock->reader_count++; 75 lock->waiters = waiter->next; 76 _kern_unblock_thread(waiter->thread, B_OK); 77 waiter = lock->waiters; 78 } 79 } 80 81 82 void 83 rw_lock_init(rw_lock *lock, const char *name) 84 { 85 rw_lock_init_etc(lock, name, 0); 86 } 87 88 89 void 90 rw_lock_init_etc(rw_lock *lock, const char *name, uint32 flags) 91 { 92 lock->waiters = NULL; 93 lock->holder = -1; 94 lock->reader_count = 0; 95 lock->writer_count = 0; 96 lock->owner_count = 0; 97 mutex_init_etc(&lock->lock, name, flags); 98 } 99 100 101 void 102 rw_lock_destroy(rw_lock *lock) 103 { 104 mutex_lock(&lock->lock); 105 106 rw_lock_waiter *waiter = lock->waiters; 107 while (waiter != NULL) { 108 _kern_unblock_thread(waiter->thread, B_ERROR); 109 waiter = waiter->next; 110 } 111 112 mutex_destroy(&lock->lock); 113 } 114 115 116 status_t 117 rw_lock_read_lock(rw_lock *lock) 118 { 119 MutexLocker locker(lock->lock); 120 121 if (lock->writer_count == 0) { 122 lock->reader_count++; 123 return B_OK; 124 } 125 126 if (lock->holder == find_thread(NULL)) { 127 lock->owner_count++; 128 return B_OK; 129 } 130 131 return rw_lock_wait(lock, false); 132 } 133 134 135 status_t 136 rw_lock_read_unlock(rw_lock *lock) 137 { 138 MutexLocker locker(lock->lock); 139 140 if (lock->holder == find_thread(NULL)) { 141 if (--lock->owner_count > 0) 142 return B_OK; 143 144 // this originally has been a write lock 145 lock->writer_count--; 146 lock->holder = -1; 147 148 rw_lock_unblock(lock); 149 return B_OK; 150 } 151 152 if (lock->reader_count <= 0) { 153 debugger("rw_lock not read locked"); 154 return B_ERROR; 155 } 156 157 lock->reader_count--; 158 rw_lock_unblock(lock); 159 return B_OK; 160 } 161 162 163 status_t 164 rw_lock_write_lock(rw_lock *lock) 165 { 166 MutexLocker locker(lock->lock); 167 168 if (lock->reader_count == 0 && lock->writer_count == 0) { 169 lock->writer_count++; 170 lock->holder = find_thread(NULL); 171 lock->owner_count = 1; 172 return B_OK; 173 } 174 175 if (lock->holder == find_thread(NULL)) { 176 lock->owner_count++; 177 return B_OK; 178 } 179 180 lock->writer_count++; 181 182 status_t result = rw_lock_wait(lock, true); 183 if (result != B_OK) 184 return result; 185 186 if (lock->holder != find_thread(NULL)) { 187 debugger("write locked but holder not set"); 188 return B_ERROR; 189 } 190 191 lock->owner_count = 1; 192 return B_OK; 193 } 194 195 196 status_t 197 rw_lock_write_unlock(rw_lock *lock) 198 { 199 MutexLocker locker(lock->lock); 200 201 if (lock->holder != find_thread(NULL)) { 202 debugger("rw_lock not write locked"); 203 return B_ERROR; 204 } 205 206 if (--lock->owner_count > 0) 207 return B_OK; 208 209 lock->writer_count--; 210 lock->holder = -1; 211 rw_lock_unblock(lock); 212 return B_OK; 213 } 214