1#ifndef LOCK_H 2#define LOCK_H 3/* Lock - simple semaphores, read/write lock implementation 4** 5** Initial version by Axel Dörfler, axeld@pinc-software.de 6** Roughly based on a Be sample code written by Nathan Schrenk. 7** 8** This file may be used under the terms of the OpenBeOS License. 9*/ 10 11 12#include <KernelExport.h> 13#include <stdio.h> 14#include "Utility.h" 15#include "Debug.h" 16 17 18// Configure here if and when real benaphores should be used 19#define USE_BENAPHORE 20 // if defined, benaphores are used for the Semaphore/RecursiveLock classes 21#ifdef USER 22//# define FAST_LOCK 23 // the ReadWriteLock class uses a second Semaphore to 24 // speed up locking - only makes sense if USE_BENAPHORE 25 // is defined, too. 26#endif 27 28 29class Semaphore { 30 public: 31 Semaphore(const char *name) 32 : 33#ifdef USE_BENAPHORE 34 fSemaphore(create_sem(0, name)), 35 fCount(1) 36#else 37 fSemaphore(create_sem(1, name)) 38#endif 39 { 40#ifndef USER 41 set_sem_owner(fSemaphore, B_SYSTEM_TEAM); 42#endif 43 } 44 45 ~Semaphore() 46 { 47 delete_sem(fSemaphore); 48 } 49 50 status_t InitCheck() 51 { 52 if (fSemaphore < B_OK) 53 return fSemaphore; 54 55 return B_OK; 56 } 57 58 status_t Lock() 59 { 60#ifdef USE_BENAPHORE 61 if (atomic_add(&fCount, -1) <= 0) 62#endif 63 return acquire_sem(fSemaphore); 64#ifdef USE_BENAPHORE 65 return B_OK; 66#endif 67 } 68 69 status_t Unlock() 70 { 71#ifdef USE_BENAPHORE 72 if (atomic_add(&fCount, 1) < 0) 73#endif 74 return release_sem(fSemaphore); 75#ifdef USE_BENAPHORE 76 return B_OK; 77#endif 78 } 79 80 private: 81 sem_id fSemaphore; 82#ifdef USE_BENAPHORE 83 vint32 fCount; 84#endif 85}; 86 87// a convenience class to lock a Semaphore object 88 89class Locker { 90 public: 91 Locker(Semaphore &lock) 92 : fLock(lock) 93 { 94 fStatus = lock.Lock(); 95 ASSERT(fStatus == B_OK); 96 } 97 98 ~Locker() 99 { 100 if (fStatus == B_OK) 101 fLock.Unlock(); 102 } 103 104 status_t Status() const 105 { 106 return fStatus; 107 } 108 109 private: 110 Semaphore &fLock; 111 status_t fStatus; 112}; 113 114 115//**** Recursive Lock 116 117class RecursiveLock { 118 public: 119 RecursiveLock(const char *name) 120 : 121#ifdef USE_BENAPHORE 122 fSemaphore(create_sem(0, name)), 123 fCount(1), 124#else 125 fSemaphore(create_sem(1, name)), 126#endif 127 fOwner(-1) 128 { 129#ifndef USER 130 set_sem_owner(fSemaphore, B_SYSTEM_TEAM); 131#endif 132 } 133 134 status_t LockWithTimeout(bigtime_t timeout) 135 { 136 thread_id thread = find_thread(NULL); 137 if (thread == fOwner) { 138 fOwnerCount++; 139 return B_OK; 140 } 141 142 status_t status; 143#ifdef USE_BENAPHORE 144 if (atomic_add(&fCount, -1) > 0) 145 status = B_OK; 146 else 147#endif 148 status = acquire_sem_etc(fSemaphore, 1, B_RELATIVE_TIMEOUT, timeout); 149 150 if (status == B_OK) { 151 fOwner = thread; 152 fOwnerCount = 1; 153 } 154 155 return status; 156 } 157 158 status_t Lock() 159 { 160 return LockWithTimeout(B_INFINITE_TIMEOUT); 161 } 162 163 status_t Unlock() 164 { 165 thread_id thread = find_thread(NULL); 166 if (thread != fOwner) { 167 #if __MWERKS__ && !USER //--- The R5 PowerPC kernel doesn't have panic() 168 char blip[255]; 169 sprintf(blip,"RecursiveLock unlocked by %ld, owned by %ld\n", thread, fOwner); 170 kernel_debugger(blip); 171 #else 172 panic("RecursiveLock unlocked by %ld, owned by %ld\n", thread, fOwner); 173 #endif 174 } 175 176 if (--fOwnerCount == 0) { 177 fOwner = -1; 178#ifdef USE_BENAPHORE 179 if (atomic_add(&fCount, 1) < 0) 180#endif 181 return release_sem(fSemaphore); 182 } 183 184 return B_OK; 185 } 186 187 private: 188 sem_id fSemaphore; 189#ifdef USE_BENAPHORE 190 vint32 fCount; 191#endif 192 thread_id fOwner; 193 int32 fOwnerCount; 194}; 195 196// a convenience class to lock an RecursiveLock object 197 198class RecursiveLocker { 199 public: 200 RecursiveLocker(RecursiveLock &lock) 201 : fLock(lock) 202 { 203 fStatus = lock.Lock(); 204 ASSERT(fStatus == B_OK); 205 } 206 207 ~RecursiveLocker() 208 { 209 if (fStatus == B_OK) 210 fLock.Unlock(); 211 } 212 213 status_t Status() const 214 { 215 return fStatus; 216 } 217 218 private: 219 RecursiveLock &fLock; 220 status_t fStatus; 221}; 222 223 224//**** Many Reader/Single Writer Lock 225 226// This is a "fast" implementation of a single writer/many reader 227// locking scheme. It's fast because it uses the benaphore idea 228// to do lazy semaphore locking - in most cases it will only have 229// to do some simple integer arithmetic. 230// The second semaphore (fWriteLock) is needed to prevent the situation 231// that a second writer can acquire the lock when there are still readers 232// holding it. 233 234#define MAX_READERS 100000 235 236// Note: this code will break if you actually have 100000 readers 237// at once. With the current thread/... limits in BeOS you can't 238// touch that value, but it might be possible in the future. 239// Also, you can only have about 20000 concurrent writers until 240// the semaphore count exceeds the int32 bounds 241 242// Timeouts: 243// It may be a good idea to have timeouts for the WriteLocked class, 244// in case something went wrong - we'll see if this is necessary, 245// but it would be a somewhat poor work-around for a deadlock... 246// But the only real problem with timeouts could be for things like 247// "chkbfs" - because such a tool may need to lock for some more time 248 249 250// define if you want to have fast locks as the foundation for the 251// ReadWriteLock class - the benefit is that acquire_sem() doesn't 252// have to be called when there is no one waiting. 253// The disadvantage is the use of 2 real semaphores which is quite 254// expensive regarding that BeOS only allows for a total of 64k 255// semaphores (since every open BFS inode has such a lock). 256 257#ifdef FAST_LOCK 258class ReadWriteLock { 259 public: 260 ReadWriteLock(const char *name) 261 : 262 fWriteLock(name) 263 { 264 Initialize(name); 265 } 266 267 ReadWriteLock() 268 : 269 fWriteLock("bfs r/w w-lock") 270 { 271 } 272 273 ~ReadWriteLock() 274 { 275 delete_sem(fSemaphore); 276 } 277 278 status_t Initialize(const char *name = "bfs r/w lock") 279 { 280 fSemaphore = create_sem(0, name); 281 fCount = MAX_READERS; 282#ifndef USER 283 set_sem_owner(fSemaphore, B_SYSTEM_TEAM); 284#endif 285 return fSemaphore; 286 } 287 288 status_t InitCheck() 289 { 290 if (fSemaphore < B_OK) 291 return fSemaphore; 292 293 return B_OK; 294 } 295 296 status_t Lock() 297 { 298 if (atomic_add(&fCount, -1) <= 0) 299 return acquire_sem(fSemaphore); 300 301 return B_OK; 302 } 303 304 void Unlock() 305 { 306 if (atomic_add(&fCount, 1) < 0) 307 release_sem(fSemaphore); 308 } 309 310 status_t LockWrite() 311 { 312 if (fWriteLock.Lock() < B_OK) 313 return B_ERROR; 314 315 int32 readers = atomic_add(&fCount, -MAX_READERS); 316 status_t status = B_OK; 317 318 if (readers < MAX_READERS) { 319 // Acquire sem for all readers currently not using a semaphore. 320 // But if we are not the only write lock in the queue, just get 321 // the one for us 322 status = acquire_sem_etc(fSemaphore, readers <= 0 ? 1 : MAX_READERS - readers, 0, 0); 323 } 324 fWriteLock.Unlock(); 325 326 return status; 327 } 328 329 void UnlockWrite() 330 { 331 int32 readers = atomic_add(&fCount, MAX_READERS); 332 if (readers < 0) { 333 // release sem for all readers only when we were the only writer 334 release_sem_etc(fSemaphore, readers <= -MAX_READERS ? 1 : -readers, 0); 335 } 336 } 337 338 private: 339 friend class ReadLocked; 340 friend class WriteLocked; 341 342 sem_id fSemaphore; 343 vint32 fCount; 344 Semaphore fWriteLock; 345}; 346#else // FAST_LOCK 347class ReadWriteLock { 348 public: 349 ReadWriteLock(const char *name) 350 { 351 Initialize(name); 352 } 353 354 ReadWriteLock() 355 { 356 } 357 358 ~ReadWriteLock() 359 { 360 delete_sem(fSemaphore); 361 } 362 363 status_t Initialize(const char *name = "bfs r/w lock") 364 { 365 fSemaphore = create_sem(MAX_READERS, name); 366#ifndef USER 367 set_sem_owner(fSemaphore, B_SYSTEM_TEAM); 368#endif 369 return fSemaphore; 370 } 371 372 status_t InitCheck() 373 { 374 if (fSemaphore < B_OK) 375 return fSemaphore; 376 377 return B_OK; 378 } 379 380 status_t Lock() 381 { 382 return acquire_sem(fSemaphore); 383 } 384 385 void Unlock() 386 { 387 release_sem(fSemaphore); 388 } 389 390 status_t LockWrite() 391 { 392 return acquire_sem_etc(fSemaphore, MAX_READERS, 0, 0); 393 } 394 395 void UnlockWrite() 396 { 397 release_sem_etc(fSemaphore, MAX_READERS, 0); 398 } 399 400 private: 401 friend class ReadLocked; 402 friend class WriteLocked; 403 404 sem_id fSemaphore; 405}; 406#endif // FAST_LOCK 407 408 409class ReadLocked { 410 public: 411 ReadLocked(ReadWriteLock &lock) 412 : 413 fLock(lock) 414 { 415 fStatus = lock.Lock(); 416 } 417 418 ~ReadLocked() 419 { 420 if (fStatus == B_OK) 421 fLock.Unlock(); 422 } 423 424 private: 425 ReadWriteLock &fLock; 426 status_t fStatus; 427}; 428 429 430class WriteLocked { 431 public: 432 WriteLocked(ReadWriteLock &lock) 433 : 434 fLock(lock) 435 { 436 fStatus = lock.LockWrite(); 437 } 438 439 ~WriteLocked() 440 { 441 if (fStatus == B_OK) 442 fLock.UnlockWrite(); 443 } 444 445 status_t IsLocked() 446 { 447 return fStatus; 448 } 449 450 private: 451 ReadWriteLock &fLock; 452 status_t fStatus; 453}; 454 455 456// A simple locking structure that doesn't use a semaphore - it's useful 457// if you have to protect critical parts with a short runtime. 458// It also allows to nest several locks for the same thread. 459 460class SimpleLock { 461 public: 462 SimpleLock() 463 : 464 fHolder(-1), 465 fCount(0) 466 { 467 } 468 469 status_t Lock(bigtime_t time = 500) 470 { 471 int32 thisThread = find_thread(NULL); 472 int32 current; 473 while (1) { 474 /*if (fHolder == -1) { 475 current = fHolder; 476 fHolder = thisThread; 477 }*/ 478 current = _atomic_test_and_set(&fHolder, thisThread, -1); 479 if (current == -1) 480 break; 481 if (current == thisThread) 482 break; 483 484 snooze(time); 485 } 486 487 // ToDo: the lock cannot fail currently! We may want 488 // to change this 489 atomic_add(&fCount, 1); 490 return B_OK; 491 } 492 493 void Unlock() 494 { 495 if (atomic_add(&fCount, -1) == 1) 496 _atomic_set(&fHolder, -1); 497 } 498 499 bool IsLocked() const 500 { 501 return fHolder == find_thread(NULL); 502 } 503 504 private: 505 vint32 fHolder; 506 vint32 fCount; 507}; 508 509// A convenience class to lock the SimpleLock, note the 510// different timing compared to the direct call 511 512class SimpleLocker { 513 public: 514 SimpleLocker(SimpleLock &lock,bigtime_t time = 1000) 515 : fLock(lock) 516 { 517 lock.Lock(time); 518 } 519 520 ~SimpleLocker() 521 { 522 fLock.Unlock(); 523 } 524 525 private: 526 SimpleLock &fLock; 527}; 528 529#endif /* LOCK_H */ 530