Annotation of XNU/osfmk/kern/sync_lock.c, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.