|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * The contents of this file constitute Original Code as defined in and ! 7: * are subject to the Apple Public Source License Version 1.1 (the ! 8: * "License"). You may not use this file except in compliance with the ! 9: * License. Please obtain a copy of the License at ! 10: * http://www.apple.com/publicsource and read it before using this file. ! 11: * ! 12: * This Original Code and all software distributed under the License are ! 13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 17: * License for the specific language governing rights and limitations ! 18: * under the License. ! 19: * ! 20: * @APPLE_LICENSE_HEADER_END@ ! 21: */ ! 22: /* ! 23: * @OSF_COPYRIGHT@ ! 24: * ! 25: */ ! 26: /* ! 27: * File: kern/sync_lock.c ! 28: * Author: Joseph CaraDonna ! 29: * ! 30: * Contains RT distributed lock synchronization services. ! 31: */ ! 32: ! 33: #include <kern/etap_macros.h> ! 34: #include <kern/misc_protos.h> ! 35: #include <kern/sync_lock.h> ! 36: #include <kern/sched_prim.h> ! 37: #include <kern/ipc_kobject.h> ! 38: #include <kern/ipc_sync.h> ! 39: #include <kern/etap_macros.h> ! 40: #include <kern/thread.h> ! 41: #include <kern/task.h> ! 42: #include <ipc/ipc_port.h> ! 43: #include <ipc/ipc_space.h> ! 44: ! 45: /* ! 46: * Ulock ownership MACROS ! 47: * ! 48: * Assumes: ulock internal lock is held ! 49: */ ! 50: ! 51: #define ulock_ownership_set(ul, th) \ ! 52: MACRO_BEGIN \ ! 53: thread_act_t _th_act; \ ! 54: _th_act = (th)->top_act; \ ! 55: act_lock(_th_act); \ ! 56: enqueue (&_th_act->held_ulocks, (queue_entry_t) (ul)); \ ! 57: act_unlock(_th_act); \ ! 58: (ul)->holder = _th_act; \ ! 59: MACRO_END ! 60: ! 61: #define ulock_ownership_clear(ul) \ ! 62: MACRO_BEGIN \ ! 63: thread_act_t _th_act; \ ! 64: _th_act = (ul)->holder; \ ! 65: if (_th_act->active) { \ ! 66: act_lock(_th_act); \ ! 67: remqueue(&_th_act->held_ulocks, \ ! 68: (queue_entry_t) (ul)); \ ! 69: act_unlock(_th_act); \ ! 70: } else { \ ! 71: remqueue(&_th_act->held_ulocks, \ ! 72: (queue_entry_t) (ul)); \ ! 73: } \ ! 74: (ul)->holder = THR_ACT_NULL; \ ! 75: MACRO_END ! 76: ! 77: /* ! 78: * Lock set ownership MACROS ! 79: */ ! 80: ! 81: #define lock_set_ownership_set(ls, t) \ ! 82: MACRO_BEGIN \ ! 83: task_lock((t)); \ ! 84: enqueue_head(&(t)->lock_set_list, (queue_entry_t) (ls));\ ! 85: (t)->lock_sets_owned++; \ ! 86: task_unlock((t)); \ ! 87: (ls)->owner = (t); \ ! 88: MACRO_END ! 89: ! 90: #define lock_set_ownership_clear(ls, t) \ ! 91: MACRO_BEGIN \ ! 92: task_lock((t)); \ ! 93: remqueue(&(t)->lock_set_list, (queue_entry_t) (ls)); \ ! 94: (t)->lock_sets_owned--; \ ! 95: task_unlock((t)); \ ! 96: MACRO_END ! 97: ! 98: unsigned int lock_set_event; ! 99: #define LOCK_SET_EVENT ((event_t)&lock_set_event) ! 100: ! 101: unsigned int lock_set_handoff; ! 102: #define LOCK_SET_HANDOFF ((event_t)&lock_set_handoff) ! 103: ! 104: /* ! 105: * ROUTINE: lock_set_create [exported] ! 106: * ! 107: * Creates a lock set. ! 108: * The port representing the lock set is returned as a parameter. ! 109: */ ! 110: kern_return_t ! 111: lock_set_create ( ! 112: task_t task, ! 113: lock_set_t *new_lock_set, ! 114: int n_ulocks, ! 115: int policy) ! 116: { ! 117: lock_set_t lock_set = LOCK_SET_NULL; ! 118: ulock_t ulock; ! 119: int size; ! 120: int x; ! 121: ! 122: *new_lock_set = LOCK_SET_NULL; ! 123: ! 124: if (task == TASK_NULL || n_ulocks <= 0 || policy > SYNC_POLICY_MAX) ! 125: return KERN_INVALID_ARGUMENT; ! 126: ! 127: size = sizeof(struct lock_set) + (sizeof(struct ulock) * (n_ulocks-1)); ! 128: lock_set = (lock_set_t) kalloc (size); ! 129: ! 130: if (lock_set == LOCK_SET_NULL) ! 131: return KERN_RESOURCE_SHORTAGE; ! 132: ! 133: ! 134: lock_set_lock_init(lock_set); ! 135: lock_set->n_ulocks = n_ulocks; ! 136: lock_set->ref_count = 1; ! 137: ! 138: /* ! 139: * Create and initialize the lock set port ! 140: */ ! 141: lock_set->port = ipc_port_alloc_kernel(); ! 142: if (lock_set->port == IP_NULL) { ! 143: /* This will deallocate the lock set */ ! 144: lock_set_dereference(lock_set); ! 145: return KERN_RESOURCE_SHORTAGE; ! 146: } ! 147: ! 148: ipc_kobject_set (lock_set->port, ! 149: (ipc_kobject_t) lock_set, ! 150: IKOT_LOCK_SET); ! 151: ! 152: /* ! 153: * Initialize each ulock in the lock set ! 154: */ ! 155: ! 156: for (x=0; x < n_ulocks; x++) { ! 157: ulock = (ulock_t) &lock_set->ulock_list[x]; ! 158: ulock_lock_init(ulock); ! 159: ulock->lock_set = lock_set; ! 160: ulock->holder = THR_ACT_NULL; ! 161: ulock->blocked = FALSE; ! 162: ulock->unstable = FALSE; ! 163: ulock->ho_wait = FALSE; ! 164: wait_queue_init(&ulock->wait_queue, policy); ! 165: } ! 166: ! 167: lock_set_ownership_set(lock_set, task); ! 168: ! 169: lock_set->active = TRUE; ! 170: *new_lock_set = lock_set; ! 171: ! 172: return KERN_SUCCESS; ! 173: } ! 174: ! 175: /* ! 176: * ROUTINE: lock_set_destroy [exported] ! 177: * ! 178: * Destroys a lock set. This call will only succeed if the ! 179: * specified task is the SAME task name specified at the lock set's ! 180: * creation. ! 181: * ! 182: * NOTES: ! 183: * - All threads currently blocked on the lock set's ulocks are awoken. ! 184: * - These threads will return with the KERN_LOCK_SET_DESTROYED error. ! 185: */ ! 186: kern_return_t ! 187: lock_set_destroy (task_t task, lock_set_t lock_set) ! 188: { ! 189: thread_t thread; ! 190: ulock_t ulock; ! 191: int i; ! 192: ! 193: if (task == TASK_NULL || lock_set == LOCK_SET_NULL) ! 194: return KERN_INVALID_ARGUMENT; ! 195: ! 196: if (lock_set->owner != task) ! 197: return KERN_INVALID_RIGHT; ! 198: ! 199: lock_set_lock(lock_set); ! 200: if (!lock_set->active) { ! 201: lock_set_unlock(lock_set); ! 202: return KERN_LOCK_SET_DESTROYED; ! 203: } ! 204: ! 205: /* ! 206: * Deactivate lock set ! 207: */ ! 208: lock_set->active = FALSE; ! 209: ! 210: /* ! 211: * If a ulock is currently held in the target lock set: ! 212: * ! 213: * 1) Wakeup all threads blocked on the ulock (if any). Threads ! 214: * may be blocked waiting normally, or waiting for a handoff. ! 215: * Blocked threads will return with KERN_LOCK_SET_DESTROYED. ! 216: * ! 217: * 2) ulock ownership is cleared. ! 218: * The thread currently holding the ulock is revoked of its ! 219: * ownership. ! 220: */ ! 221: for (i = 0; i < lock_set->n_ulocks; i++) { ! 222: ulock = &lock_set->ulock_list[i]; ! 223: ! 224: ulock_lock(ulock); ! 225: ! 226: if (ulock->accept_wait) { ! 227: ulock->accept_wait = FALSE; ! 228: wait_queue_wakeup_one(&ulock->wait_queue, ! 229: LOCK_SET_HANDOFF, ! 230: THREAD_RESTART); ! 231: } ! 232: ! 233: if (ulock->holder) { ! 234: if (ulock->blocked) { ! 235: ulock->blocked = FALSE; ! 236: wait_queue_wakeup_all(&ulock->wait_queue, ! 237: LOCK_SET_EVENT, ! 238: THREAD_RESTART); ! 239: } ! 240: if (ulock->ho_wait) { ! 241: ulock->ho_wait = FALSE; ! 242: wait_queue_wakeup_one(&ulock->wait_queue, ! 243: LOCK_SET_HANDOFF, ! 244: THREAD_RESTART); ! 245: } ! 246: ulock_ownership_clear(ulock); ! 247: } ! 248: ! 249: ulock_unlock(ulock); ! 250: } ! 251: ! 252: lock_set_unlock(lock_set); ! 253: lock_set_ownership_clear(lock_set, task); ! 254: ! 255: /* ! 256: * Deallocate ! 257: * ! 258: * Drop the lock set reference, which inturn destroys the ! 259: * lock set structure if the reference count goes to zero. ! 260: */ ! 261: ! 262: ipc_port_dealloc_kernel(lock_set->port); ! 263: lock_set_dereference(lock_set); ! 264: ! 265: return KERN_SUCCESS; ! 266: } ! 267: ! 268: kern_return_t ! 269: lock_acquire (lock_set_t lock_set, int lock_id) ! 270: { ! 271: ulock_t ulock; ! 272: ! 273: if (lock_set == LOCK_SET_NULL) ! 274: return KERN_INVALID_ARGUMENT; ! 275: ! 276: if (lock_id < 0 || lock_id >= lock_set->n_ulocks) ! 277: return KERN_INVALID_ARGUMENT; ! 278: ! 279: retry: ! 280: lock_set_lock(lock_set); ! 281: if (!lock_set->active) { ! 282: lock_set_unlock(lock_set); ! 283: return KERN_LOCK_SET_DESTROYED; ! 284: } ! 285: ! 286: ulock = (ulock_t) &lock_set->ulock_list[lock_id]; ! 287: ulock_lock(ulock); ! 288: lock_set_unlock(lock_set); ! 289: ! 290: /* ! 291: * Block the current thread if the lock is already held. ! 292: */ ! 293: ! 294: if (ulock->holder != THR_ACT_NULL) { ! 295: int wait_result; ! 296: ! 297: lock_set_unlock(lock_set); ! 298: ! 299: if (ulock->holder == current_act()) { ! 300: ulock_unlock(ulock); ! 301: return KERN_LOCK_OWNED_SELF; ! 302: } ! 303: ! 304: ulock->blocked = TRUE; ! 305: wait_queue_assert_wait(&ulock->wait_queue, ! 306: LOCK_SET_EVENT, ! 307: THREAD_ABORTSAFE); ! 308: ulock_unlock(ulock); ! 309: ! 310: /* ! 311: * Block - Wait for lock to become available. ! 312: */ ! 313: ! 314: wait_result = thread_block((void (*)(void))0); ! 315: ! 316: /* ! 317: * Check the result status: ! 318: * ! 319: * Check to see why thread was woken up. In all cases, we ! 320: * already have been removed from the queue. ! 321: */ ! 322: switch (wait_result) { ! 323: case THREAD_AWAKENED: ! 324: /* lock transitioned from old locker to us */ ! 325: /* he already made us owner */ ! 326: return (ulock->unstable) ? KERN_LOCK_UNSTABLE : ! 327: KERN_SUCCESS; ! 328: ! 329: case THREAD_INTERRUPTED: ! 330: return KERN_ABORTED; ! 331: ! 332: case THREAD_RESTART: ! 333: goto retry; /* probably a dead lock_set */ ! 334: ! 335: default: ! 336: panic("lock_acquire\n"); ! 337: } ! 338: } ! 339: ! 340: /* ! 341: * Assign lock ownership ! 342: */ ! 343: ulock_ownership_set(ulock, current_thread()); ! 344: ulock_unlock(ulock); ! 345: ! 346: return (ulock->unstable) ? KERN_LOCK_UNSTABLE : KERN_SUCCESS; ! 347: } ! 348: ! 349: kern_return_t ! 350: lock_release (lock_set_t lock_set, int lock_id) ! 351: { ! 352: ulock_t ulock; ! 353: ! 354: if (lock_set == LOCK_SET_NULL) ! 355: return KERN_INVALID_ARGUMENT; ! 356: ! 357: if (lock_id < 0 || lock_id >= lock_set->n_ulocks) ! 358: return KERN_INVALID_ARGUMENT; ! 359: ! 360: ulock = (ulock_t) &lock_set->ulock_list[lock_id]; ! 361: ! 362: return (lock_release_internal(ulock, current_act())); ! 363: } ! 364: ! 365: kern_return_t ! 366: lock_try (lock_set_t lock_set, int lock_id) ! 367: { ! 368: ulock_t ulock; ! 369: ! 370: ! 371: if (lock_set == LOCK_SET_NULL) ! 372: return KERN_INVALID_ARGUMENT; ! 373: ! 374: if (lock_id < 0 || lock_id >= lock_set->n_ulocks) ! 375: return KERN_INVALID_ARGUMENT; ! 376: ! 377: ! 378: lock_set_lock(lock_set); ! 379: if (!lock_set->active) { ! 380: lock_set_unlock(lock_set); ! 381: return KERN_LOCK_SET_DESTROYED; ! 382: } ! 383: ! 384: ulock = (ulock_t) &lock_set->ulock_list[lock_id]; ! 385: ulock_lock(ulock); ! 386: lock_set_unlock(lock_set); ! 387: ! 388: /* ! 389: * If the lock is already owned, we return without blocking. ! 390: * ! 391: * An ownership status is returned to inform the caller as to ! 392: * whether it already holds the lock or another thread does. ! 393: */ ! 394: ! 395: if (ulock->holder != THR_ACT_NULL) { ! 396: lock_set_unlock(lock_set); ! 397: ! 398: if (ulock->holder == current_act()) { ! 399: ulock_unlock(ulock); ! 400: return KERN_LOCK_OWNED_SELF; ! 401: } ! 402: ! 403: ulock_unlock(ulock); ! 404: return KERN_LOCK_OWNED; ! 405: } ! 406: ! 407: /* ! 408: * Add the ulock to the lock set's held_ulocks list. ! 409: */ ! 410: ! 411: ulock_ownership_set(ulock, current_thread()); ! 412: ulock_unlock(ulock); ! 413: ! 414: return (ulock->unstable) ? KERN_LOCK_UNSTABLE : KERN_SUCCESS; ! 415: } ! 416: ! 417: kern_return_t ! 418: lock_make_stable (lock_set_t lock_set, int lock_id) ! 419: { ! 420: ulock_t ulock; ! 421: ! 422: ! 423: if (lock_set == LOCK_SET_NULL) ! 424: return KERN_INVALID_ARGUMENT; ! 425: ! 426: if (lock_id < 0 || lock_id >= lock_set->n_ulocks) ! 427: return KERN_INVALID_ARGUMENT; ! 428: ! 429: ! 430: lock_set_lock(lock_set); ! 431: if (!lock_set->active) { ! 432: lock_set_unlock(lock_set); ! 433: return KERN_LOCK_SET_DESTROYED; ! 434: } ! 435: ! 436: ulock = (ulock_t) &lock_set->ulock_list[lock_id]; ! 437: ulock_lock(ulock); ! 438: lock_set_unlock(lock_set); ! 439: ! 440: if (ulock->holder != current_act()) { ! 441: ulock_unlock(ulock); ! 442: return KERN_INVALID_RIGHT; ! 443: } ! 444: ! 445: ulock->unstable = FALSE; ! 446: ulock_unlock(ulock); ! 447: ! 448: return KERN_SUCCESS; ! 449: } ! 450: ! 451: /* ! 452: * ROUTINE: lock_make_unstable [internal] ! 453: * ! 454: * Marks the lock as unstable. ! 455: * ! 456: * NOTES: ! 457: * - All future acquisitions of the lock will return with a ! 458: * KERN_LOCK_UNSTABLE status, until the lock is made stable again. ! 459: */ ! 460: kern_return_t ! 461: lock_make_unstable (ulock_t ulock, thread_act_t thr_act) ! 462: { ! 463: lock_set_t lock_set; ! 464: ! 465: ! 466: lock_set = ulock->lock_set; ! 467: lock_set_lock(lock_set); ! 468: if (!lock_set->active) { ! 469: lock_set_unlock(lock_set); ! 470: return KERN_LOCK_SET_DESTROYED; ! 471: } ! 472: ! 473: ulock_lock(ulock); ! 474: lock_set_unlock(lock_set); ! 475: ! 476: if (ulock->holder != thr_act) { ! 477: ulock_unlock(ulock); ! 478: return KERN_INVALID_RIGHT; ! 479: } ! 480: ! 481: ulock->unstable = TRUE; ! 482: ulock_unlock(ulock); ! 483: ! 484: return KERN_SUCCESS; ! 485: } ! 486: ! 487: /* ! 488: * ROUTINE: lock_release_internal [internal] ! 489: * ! 490: * Releases the ulock. ! 491: * If any threads are blocked waiting for the ulock, one is woken-up. ! 492: * ! 493: */ ! 494: kern_return_t ! 495: lock_release_internal (ulock_t ulock, thread_act_t thr_act) ! 496: { ! 497: lock_set_t lock_set; ! 498: int result; ! 499: ! 500: ! 501: if ((lock_set = ulock->lock_set) == LOCK_SET_NULL) ! 502: return KERN_INVALID_ARGUMENT; ! 503: ! 504: lock_set_lock(lock_set); ! 505: if (!lock_set->active) { ! 506: lock_set_unlock(lock_set); ! 507: return KERN_LOCK_SET_DESTROYED; ! 508: } ! 509: ulock_lock(ulock); ! 510: lock_set_unlock(lock_set); ! 511: ! 512: if (ulock->holder != thr_act) { ! 513: ulock_unlock(ulock); ! 514: lock_set_unlock(lock_set); ! 515: return KERN_INVALID_RIGHT; ! 516: } ! 517: ! 518: /* ! 519: * If we have a hint that threads might be waiting, ! 520: * try to transfer the lock ownership to a waiting thread ! 521: * and wake it up. ! 522: */ ! 523: if (ulock->blocked) { ! 524: wait_queue_t wq = &ulock->wait_queue; ! 525: thread_t thread; ! 526: spl_t s; ! 527: ! 528: s = splsched(); ! 529: wait_queue_lock(wq); ! 530: thread = wait_queue_wakeup_identity_locked(wq, ! 531: LOCK_SET_EVENT, ! 532: THREAD_AWAKENED, ! 533: TRUE); ! 534: /* wait_queue now unlocked, thread locked */ ! 535: ! 536: if (thread != THREAD_NULL) { ! 537: /* ! 538: * JMM - These ownership transfer macros have a ! 539: * locking/race problem. To keep the thread from ! 540: * changing states on us (nullifying the ownership ! 541: * assignment) we need to keep the thread locked ! 542: * during the assignment. But we can't because the ! 543: * macros take an activation lock, which is a mutex. ! 544: * Since this code was already broken before I got ! 545: * here, I will leave it for now. ! 546: */ ! 547: thread_unlock(thread); ! 548: ! 549: /* ! 550: * Transfer ulock ownership ! 551: * from the current thread to the acquisition thread. ! 552: */ ! 553: ulock_ownership_clear(ulock); ! 554: ulock_ownership_set(ulock, thread); ! 555: ulock_unlock(ulock); ! 556: ! 557: return KERN_SUCCESS; ! 558: } else { ! 559: ulock->blocked = FALSE; ! 560: } ! 561: } ! 562: ! 563: /* ! 564: * Disown ulock ! 565: */ ! 566: ulock_ownership_clear(ulock); ! 567: ulock_unlock(ulock); ! 568: ! 569: return KERN_SUCCESS; ! 570: } ! 571: ! 572: kern_return_t ! 573: lock_handoff (lock_set_t lock_set, int lock_id) ! 574: { ! 575: ulock_t ulock; ! 576: int wait_result; ! 577: ! 578: ! 579: if (lock_set == LOCK_SET_NULL) ! 580: return KERN_INVALID_ARGUMENT; ! 581: ! 582: if (lock_id < 0 || lock_id >= lock_set->n_ulocks) ! 583: return KERN_INVALID_ARGUMENT; ! 584: ! 585: retry: ! 586: lock_set_lock(lock_set); ! 587: ! 588: if (!lock_set->active) { ! 589: lock_set_unlock(lock_set); ! 590: return KERN_LOCK_SET_DESTROYED; ! 591: } ! 592: ! 593: ulock = (ulock_t) &lock_set->ulock_list[lock_id]; ! 594: ulock_lock(ulock); ! 595: lock_set_unlock(lock_set); ! 596: ! 597: if (ulock->holder != current_act()) { ! 598: ulock_unlock(ulock); ! 599: lock_set_unlock(lock_set); ! 600: return KERN_INVALID_RIGHT; ! 601: } ! 602: ! 603: /* ! 604: * If the accepting thread (the receiver) is already waiting ! 605: * to accept the lock from the handoff thread (the sender), ! 606: * then perform the hand-off now. ! 607: */ ! 608: ! 609: if (ulock->accept_wait) { ! 610: wait_queue_t wq = &ulock->wait_queue; ! 611: thread_t thread; ! 612: spl_t s; ! 613: ! 614: /* ! 615: * See who the lucky devil is, if he is still there waiting. ! 616: */ ! 617: s = splsched(); ! 618: wait_queue_lock(wq); ! 619: thread = wait_queue_wakeup_identity_locked( ! 620: wq, ! 621: LOCK_SET_HANDOFF, ! 622: THREAD_AWAKENED, ! 623: TRUE); ! 624: /* wait queue unlocked, thread locked */ ! 625: ! 626: /* ! 627: * Transfer lock ownership ! 628: */ ! 629: if (thread != THREAD_NULL) { ! 630: /* ! 631: * JMM - These ownership transfer macros have a ! 632: * locking/race problem. To keep the thread from ! 633: * changing states on us (nullifying the ownership ! 634: * assignment) we need to keep the thread locked ! 635: * during the assignment. But we can't because the ! 636: * macros take an activation lock, which is a mutex. ! 637: * Since this code was already broken before I got ! 638: * here, I will leave it for now. ! 639: */ ! 640: thread_unlock(thread); ! 641: splx(s); ! 642: ! 643: ulock_ownership_clear(ulock); ! 644: ulock_ownership_set(ulock, thread); ! 645: ulock->accept_wait = FALSE; ! 646: ulock_unlock(ulock); ! 647: return KERN_SUCCESS; ! 648: } else { ! 649: ! 650: /* ! 651: * OOPS. The accepting thread must have been aborted. ! 652: * and is racing back to clear the flag that says is ! 653: * waiting for an accept. He will clear it when we ! 654: * release the lock, so just fall thru and wait for ! 655: * the next accept thread (that's the way it is ! 656: * specified). ! 657: */ ! 658: splx(s); ! 659: } ! 660: } ! 661: ! 662: /* ! 663: * Indicate that there is a hand-off thread waiting, and then wait ! 664: * for an accepting thread. ! 665: */ ! 666: ulock->ho_wait = TRUE; ! 667: wait_queue_assert_wait(&ulock->wait_queue, ! 668: LOCK_SET_HANDOFF, ! 669: THREAD_ABORTSAFE); ! 670: ulock_unlock(ulock); ! 671: ! 672: ETAP_SET_REASON(current_thread(), BLOCKED_ON_LOCK_HANDOFF); ! 673: wait_result = thread_block((void (*)(void))0); ! 674: ! 675: /* ! 676: * If the thread was woken-up via some action other than ! 677: * lock_handoff_accept or lock_set_destroy (i.e. thread_terminate), ! 678: * then we need to clear the ulock's handoff state. ! 679: */ ! 680: switch (wait_result) { ! 681: ! 682: case THREAD_AWAKENED: ! 683: return KERN_SUCCESS; ! 684: ! 685: case THREAD_INTERRUPTED: ! 686: ulock_lock(ulock); ! 687: assert(ulock->holder == current_act()); ! 688: ulock->ho_wait = FALSE; ! 689: ulock_unlock(ulock); ! 690: return KERN_ABORTED; ! 691: ! 692: case THREAD_RESTART: ! 693: goto retry; ! 694: ! 695: default: ! 696: panic("lock_handoff"); ! 697: } ! 698: } ! 699: ! 700: kern_return_t ! 701: lock_handoff_accept (lock_set_t lock_set, int lock_id) ! 702: { ! 703: ulock_t ulock; ! 704: int wait_result; ! 705: ! 706: ! 707: if (lock_set == LOCK_SET_NULL) ! 708: return KERN_INVALID_ARGUMENT; ! 709: ! 710: if (lock_id < 0 || lock_id >= lock_set->n_ulocks) ! 711: return KERN_INVALID_ARGUMENT; ! 712: ! 713: retry: ! 714: lock_set_lock(lock_set); ! 715: if (!lock_set->active) { ! 716: lock_set_unlock(lock_set); ! 717: return KERN_LOCK_SET_DESTROYED; ! 718: } ! 719: ! 720: ulock = (ulock_t) &lock_set->ulock_list[lock_id]; ! 721: ulock_lock(ulock); ! 722: lock_set_unlock(lock_set); ! 723: ! 724: /* ! 725: * If there is another accepting thread that beat us, just ! 726: * return with an error. ! 727: */ ! 728: if (ulock->accept_wait) { ! 729: ulock_unlock(ulock); ! 730: return KERN_ALREADY_WAITING; ! 731: } ! 732: ! 733: if (ulock->holder == current_act()) { ! 734: ulock_unlock(ulock); ! 735: return KERN_LOCK_OWNED_SELF; ! 736: } ! 737: ! 738: /* ! 739: * If the handoff thread (the sender) is already waiting to ! 740: * hand-off the lock to the accepting thread (the receiver), ! 741: * then perform the hand-off now. ! 742: */ ! 743: if (ulock->ho_wait) { ! 744: wait_queue_t wq = &ulock->wait_queue; ! 745: thread_t thread; ! 746: spl_t s; ! 747: ! 748: /* ! 749: * See who the lucky devil is, if he is still there waiting. ! 750: */ ! 751: assert(ulock->holder != THR_ACT_NULL); ! 752: thread = ulock->holder->thread; ! 753: ! 754: wait_queue_lock(wq); ! 755: if (wait_queue_wakeup_thread_locked(wq, ! 756: LOCK_SET_HANDOFF, ! 757: thread, ! 758: THREAD_AWAKENED, ! 759: /* unlock? */ TRUE) == KERN_SUCCESS) { ! 760: /* ! 761: * Holder thread was still waiting to give it ! 762: * away. Take over ownership. ! 763: */ ! 764: ulock_ownership_clear(ulock); ! 765: ulock_ownership_set(ulock, current_thread()); ! 766: ulock->ho_wait = FALSE; ! 767: ulock_unlock(ulock); ! 768: return (ulock->unstable) ? KERN_LOCK_UNSTABLE : ! 769: KERN_SUCCESS; ! 770: } ! 771: ! 772: /* ! 773: * OOPS. The owner was aborted out of the handoff. ! 774: * He will clear his own flag when he gets back. ! 775: * in the meantime, we will wait as if we didn't ! 776: * even see his flag (by falling thru). ! 777: */ ! 778: } ! 779: ! 780: ulock->accept_wait = TRUE; ! 781: wait_queue_assert_wait(&ulock->wait_queue, ! 782: LOCK_SET_HANDOFF, ! 783: THREAD_ABORTSAFE); ! 784: ulock_unlock(ulock); ! 785: ! 786: ETAP_SET_REASON(current_thread(), BLOCKED_ON_LOCK_HANDOFF); ! 787: wait_result = thread_block((void (*)(void))0); ! 788: ! 789: /* ! 790: * If the thread was woken-up via some action other than ! 791: * lock_handoff_accept or lock_set_destroy (i.e. thread_terminate), ! 792: * then we need to clear the ulock's handoff state. ! 793: */ ! 794: switch (wait_result) { ! 795: ! 796: case THREAD_AWAKENED: ! 797: return KERN_SUCCESS; ! 798: ! 799: case THREAD_INTERRUPTED: ! 800: ulock_lock(ulock); ! 801: ulock->accept_wait = FALSE; ! 802: ulock_unlock(ulock); ! 803: return KERN_ABORTED; ! 804: ! 805: case THREAD_RESTART: ! 806: goto retry; ! 807: ! 808: default: ! 809: panic("lock_handoff_accept"); ! 810: } ! 811: } ! 812: ! 813: /* ! 814: * Routine: lock_set_reference ! 815: * ! 816: * Take out a reference on a lock set. This keeps the data structure ! 817: * in existence (but the lock set may be deactivated). ! 818: */ ! 819: void ! 820: lock_set_reference(lock_set_t lock_set) ! 821: { ! 822: lock_set_lock(lock_set); ! 823: lock_set->ref_count++; ! 824: lock_set_unlock(lock_set); ! 825: } ! 826: ! 827: /* ! 828: * Routine: lock_set_dereference ! 829: * ! 830: * Release a reference on a lock set. If this is the last reference, ! 831: * the lock set data structure is deallocated. ! 832: */ ! 833: void ! 834: lock_set_dereference(lock_set_t lock_set) ! 835: { ! 836: int ref_count; ! 837: int size; ! 838: ! 839: lock_set_lock(lock_set); ! 840: ref_count = --(lock_set->ref_count); ! 841: lock_set_unlock(lock_set); ! 842: ! 843: if (ref_count == 0) { ! 844: size = sizeof(struct lock_set) + ! 845: (sizeof(struct ulock) * (lock_set->n_ulocks - 1)); ! 846: kfree((vm_offset_t) lock_set, size); ! 847: } ! 848: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.