[BACK]Return to mutex.c CVS log [TXT][DIR] Up to [local] / prex-old / sys / sync

Annotation of prex-old/sys/sync/mutex.c, Revision 1.1

1.1     ! nbrk        1: /*-
        !             2:  * Copyright (c) 2005-2007, Kohsuke Ohtani
        !             3:  * All rights reserved.
        !             4:  *
        !             5:  * Redistribution and use in source and binary forms, with or without
        !             6:  * modification, are permitted provided that the following conditions
        !             7:  * are met:
        !             8:  * 1. Redistributions of source code must retain the above copyright
        !             9:  *    notice, this list of conditions and the following disclaimer.
        !            10:  * 2. Redistributions in binary form must reproduce the above copyright
        !            11:  *    notice, this list of conditions and the following disclaimer in the
        !            12:  *    documentation and/or other materials provided with the distribution.
        !            13:  * 3. Neither the name of the author nor the names of any co-contributors
        !            14:  *    may be used to endorse or promote products derived from this software
        !            15:  *    without specific prior written permission.
        !            16:  *
        !            17:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
        !            18:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            19:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            20:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
        !            21:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            22:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
        !            23:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            24:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
        !            25:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        !            26:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !            27:  * SUCH DAMAGE.
        !            28:  */
        !            29:
        !            30: /*
        !            31:  * mutex.c - mutual exclusion service.
        !            32:  */
        !            33:
        !            34: /*
        !            35:  * A mutex is used to protect un-sharable resources.
        !            36:  * A thread can use mutex_lock() to ensure that global resource is not
        !            37:  * accessed by other thread. The mutex is effective only the threads
        !            38:  * belonging to the same task.
        !            39:  *
        !            40:  * Prex will change the thread priority to prevent priority inversion.
        !            41:  *
        !            42:  * <Priority inheritance>
        !            43:  *   The priority is changed at the following conditions.
        !            44:  *
        !            45:  *   1. When the current thread can not lock the mutex and its mutex
        !            46:  *      owner has lower priority than current thread, the priority
        !            47:  *      of mutex owner is boosted to same priority with current thread.
        !            48:  *      If this mutex owner is waiting for another mutex, such related
        !            49:  *      mutexes are also processed.
        !            50:  *
        !            51:  *   2. When the current thread unlocks the mutex and its priority
        !            52:  *      has already been inherited, the current priority is reset.
        !            53:  *      In this time, the current priority is changed to the highest
        !            54:  *      priority among the threads waiting for the mutexes locked by
        !            55:  *      current thread.
        !            56:  *
        !            57:  *   3. When the thread priority is changed by user request, the
        !            58:  *      inherited thread's priority is changed.
        !            59:  *
        !            60:  * <Limitation>
        !            61:  *
        !            62:  *   1. If the priority is changed by user request, the priority
        !            63:  *      recomputation is done only when the new priority is higher
        !            64:  *      than old priority. The inherited priority is reset to base
        !            65:  *      priority when the mutex is unlocked.
        !            66:  *
        !            67:  *   2. Even if thread is killed with mutex waiting, the related
        !            68:  *      priority is not adjusted.
        !            69:  *
        !            70:  * <Important>
        !            71:  *  Since this implementation does not support recursive lock, a thread
        !            72:  *  can not lock the same mutex twice.
        !            73:  */
        !            74:
        !            75: #include <kernel.h>
        !            76: #include <event.h>
        !            77: #include <sched.h>
        !            78: #include <kmem.h>
        !            79: #include <thread.h>
        !            80: #include <task.h>
        !            81: #include <sync.h>
        !            82:
        !            83: /* max mutex count to inherit priority */
        !            84: #define MAXINHERIT     10
        !            85:
        !            86: /* forward declarations */
        !            87: static int prio_inherit(thread_t th);
        !            88: static void prio_uninherit(thread_t th);
        !            89:
        !            90: /*
        !            91:  * Initialize a mutex.
        !            92:  *
        !            93:  * If an initialized mutex is reinitialized, undefined behavior
        !            94:  * results. Technically, we can not detect such error condition
        !            95:  * here because we can not touch the passed object in kernel.
        !            96:  */
        !            97: int
        !            98: mutex_init(mutex_t *mtx)
        !            99: {
        !           100:        mutex_t m;
        !           101:
        !           102:        if ((m = kmem_alloc(sizeof(struct mutex))) == NULL)
        !           103:                return ENOMEM;
        !           104:
        !           105:        event_init(&m->event, "mutex");
        !           106:        m->task = cur_task();
        !           107:        m->owner = NULL;
        !           108:        m->prio = MIN_PRIO;
        !           109:        m->magic = MUTEX_MAGIC;
        !           110:
        !           111:        if (umem_copyout(&m, mtx, sizeof(mutex_t))) {
        !           112:                kmem_free(m);
        !           113:                return EFAULT;
        !           114:        }
        !           115:        return 0;
        !           116: }
        !           117:
        !           118: /*
        !           119:  * Destroy the specified mutex.
        !           120:  * The mutex must be unlock state, otherwise it fails with EBUSY.
        !           121:  */
        !           122: int
        !           123: mutex_destroy(mutex_t *mtx)
        !           124: {
        !           125:        mutex_t m;
        !           126:        int err = 0;
        !           127:
        !           128:        sched_lock();
        !           129:        if (umem_copyin(mtx, &m, sizeof(mutex_t))) {
        !           130:                err = EFAULT;
        !           131:        } else if (!mutex_valid(m)) {
        !           132:                err = EINVAL;
        !           133:        } else if (m->owner || event_waiting(&m->event)) {
        !           134:                err = EBUSY;
        !           135:        } else {
        !           136:                m->magic = 0;
        !           137:                kmem_free(m);
        !           138:        }
        !           139:        sched_unlock();
        !           140:        ASSERT(err == 0);
        !           141:        return err;
        !           142: }
        !           143:
        !           144: /*
        !           145:  * Copy mutex from user space.
        !           146:  * If it is not initialized, create new mutex.
        !           147:  * @umtx: pointer to mutex in user space.
        !           148:  * @kmtx: pointer to mutex in kernel space.
        !           149:  */
        !           150: static int
        !           151: mutex_copyin(mutex_t *umtx, mutex_t *kmtx)
        !           152: {
        !           153:        mutex_t m;
        !           154:        int err;
        !           155:
        !           156:        if (umem_copyin(umtx, &m, sizeof(mutex_t)))
        !           157:                return EFAULT;
        !           158:
        !           159:        if (m == MUTEX_INITIALIZER) {
        !           160:                /*
        !           161:                 * Allocate mutex.
        !           162:                 */
        !           163:                if ((err = mutex_init(umtx)))
        !           164:                        return err;
        !           165:                umem_copyin(umtx, &m, sizeof(mutex_t));
        !           166:        } else {
        !           167:                if (!mutex_valid(m))
        !           168:                        return EINVAL;
        !           169:        }
        !           170:        *kmtx = m;
        !           171:        return 0;
        !           172: }
        !           173:
        !           174: /*
        !           175:  * Lock a mutex.
        !           176:  *
        !           177:  * A current thread is blocked if the mutex has already been locked.
        !           178:  * If current thread receives any exception while waiting mutex, this
        !           179:  * routine returns with EINTR in order to invoke exception handler.
        !           180:  * But, POSIX thread assumes this function does NOT return with EINTR.
        !           181:  * So, system call stub routine in library must call this again if
        !           182:  * it gets EINTR.
        !           183:  */
        !           184: int
        !           185: mutex_lock(mutex_t *mtx)
        !           186: {
        !           187:        mutex_t m;
        !           188:        int rc, err;
        !           189:
        !           190:        sched_lock();
        !           191:        if ((err = mutex_copyin(mtx, &m)))
        !           192:                goto out;
        !           193:
        !           194:        if (m->owner == cur_thread) {
        !           195:                /*
        !           196:                 * Recursive lock
        !           197:                 */
        !           198:                m->lock_count++;
        !           199:        } else {
        !           200:                /*
        !           201:                 * Check whether a target mutex is locked.
        !           202:                 * If the mutex is not locked, this routine
        !           203:                 * returns immediately.
        !           204:                 */
        !           205:                if (m->owner == NULL)
        !           206:                        m->prio = cur_thread->prio;
        !           207:                else {
        !           208:                        /*
        !           209:                         * Wait for a mutex.
        !           210:                         */
        !           211:                        cur_thread->wait_mutex = m;
        !           212:                        if ((err = prio_inherit(cur_thread))) {
        !           213:                                cur_thread->wait_mutex = NULL;
        !           214:                                goto out;
        !           215:                        }
        !           216:                        rc = sched_sleep(&m->event);
        !           217:                        cur_thread->wait_mutex = NULL;
        !           218:                        if (rc == SLP_INTR) {
        !           219:                                err = EINTR;
        !           220:                                goto out;
        !           221:                        }
        !           222:                }
        !           223:                m->lock_count = 1;
        !           224:        }
        !           225:        m->owner = cur_thread;
        !           226:        list_insert(&cur_thread->mutexes, &m->link);
        !           227:  out:
        !           228:        sched_unlock();
        !           229:        return err;
        !           230: }
        !           231:
        !           232: /*
        !           233:  * Try to lock a mutex without blocking.
        !           234:  */
        !           235: int
        !           236: mutex_trylock(mutex_t *mtx)
        !           237: {
        !           238:        mutex_t m;
        !           239:        int err;
        !           240:
        !           241:        sched_lock();
        !           242:        if ((err = mutex_copyin(mtx, &m)))
        !           243:                goto out;
        !           244:        if (m->owner == cur_thread)
        !           245:                m->lock_count++;
        !           246:        else {
        !           247:                if (m->owner != NULL)
        !           248:                        err = EBUSY;
        !           249:                else {
        !           250:                        m->lock_count = 1;
        !           251:                        m->owner = cur_thread;
        !           252:                        list_insert(&cur_thread->mutexes, &m->link);
        !           253:                }
        !           254:        }
        !           255:  out:
        !           256:        sched_unlock();
        !           257:        return err;
        !           258: }
        !           259:
        !           260: /*
        !           261:  * Unlock a mutex.
        !           262:  * Caller must be a current mutex owner.
        !           263:  */
        !           264: int
        !           265: mutex_unlock(mutex_t *mtx)
        !           266: {
        !           267:        mutex_t m;
        !           268:        int err;
        !           269:
        !           270:        sched_lock();
        !           271:        if ((err = mutex_copyin(mtx, &m)))
        !           272:                goto out;
        !           273:        if (m->owner != cur_thread || m->lock_count <= 0) {
        !           274:                err = EPERM;
        !           275:                goto out;
        !           276:        }
        !           277:        if (--m->lock_count == 0) {
        !           278:                list_remove(&m->link);
        !           279:                prio_uninherit(cur_thread);
        !           280:                /*
        !           281:                 * Change the mutex owner, and make the next
        !           282:                 * owner runnable if it exists.
        !           283:                 */
        !           284:                m->owner = sched_wakeone(&m->event);
        !           285:                if (m->owner)
        !           286:                        m->owner->wait_mutex = NULL;
        !           287:
        !           288:                m->prio = m->owner ? m->owner->prio : MIN_PRIO;
        !           289:        }
        !           290:  out:
        !           291:        sched_unlock();
        !           292:        ASSERT(err == 0);
        !           293:        return err;
        !           294: }
        !           295:
        !           296: /*
        !           297:  * Clean up mutex.
        !           298:  *
        !           299:  * This is called with scheduling locked when thread is terminated.
        !           300:  * If a thread is terminated with mutex hold, all waiting threads
        !           301:  * keeps waiting forever. So, all mutex locked by terminated thread
        !           302:  * must be unlocked. Even if the terminated thread is waiting some
        !           303:  * mutex, the inherited priority of other mutex owner is not adjusted.
        !           304:  */
        !           305: void
        !           306: mutex_cleanup(thread_t th)
        !           307: {
        !           308:        list_t head;
        !           309:        mutex_t m;
        !           310:        thread_t owner;
        !           311:
        !           312:        /*
        !           313:         * Purge all mutexes held by the thread.
        !           314:         */
        !           315:        head = &th->mutexes;
        !           316:        while (!list_empty(head)) {
        !           317:                /*
        !           318:                 * Release locked mutex.
        !           319:                 */
        !           320:                m = list_entry(list_first(head), struct mutex, link);
        !           321:                m->lock_count = 0;
        !           322:                list_remove(&m->link);
        !           323:                /*
        !           324:                 * Change the mutex owner if other thread
        !           325:                 * is waiting for it.
        !           326:                 */
        !           327:                owner = sched_wakeone(&m->event);
        !           328:                if (owner) {
        !           329:                        owner->wait_mutex = NULL;
        !           330:                        m->lock_count = 1;
        !           331:                        list_insert(&owner->mutexes, &m->link);
        !           332:                }
        !           333:                m->owner = owner;
        !           334:        }
        !           335: }
        !           336:
        !           337: /*
        !           338:  * This is called with scheduling locked before thread priority
        !           339:  * is changed.
        !           340:  */
        !           341: void
        !           342: mutex_setprio(thread_t th, int prio)
        !           343: {
        !           344:        if (th->wait_mutex && prio < th->prio)
        !           345:                prio_inherit(th);
        !           346: }
        !           347:
        !           348: /*
        !           349:  * Inherit priority.
        !           350:  * @waiter: thread that is about to wait a mutex.
        !           351:  *
        !           352:  * To prevent priority inversion, we must ensure the higher priority
        !           353:  * thread does not wait other lower priority thread. So, raise the
        !           354:  * priority of mutex owner which blocks the "waiter" thread. If such
        !           355:  * mutex owner is also waiting for other mutex, that mutex is also
        !           356:  * processed.
        !           357:  * Returns EDEALK if it finds deadlock condition.
        !           358:  */
        !           359: static int
        !           360: prio_inherit(thread_t waiter)
        !           361: {
        !           362:        mutex_t m = waiter->wait_mutex;
        !           363:        thread_t owner;
        !           364:        int count = 0;
        !           365:
        !           366:        do {
        !           367:                owner = m->owner;
        !           368:                /*
        !           369:                 * If the owner of relative mutex has already
        !           370:                 * been waiting for the "waiter" thread, it
        !           371:                 * causes a deadlock.
        !           372:                 */
        !           373:                if (owner == waiter) {
        !           374:                        printk("Deadlock! mutex=%x owner=%x waiter=%x\n",
        !           375:                               m, owner, waiter);
        !           376:                        return EDEADLK;
        !           377:                }
        !           378:                /*
        !           379:                 * If the priority of the mutex owner is lower
        !           380:                 * than "waiter" thread's, we rise the mutex
        !           381:                 * owner's priority.
        !           382:                 */
        !           383:                if (owner->prio > waiter->prio) {
        !           384:                        sched_setprio(owner, owner->base_prio, waiter->prio);
        !           385:                        m->prio = waiter->prio;
        !           386:                }
        !           387:                /*
        !           388:                 * If the mutex owner is waiting for another
        !           389:                 * mutex, that mutex is also processed.
        !           390:                 */
        !           391:                m = (mutex_t)owner->wait_mutex;
        !           392:
        !           393:                /* Fail safe... */
        !           394:                ASSERT(count < MAXINHERIT);
        !           395:                if (count >= MAXINHERIT)
        !           396:                        break;
        !           397:
        !           398:        } while (m != NULL);
        !           399:        return 0;
        !           400: }
        !           401:
        !           402: /*
        !           403:  * Un-inherit priority
        !           404:  *
        !           405:  * The priority of specified thread is reset to the base priority.
        !           406:  * If specified thread locks other mutex and higher priority thread
        !           407:  * is waiting for it, the priority is kept to that level.
        !           408:  */
        !           409: static void
        !           410: prio_uninherit(thread_t th)
        !           411: {
        !           412:        int top_prio;
        !           413:        list_t head, n;
        !           414:        mutex_t m;
        !           415:
        !           416:        /* Check if the priority is inherited. */
        !           417:        if (th->prio == th->base_prio)
        !           418:                return;
        !           419:
        !           420:        top_prio = th->base_prio;
        !           421:        /*
        !           422:         * Find the highest priority thread that is waiting
        !           423:         * for the thread. This is done by checking all mutexes
        !           424:         * that the thread locks.
        !           425:         */
        !           426:        head = &th->mutexes;
        !           427:        for (n = list_first(head); n != head; n = list_next(n)) {
        !           428:                m = list_entry(n, struct mutex, link);
        !           429:                if (m->prio < top_prio)
        !           430:                        top_prio = m->prio;
        !           431:        }
        !           432:        sched_setprio(th, th->base_prio, top_prio);
        !           433: }

CVSweb