1 /* 2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <pthread.h> 7 8 #include <new> 9 10 #include <Debug.h> 11 12 #include <AutoLocker.h> 13 #include <syscalls.h> 14 #include <user_mutex_defs.h> 15 #include <user_thread.h> 16 #include <util/DoublyLinkedList.h> 17 18 #include "pthread_private.h" 19 20 #define MAX_READER_COUNT 1000000 21 22 #define RWLOCK_FLAG_SHARED 0x01 23 24 25 struct Waiter : DoublyLinkedListLinkImpl<Waiter> { 26 Waiter(bool writer) 27 : 28 userThread(get_user_thread()), 29 thread(find_thread(NULL)), 30 writer(writer), 31 queued(false) 32 { 33 } 34 35 user_thread* userThread; 36 thread_id thread; 37 status_t status; 38 bool writer; 39 bool queued; 40 }; 41 42 typedef DoublyLinkedList<Waiter> WaiterList; 43 44 45 struct SharedRWLock { 46 uint32_t flags; 47 int32_t owner; 48 int32_t sem; 49 50 status_t Init() 51 { 52 flags = RWLOCK_FLAG_SHARED; 53 owner = -1; 54 sem = create_sem(MAX_READER_COUNT, "pthread rwlock"); 55 56 return sem >= 0 ? B_OK : EAGAIN; 57 } 58 59 status_t Destroy() 60 { 61 if (sem < 0) 62 return B_BAD_VALUE; 63 return delete_sem(sem) == B_OK ? B_OK : B_BAD_VALUE; 64 } 65 66 status_t ReadLock(bigtime_t timeout) 67 { 68 return acquire_sem_etc(sem, 1, 69 timeout >= 0 ? B_ABSOLUTE_REAL_TIME_TIMEOUT : 0, timeout); 70 } 71 72 status_t WriteLock(bigtime_t timeout) 73 { 74 status_t error = acquire_sem_etc(sem, MAX_READER_COUNT, 75 timeout >= 0 ? B_ABSOLUTE_REAL_TIME_TIMEOUT : 0, timeout); 76 if (error == B_OK) 77 owner = find_thread(NULL); 78 return error; 79 } 80 81 status_t Unlock() 82 { 83 if (find_thread(NULL) == owner) { 84 owner = -1; 85 return release_sem_etc(sem, MAX_READER_COUNT, 0); 86 } else 87 return release_sem(sem); 88 } 89 }; 90 91 92 struct LocalRWLock { 93 uint32_t flags; 94 int32_t owner; 95 int32_t mutex; 96 int32_t unused; 97 int32_t reader_count; 98 int32_t writer_count; 99 // Note, that reader_count and writer_count are not used the same way. 100 // writer_count includes the write lock owner as well as waiting 101 // writers. reader_count includes read lock owners only. 102 WaiterList waiters; 103 104 status_t Init() 105 { 106 flags = 0; 107 owner = -1; 108 mutex = 0; 109 reader_count = 0; 110 writer_count = 0; 111 new(&waiters) WaiterList; 112 113 return B_OK; 114 } 115 116 status_t Destroy() 117 { 118 Locker locker(this); 119 if (reader_count > 0 || waiters.Head() != NULL || writer_count > 0) 120 return EBUSY; 121 return B_OK; 122 } 123 124 bool StructureLock() 125 { 126 // Enter critical region: lock the mutex 127 int32 status = atomic_or((int32*)&mutex, B_USER_MUTEX_LOCKED); 128 129 // If already locked, call the kernel 130 if ((status & (B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING)) != 0) { 131 do { 132 status = _kern_mutex_lock((int32*)&mutex, NULL, 0, 0); 133 } while (status == B_INTERRUPTED); 134 135 if (status != B_OK) 136 return false; 137 } 138 return true; 139 } 140 141 void StructureUnlock() 142 { 143 // Exit critical region: unlock the mutex 144 int32 status = atomic_and((int32*)&mutex, 145 ~(int32)B_USER_MUTEX_LOCKED); 146 147 if ((status & B_USER_MUTEX_WAITING) != 0) 148 _kern_mutex_unlock((int32*)&mutex, 0); 149 } 150 151 status_t ReadLock(bigtime_t timeout) 152 { 153 Locker locker(this); 154 155 if (writer_count == 0) { 156 reader_count++; 157 return B_OK; 158 } 159 160 return _Wait(false, timeout); 161 } 162 163 status_t WriteLock(bigtime_t timeout) 164 { 165 Locker locker(this); 166 167 if (reader_count == 0 && writer_count == 0) { 168 writer_count++; 169 owner = find_thread(NULL); 170 return B_OK; 171 } 172 173 return _Wait(true, timeout); 174 } 175 176 status_t Unlock() 177 { 178 Locker locker(this); 179 180 if (find_thread(NULL) == owner) { 181 writer_count--; 182 owner = -1; 183 } else 184 reader_count--; 185 186 _Unblock(); 187 188 return B_OK; 189 } 190 191 private: 192 status_t _Wait(bool writer, bigtime_t timeout) 193 { 194 if (timeout == 0) 195 return B_TIMED_OUT; 196 197 Waiter waiter(writer); 198 waiters.Add(&waiter); 199 waiter.queued = true; 200 waiter.userThread->wait_status = 1; 201 202 if (writer) 203 writer_count++; 204 205 StructureUnlock(); 206 status_t error = _kern_block_thread( 207 timeout >= 0 ? B_ABSOLUTE_REAL_TIME_TIMEOUT : 0, timeout); 208 StructureLock(); 209 210 if (!waiter.queued) 211 return waiter.status; 212 213 // we're still queued, which means an error (timeout, interrupt) 214 // occurred 215 waiters.Remove(&waiter); 216 217 if (writer) 218 writer_count--; 219 220 _Unblock(); 221 222 return error; 223 } 224 225 void _Unblock() 226 { 227 // Check whether there any waiting threads at all and whether anyone 228 // has the write lock 229 Waiter* waiter = waiters.Head(); 230 if (waiter == NULL || owner >= 0) 231 return; 232 233 // writer at head of queue? 234 if (waiter->writer) { 235 if (reader_count == 0) { 236 waiter->status = B_OK; 237 waiter->queued = false; 238 waiters.Remove(waiter); 239 owner = waiter->thread; 240 241 if (waiter->userThread->wait_status > 0) 242 _kern_unblock_thread(waiter->thread, B_OK); 243 } 244 return; 245 } 246 247 // wake up one or more readers -- we unblock more than one reader at 248 // a time to save trips to the kernel 249 while (!waiters.IsEmpty() && !waiters.Head()->writer) { 250 static const int kMaxReaderUnblockCount = 128; 251 thread_id readers[kMaxReaderUnblockCount]; 252 int readerCount = 0; 253 254 while (readerCount < kMaxReaderUnblockCount 255 && (waiter = waiters.Head()) != NULL 256 && !waiter->writer) { 257 waiter->status = B_OK; 258 waiter->queued = false; 259 waiters.Remove(waiter); 260 261 if (waiter->userThread->wait_status > 0) { 262 readers[readerCount++] = waiter->thread; 263 reader_count++; 264 } 265 } 266 267 if (readerCount > 0) 268 _kern_unblock_threads(readers, readerCount, B_OK); 269 } 270 } 271 272 273 struct Locking { 274 inline bool Lock(LocalRWLock* lockable) 275 { 276 return lockable->StructureLock(); 277 } 278 279 inline void Unlock(LocalRWLock* lockable) 280 { 281 lockable->StructureUnlock(); 282 } 283 }; 284 typedef AutoLocker<LocalRWLock, Locking> Locker; 285 }; 286 287 288 static void inline 289 assert_dummy() 290 { 291 STATIC_ASSERT(sizeof(pthread_rwlock_t) >= sizeof(SharedRWLock)); 292 STATIC_ASSERT(sizeof(pthread_rwlock_t) >= sizeof(LocalRWLock)); 293 } 294 295 296 // #pragma mark - public lock functions 297 298 299 int 300 pthread_rwlock_init(pthread_rwlock_t* lock, const pthread_rwlockattr_t* _attr) 301 { 302 pthread_rwlockattr* attr = _attr != NULL ? *_attr : NULL; 303 bool shared = attr != NULL && (attr->flags & RWLOCK_FLAG_SHARED) != 0; 304 305 if (shared) 306 return ((SharedRWLock*)lock)->Init(); 307 else 308 return ((LocalRWLock*)lock)->Init(); 309 } 310 311 312 int 313 pthread_rwlock_destroy(pthread_rwlock_t* lock) 314 { 315 if ((lock->flags & RWLOCK_FLAG_SHARED) != 0) 316 return ((SharedRWLock*)lock)->Destroy(); 317 else 318 return ((LocalRWLock*)lock)->Destroy(); 319 } 320 321 322 int 323 pthread_rwlock_rdlock(pthread_rwlock_t* lock) 324 { 325 if ((lock->flags & RWLOCK_FLAG_SHARED) != 0) 326 return ((SharedRWLock*)lock)->ReadLock(B_INFINITE_TIMEOUT); 327 else 328 return ((LocalRWLock*)lock)->ReadLock(B_INFINITE_TIMEOUT); 329 } 330 331 332 int 333 pthread_rwlock_tryrdlock(pthread_rwlock_t* lock) 334 { 335 status_t error; 336 if ((lock->flags & RWLOCK_FLAG_SHARED) != 0) 337 error = ((SharedRWLock*)lock)->ReadLock(0); 338 else 339 error = ((LocalRWLock*)lock)->ReadLock(0); 340 341 return error == B_TIMED_OUT ? EBUSY : error; 342 } 343 344 345 int pthread_rwlock_timedrdlock(pthread_rwlock_t* lock, 346 const struct timespec *timeout) 347 { 348 bigtime_t timeoutMicros = timeout->tv_sec * 1000000LL 349 + timeout->tv_nsec / 1000LL; 350 351 status_t error; 352 if ((lock->flags & RWLOCK_FLAG_SHARED) != 0) 353 error = ((SharedRWLock*)lock)->ReadLock(timeoutMicros); 354 else 355 error = ((LocalRWLock*)lock)->ReadLock(timeoutMicros); 356 357 return error == B_TIMED_OUT ? EBUSY : error; 358 } 359 360 361 int 362 pthread_rwlock_wrlock(pthread_rwlock_t* lock) 363 { 364 if ((lock->flags & RWLOCK_FLAG_SHARED) != 0) 365 return ((SharedRWLock*)lock)->WriteLock(B_INFINITE_TIMEOUT); 366 else 367 return ((LocalRWLock*)lock)->WriteLock(B_INFINITE_TIMEOUT); 368 } 369 370 371 int 372 pthread_rwlock_trywrlock(pthread_rwlock_t* lock) 373 { 374 status_t error; 375 if ((lock->flags & RWLOCK_FLAG_SHARED) != 0) 376 error = ((SharedRWLock*)lock)->WriteLock(0); 377 else 378 error = ((LocalRWLock*)lock)->WriteLock(0); 379 380 return error == B_TIMED_OUT ? EBUSY : error; 381 } 382 383 384 int 385 pthread_rwlock_timedwrlock(pthread_rwlock_t* lock, 386 const struct timespec *timeout) 387 { 388 bigtime_t timeoutMicros = timeout->tv_sec * 1000000LL 389 + timeout->tv_nsec / 1000LL; 390 391 status_t error; 392 if ((lock->flags & RWLOCK_FLAG_SHARED) != 0) 393 error = ((SharedRWLock*)lock)->WriteLock(timeoutMicros); 394 else 395 error = ((LocalRWLock*)lock)->WriteLock(timeoutMicros); 396 397 return error == B_TIMED_OUT ? EBUSY : error; 398 } 399 400 401 int 402 pthread_rwlock_unlock(pthread_rwlock_t* lock) 403 { 404 if ((lock->flags & RWLOCK_FLAG_SHARED) != 0) 405 return ((SharedRWLock*)lock)->Unlock(); 406 else 407 return ((LocalRWLock*)lock)->Unlock(); 408 } 409 410 411 // #pragma mark - public attribute functions 412 413 414 int 415 pthread_rwlockattr_init(pthread_rwlockattr_t* _attr) 416 { 417 pthread_rwlockattr* attr = (pthread_rwlockattr*)malloc( 418 sizeof(pthread_rwlockattr)); 419 if (attr == NULL) 420 return B_NO_MEMORY; 421 422 attr->flags = 0; 423 *_attr = attr; 424 425 return 0; 426 } 427 428 429 int 430 pthread_rwlockattr_destroy(pthread_rwlockattr_t* _attr) 431 { 432 pthread_rwlockattr* attr = *_attr; 433 434 free(attr); 435 return 0; 436 } 437 438 439 int 440 pthread_rwlockattr_getpshared(const pthread_rwlockattr_t* _attr, int* shared) 441 { 442 pthread_rwlockattr* attr = *_attr; 443 444 *shared = (attr->flags & RWLOCK_FLAG_SHARED) != 0 445 ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE; 446 return 0; 447 } 448 449 450 int 451 pthread_rwlockattr_setpshared(pthread_rwlockattr_t* _attr, int shared) 452 { 453 pthread_rwlockattr* attr = *_attr; 454 455 if (shared == PTHREAD_PROCESS_SHARED) 456 attr->flags |= RWLOCK_FLAG_SHARED; 457 else 458 attr->flags &= ~RWLOCK_FLAG_SHARED; 459 460 return 0; 461 } 462 463