1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <RWLockManager.h> 7 8 #include <AutoLocker.h> 9 10 #include <syscalls.h> 11 #include <user_thread.h> 12 13 14 RWLockable::RWLockable() 15 : 16 fOwner(-1), 17 fOwnerCount(0), 18 fReaderCount(0) 19 { 20 } 21 22 23 RWLockManager::RWLockManager() 24 : 25 fLock("r/w lock manager") 26 { 27 } 28 29 30 RWLockManager::~RWLockManager() 31 { 32 } 33 34 35 bool 36 RWLockManager::ReadLock(RWLockable* lockable) 37 { 38 AutoLocker<RWLockManager> locker(this); 39 40 if (lockable->fWaiters.IsEmpty()) { 41 lockable->fReaderCount++; 42 return true; 43 } 44 45 return _Wait(lockable, false, B_INFINITE_TIMEOUT) == B_OK; 46 } 47 48 49 bool 50 RWLockManager::TryReadLock(RWLockable* lockable) 51 { 52 AutoLocker<RWLockManager> locker(this); 53 54 if (lockable->fWaiters.IsEmpty()) { 55 lockable->fReaderCount++; 56 return true; 57 } 58 59 return false; 60 } 61 62 63 status_t 64 RWLockManager::ReadLockWithTimeout(RWLockable* lockable, bigtime_t timeout) 65 { 66 AutoLocker<RWLockManager> locker(this); 67 68 if (lockable->fWaiters.IsEmpty()) { 69 lockable->fReaderCount++; 70 return B_OK; 71 } 72 73 return _Wait(lockable, false, timeout); 74 } 75 76 77 void 78 RWLockManager::ReadUnlock(RWLockable* lockable) 79 { 80 AutoLocker<RWLockManager> locker(this); 81 82 if (lockable->fReaderCount <= 0) { 83 debugger("RWLockManager::ReadUnlock(): Not read-locked!"); 84 return; 85 } 86 87 if (--lockable->fReaderCount == 0) 88 _Unblock(lockable); 89 } 90 91 92 bool 93 RWLockManager::WriteLock(RWLockable* lockable) 94 { 95 AutoLocker<RWLockManager> locker(this); 96 97 thread_id thread = find_thread(NULL); 98 99 if (lockable->fOwner == thread) { 100 lockable->fOwnerCount++; 101 return true; 102 } 103 104 if (lockable->fReaderCount == 0 && lockable->fWaiters.IsEmpty()) { 105 lockable->fOwnerCount = 1; 106 lockable->fOwner = find_thread(NULL); 107 return true; 108 } 109 110 return _Wait(lockable, true, B_INFINITE_TIMEOUT) == B_OK; 111 } 112 113 114 bool 115 RWLockManager::TryWriteLock(RWLockable* lockable) 116 { 117 AutoLocker<RWLockManager> locker(this); 118 119 thread_id thread = find_thread(NULL); 120 121 if (lockable->fOwner == thread) { 122 lockable->fOwnerCount++; 123 return true; 124 } 125 126 if (lockable->fReaderCount == 0 && lockable->fWaiters.IsEmpty()) { 127 lockable->fOwnerCount++; 128 lockable->fOwner = thread; 129 return true; 130 } 131 132 return false; 133 } 134 135 136 status_t 137 RWLockManager::WriteLockWithTimeout(RWLockable* lockable, bigtime_t timeout) 138 { 139 AutoLocker<RWLockManager> locker(this); 140 141 thread_id thread = find_thread(NULL); 142 143 if (lockable->fOwner == thread) { 144 lockable->fOwnerCount++; 145 return B_OK; 146 } 147 148 if (lockable->fReaderCount == 0 && lockable->fWaiters.IsEmpty()) { 149 lockable->fOwnerCount++; 150 lockable->fOwner = thread; 151 return B_OK; 152 } 153 154 return _Wait(lockable, true, timeout); 155 } 156 157 158 void 159 RWLockManager::WriteUnlock(RWLockable* lockable) 160 { 161 AutoLocker<RWLockManager> locker(this); 162 163 if (find_thread(NULL) != lockable->fOwner) { 164 debugger("RWLockManager::WriteUnlock(): Not write-locked by calling " 165 "thread!"); 166 return; 167 } 168 169 if (--lockable->fOwnerCount == 0) { 170 lockable->fOwner = -1; 171 _Unblock(lockable); 172 } 173 } 174 175 176 status_t 177 RWLockManager::_Wait(RWLockable* lockable, bool writer, bigtime_t timeout) 178 { 179 if (timeout == 0) 180 return B_TIMED_OUT; 181 182 // enqueue a waiter 183 RWLockable::Waiter waiter(writer); 184 lockable->fWaiters.Add(&waiter); 185 waiter.queued = true; 186 get_user_thread()->wait_status = 1; 187 188 // wait 189 Unlock(); 190 191 status_t error; 192 do { 193 error = _kern_block_thread( 194 timeout >= 0 ? B_RELATIVE_TIMEOUT : 0, timeout); 195 // TODO: When interrupted we should adjust the timeout, respectively 196 // convert to an absolute timeout in the first place! 197 } while (error == B_INTERRUPTED); 198 199 Lock(); 200 201 if (!waiter.queued) 202 return waiter.status; 203 204 // we're still queued, which means an error (timeout, interrupt) 205 // occurred 206 lockable->fWaiters.Remove(&waiter); 207 208 _Unblock(lockable); 209 210 return error; 211 } 212 213 214 void 215 RWLockManager::_Unblock(RWLockable* lockable) 216 { 217 // Check whether there any waiting threads at all and whether anyone 218 // has the write lock 219 RWLockable::Waiter* waiter = lockable->fWaiters.Head(); 220 if (waiter == NULL || lockable->fOwner >= 0) 221 return; 222 223 // writer at head of queue? 224 if (waiter->writer) { 225 if (lockable->fReaderCount == 0) { 226 waiter->status = B_OK; 227 waiter->queued = false; 228 lockable->fWaiters.Remove(waiter); 229 lockable->fOwner = waiter->thread; 230 lockable->fOwnerCount = 1; 231 232 _kern_unblock_thread(waiter->thread, B_OK); 233 } 234 return; 235 } 236 237 // wake up one or more readers -- we unblock more than one reader at 238 // a time to save trips to the kernel 239 while (!lockable->fWaiters.IsEmpty() 240 && !lockable->fWaiters.Head()->writer) { 241 static const int kMaxReaderUnblockCount = 128; 242 thread_id readers[kMaxReaderUnblockCount]; 243 int readerCount = 0; 244 245 while (readerCount < kMaxReaderUnblockCount 246 && (waiter = lockable->fWaiters.Head()) != NULL 247 && !waiter->writer) { 248 waiter->status = B_OK; 249 waiter->queued = false; 250 lockable->fWaiters.Remove(waiter); 251 252 readers[readerCount++] = waiter->thread; 253 lockable->fReaderCount++; 254 } 255 256 if (readerCount > 0) 257 _kern_unblock_threads(readers, readerCount, B_OK); 258 } 259 } 260