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