Annotation of embedaddon/strongswan/src/libstrongswan/threading/rwlock.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (C) 2008-2012 Tobias Brunner
        !             3:  * Copyright (C) 2008 Martin Willi
        !             4:  * HSR Hochschule fuer Technik Rapperswil
        !             5:  *
        !             6:  * This program is free software; you can redistribute it and/or modify it
        !             7:  * under the terms of the GNU General Public License as published by the
        !             8:  * Free Software Foundation; either version 2 of the License, or (at your
        !             9:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
        !            10:  *
        !            11:  * This program is distributed in the hope that it will be useful, but
        !            12:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
        !            13:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        !            14:  * for more details.
        !            15:  */
        !            16: 
        !            17: #define _GNU_SOURCE
        !            18: #include <pthread.h>
        !            19: 
        !            20: #include <library.h>
        !            21: #include <utils/debug.h>
        !            22: 
        !            23: #include "rwlock.h"
        !            24: #include "rwlock_condvar.h"
        !            25: #include "thread.h"
        !            26: #include "condvar.h"
        !            27: #include "mutex.h"
        !            28: #include "lock_profiler.h"
        !            29: 
        !            30: #ifdef __APPLE__
        !            31: /* while pthread_rwlock_rdlock(3) says that it supports multiple read locks,
        !            32:  * this does not seem to be true. After releasing a recursive rdlock,
        !            33:  * a subsequent wrlock fails... */
        !            34: # undef HAVE_PTHREAD_RWLOCK_INIT
        !            35: #endif
        !            36: 
        !            37: typedef struct private_rwlock_t private_rwlock_t;
        !            38: typedef struct private_rwlock_condvar_t private_rwlock_condvar_t;
        !            39: 
        !            40: /**
        !            41:  * private data of rwlock
        !            42:  */
        !            43: struct private_rwlock_t {
        !            44: 
        !            45:        /**
        !            46:         * public functions
        !            47:         */
        !            48:        rwlock_t public;
        !            49: 
        !            50: #ifdef HAVE_PTHREAD_RWLOCK_INIT
        !            51: 
        !            52:        /**
        !            53:         * wrapped pthread rwlock
        !            54:         */
        !            55:        pthread_rwlock_t rwlock;
        !            56: 
        !            57: #else
        !            58: 
        !            59:        /**
        !            60:         * mutex to emulate a native rwlock
        !            61:         */
        !            62:        mutex_t *mutex;
        !            63: 
        !            64:        /**
        !            65:         * condvar to handle writers
        !            66:         */
        !            67:        condvar_t *writers;
        !            68: 
        !            69:        /**
        !            70:         * condvar to handle readers
        !            71:         */
        !            72:        condvar_t *readers;
        !            73: 
        !            74:        /**
        !            75:         * number of waiting writers
        !            76:         */
        !            77:        u_int waiting_writers;
        !            78: 
        !            79:        /**
        !            80:         * number of readers holding the lock
        !            81:         */
        !            82:        u_int reader_count;
        !            83: 
        !            84:        /**
        !            85:         * TRUE, if a writer is holding the lock currently
        !            86:         */
        !            87:        bool writer;
        !            88: 
        !            89: #endif /* HAVE_PTHREAD_RWLOCK_INIT */
        !            90: 
        !            91:        /**
        !            92:         * profiling info, if enabled
        !            93:         */
        !            94:        lock_profile_t profile;
        !            95: };
        !            96: 
        !            97: /**
        !            98:  * private data of condvar
        !            99:  */
        !           100: struct private_rwlock_condvar_t {
        !           101: 
        !           102:        /**
        !           103:         * public interface
        !           104:         */
        !           105:        rwlock_condvar_t public;
        !           106: 
        !           107:        /**
        !           108:         * mutex used to implement rwlock condvar
        !           109:         */
        !           110:        mutex_t *mutex;
        !           111: 
        !           112:        /**
        !           113:         * regular condvar to implement rwlock condvar
        !           114:         */
        !           115:        condvar_t *condvar;
        !           116: };
        !           117: 
        !           118: 
        !           119: #ifdef HAVE_PTHREAD_RWLOCK_INIT
        !           120: 
        !           121: METHOD(rwlock_t, read_lock, void,
        !           122:        private_rwlock_t *this)
        !           123: {
        !           124:        int err;
        !           125: 
        !           126:        profiler_start(&this->profile);
        !           127:        err = pthread_rwlock_rdlock(&this->rwlock);
        !           128:        if (err != 0)
        !           129:        {
        !           130:                DBG1(DBG_LIB, "!!! RWLOCK READ LOCK ERROR: %s !!!", strerror(err));
        !           131:        }
        !           132:        profiler_end(&this->profile);
        !           133: }
        !           134: 
        !           135: METHOD(rwlock_t, write_lock, void,
        !           136:        private_rwlock_t *this)
        !           137: {
        !           138:        int err;
        !           139: 
        !           140:        profiler_start(&this->profile);
        !           141:        err = pthread_rwlock_wrlock(&this->rwlock);
        !           142:        if (err != 0)
        !           143:        {
        !           144:                DBG1(DBG_LIB, "!!! RWLOCK WRITE LOCK ERROR: %s !!!", strerror(err));
        !           145:        }
        !           146:        profiler_end(&this->profile);
        !           147: }
        !           148: 
        !           149: METHOD(rwlock_t, try_write_lock, bool,
        !           150:        private_rwlock_t *this)
        !           151: {
        !           152:        return pthread_rwlock_trywrlock(&this->rwlock) == 0;
        !           153: }
        !           154: 
        !           155: METHOD(rwlock_t, unlock, void,
        !           156:        private_rwlock_t *this)
        !           157: {
        !           158:        int err;
        !           159: 
        !           160:        err = pthread_rwlock_unlock(&this->rwlock);
        !           161:        if (err != 0)
        !           162:        {
        !           163:                DBG1(DBG_LIB, "!!! RWLOCK UNLOCK ERROR: %s !!!", strerror(err));
        !           164:        }
        !           165: }
        !           166: 
        !           167: METHOD(rwlock_t, destroy, void,
        !           168:        private_rwlock_t *this)
        !           169: {
        !           170:        pthread_rwlock_destroy(&this->rwlock);
        !           171:        profiler_cleanup(&this->profile);
        !           172:        free(this);
        !           173: }
        !           174: 
        !           175: /*
        !           176:  * see header file
        !           177:  */
        !           178: rwlock_t *rwlock_create(rwlock_type_t type)
        !           179: {
        !           180:        switch (type)
        !           181:        {
        !           182:                case RWLOCK_TYPE_DEFAULT:
        !           183:                default:
        !           184:                {
        !           185:                        private_rwlock_t *this;
        !           186: 
        !           187:                        INIT(this,
        !           188:                                .public = {
        !           189:                                        .read_lock = _read_lock,
        !           190:                                        .write_lock = _write_lock,
        !           191:                                        .try_write_lock = _try_write_lock,
        !           192:                                        .unlock = _unlock,
        !           193:                                        .destroy = _destroy,
        !           194:                                }
        !           195:                        );
        !           196: 
        !           197:                        pthread_rwlock_init(&this->rwlock, NULL);
        !           198:                        profiler_init(&this->profile);
        !           199: 
        !           200:                        return &this->public;
        !           201:                }
        !           202:        }
        !           203: }
        !           204: 
        !           205: #else /* HAVE_PTHREAD_RWLOCK_INIT */
        !           206: 
        !           207: /**
        !           208:  * This implementation of the rwlock_t interface uses mutex_t and condvar_t
        !           209:  * primitives, if the pthread_rwlock_* group of functions is not available or
        !           210:  * don't allow recursive locking for readers.
        !           211:  *
        !           212:  * The following constraints are enforced:
        !           213:  *   - Multiple readers can hold the lock at the same time.
        !           214:  *   - Only a single writer can hold the lock at any given time.
        !           215:  *   - A writer must block until all readers have released the lock before
        !           216:  *     obtaining the lock exclusively.
        !           217:  *   - Readers that don't hold any read lock and arrive while a writer is
        !           218:  *     waiting to acquire the lock will block until after the writer has
        !           219:  *     obtained and released the lock.
        !           220:  * These constraints allow for read sharing, prevent write sharing, prevent
        !           221:  * read-write sharing and (largely) prevent starvation of writers by a steady
        !           222:  * stream of incoming readers.  Reader starvation is not prevented (this could
        !           223:  * happen if there are more writers than readers).
        !           224:  *
        !           225:  * The implementation supports recursive locking of the read lock but not of
        !           226:  * the write lock.  Readers must not acquire the lock exclusively at the same
        !           227:  * time and vice-versa (this is not checked or enforced so behave yourself to
        !           228:  * prevent deadlocks).
        !           229:  *
        !           230:  * Since writers are preferred a thread currently holding the read lock that
        !           231:  * tries to acquire the read lock recursively while a writer is waiting would
        !           232:  * result in a deadlock.  In order to avoid having to use a thread-specific
        !           233:  * value for each rwlock_t (or a list of threads) to keep track if a thread
        !           234:  * already acquired the read lock we use a single thread-specific value for all
        !           235:  * rwlock_t objects that keeps track of how many read locks a thread currently
        !           236:  * holds.  Preferring readers that already hold ANY read locks prevents this
        !           237:  * deadlock while it still largely avoids writer starvation (for locks that can
        !           238:  * only be acquired while holding another read lock this will obviously not
        !           239:  * work).
        !           240:  */
        !           241: 
        !           242: /**
        !           243:  * Keep track of how many read locks a thread holds.
        !           244:  */
        !           245: static pthread_key_t is_reader;
        !           246: 
        !           247: /**
        !           248:  * Only initialize the read lock counter once.
        !           249:  */
        !           250: static pthread_once_t is_reader_initialized = PTHREAD_ONCE_INIT;
        !           251: 
        !           252: /**
        !           253:  * Initialize the read lock counter.
        !           254:  */
        !           255: static void initialize_is_reader()
        !           256: {
        !           257:        pthread_key_create(&is_reader, NULL);
        !           258: }
        !           259: 
        !           260: METHOD(rwlock_t, read_lock, void,
        !           261:        private_rwlock_t *this)
        !           262: {
        !           263:        uintptr_t reading;
        !           264:        bool old;
        !           265: 
        !           266:        reading = (uintptr_t)pthread_getspecific(is_reader);
        !           267:        profiler_start(&this->profile);
        !           268:        this->mutex->lock(this->mutex);
        !           269:        if (!this->writer && reading > 0)
        !           270:        {
        !           271:                /* directly allow threads that hold ANY read locks, to avoid a deadlock
        !           272:                 * caused by preferring writers in the loop below */
        !           273:        }
        !           274:        else
        !           275:        {
        !           276:                old = thread_cancelability(FALSE);
        !           277:                while (this->writer || this->waiting_writers)
        !           278:                {
        !           279:                        this->readers->wait(this->readers, this->mutex);
        !           280:                }
        !           281:                thread_cancelability(old);
        !           282:        }
        !           283:        this->reader_count++;
        !           284:        profiler_end(&this->profile);
        !           285:        this->mutex->unlock(this->mutex);
        !           286:        pthread_setspecific(is_reader, (void*)(reading + 1));
        !           287: }
        !           288: 
        !           289: METHOD(rwlock_t, write_lock, void,
        !           290:        private_rwlock_t *this)
        !           291: {
        !           292:        bool old;
        !           293: 
        !           294:        profiler_start(&this->profile);
        !           295:        this->mutex->lock(this->mutex);
        !           296:        this->waiting_writers++;
        !           297:        old = thread_cancelability(FALSE);
        !           298:        while (this->writer || this->reader_count)
        !           299:        {
        !           300:                this->writers->wait(this->writers, this->mutex);
        !           301:        }
        !           302:        thread_cancelability(old);
        !           303:        this->waiting_writers--;
        !           304:        this->writer = TRUE;
        !           305:        profiler_end(&this->profile);
        !           306:        this->mutex->unlock(this->mutex);
        !           307: }
        !           308: 
        !           309: METHOD(rwlock_t, try_write_lock, bool,
        !           310:        private_rwlock_t *this)
        !           311: {
        !           312:        bool res = FALSE;
        !           313:        this->mutex->lock(this->mutex);
        !           314:        if (!this->writer && !this->reader_count)
        !           315:        {
        !           316:                res = this->writer = TRUE;
        !           317:        }
        !           318:        this->mutex->unlock(this->mutex);
        !           319:        return res;
        !           320: }
        !           321: 
        !           322: METHOD(rwlock_t, unlock, void,
        !           323:        private_rwlock_t *this)
        !           324: {
        !           325:        this->mutex->lock(this->mutex);
        !           326:        if (this->writer)
        !           327:        {
        !           328:                this->writer = FALSE;
        !           329:        }
        !           330:        else
        !           331:        {
        !           332:                uintptr_t reading;
        !           333: 
        !           334:                this->reader_count--;
        !           335:                reading = (uintptr_t)pthread_getspecific(is_reader);
        !           336:                pthread_setspecific(is_reader, (void*)(reading - 1));
        !           337:        }
        !           338:        if (!this->reader_count)
        !           339:        {
        !           340:                if (this->waiting_writers)
        !           341:                {
        !           342:                        this->writers->signal(this->writers);
        !           343:                }
        !           344:                else
        !           345:                {
        !           346:                        this->readers->broadcast(this->readers);
        !           347:                }
        !           348:        }
        !           349:        this->mutex->unlock(this->mutex);
        !           350: }
        !           351: 
        !           352: METHOD(rwlock_t, destroy, void,
        !           353:        private_rwlock_t *this)
        !           354: {
        !           355:        this->mutex->destroy(this->mutex);
        !           356:        this->writers->destroy(this->writers);
        !           357:        this->readers->destroy(this->readers);
        !           358:        profiler_cleanup(&this->profile);
        !           359:        free(this);
        !           360: }
        !           361: 
        !           362: /*
        !           363:  * see header file
        !           364:  */
        !           365: rwlock_t *rwlock_create(rwlock_type_t type)
        !           366: {
        !           367:        pthread_once(&is_reader_initialized,  initialize_is_reader);
        !           368: 
        !           369:        switch (type)
        !           370:        {
        !           371:                case RWLOCK_TYPE_DEFAULT:
        !           372:                default:
        !           373:                {
        !           374:                        private_rwlock_t *this;
        !           375: 
        !           376:                        INIT(this,
        !           377:                                .public = {
        !           378:                                        .read_lock = _read_lock,
        !           379:                                        .write_lock = _write_lock,
        !           380:                                        .try_write_lock = _try_write_lock,
        !           381:                                        .unlock = _unlock,
        !           382:                                        .destroy = _destroy,
        !           383:                                },
        !           384:                                .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
        !           385:                                .writers = condvar_create(CONDVAR_TYPE_DEFAULT),
        !           386:                                .readers = condvar_create(CONDVAR_TYPE_DEFAULT),
        !           387:                        );
        !           388: 
        !           389:                        profiler_init(&this->profile);
        !           390: 
        !           391:                        return &this->public;
        !           392:                }
        !           393:        }
        !           394: }
        !           395: 
        !           396: #endif /* HAVE_PTHREAD_RWLOCK_INIT */
        !           397: 
        !           398: 
        !           399: METHOD(rwlock_condvar_t, wait_, void,
        !           400:        private_rwlock_condvar_t *this, rwlock_t *lock)
        !           401: {
        !           402:        /* at this point we have the write lock locked, to make signals more
        !           403:         * predictable we try to prevent other threads from signaling by acquiring
        !           404:         * the mutex while we still hold the write lock (this assumes they will
        !           405:         * hold the write lock themselves when signaling, which is not mandatory) */
        !           406:        this->mutex->lock(this->mutex);
        !           407:        /* unlock the rwlock and wait for a signal */
        !           408:        lock->unlock(lock);
        !           409:        /* if the calling thread enabled thread cancelability we want to replicate
        !           410:         * the behavior of the regular condvar, i.e. the lock will be held again
        !           411:         * before executing cleanup functions registered by the calling thread */
        !           412:        thread_cleanup_push((thread_cleanup_t)lock->write_lock, lock);
        !           413:        thread_cleanup_push((thread_cleanup_t)this->mutex->unlock, this->mutex);
        !           414:        this->condvar->wait(this->condvar, this->mutex);
        !           415:        /* we release the mutex to allow other threads into the condvar (might even
        !           416:         * be required so we can acquire the lock again below) */
        !           417:        thread_cleanup_pop(TRUE);
        !           418:        /* finally we reacquire the lock we held previously */
        !           419:        thread_cleanup_pop(TRUE);
        !           420: }
        !           421: 
        !           422: METHOD(rwlock_condvar_t, timed_wait_abs, bool,
        !           423:        private_rwlock_condvar_t *this, rwlock_t *lock, timeval_t time)
        !           424: {
        !           425:        bool timed_out;
        !           426: 
        !           427:        /* see wait() above for details on what is going on here */
        !           428:        this->mutex->lock(this->mutex);
        !           429:        lock->unlock(lock);
        !           430:        thread_cleanup_push((thread_cleanup_t)lock->write_lock, lock);
        !           431:        thread_cleanup_push((thread_cleanup_t)this->mutex->unlock, this->mutex);
        !           432:        timed_out = this->condvar->timed_wait_abs(this->condvar, this->mutex, time);
        !           433:        thread_cleanup_pop(TRUE);
        !           434:        thread_cleanup_pop(TRUE);
        !           435:        return timed_out;
        !           436: }
        !           437: 
        !           438: METHOD(rwlock_condvar_t, timed_wait, bool,
        !           439:        private_rwlock_condvar_t *this, rwlock_t *lock, u_int timeout)
        !           440: {
        !           441:        timeval_t tv;
        !           442:        u_int s, ms;
        !           443: 
        !           444:        time_monotonic(&tv);
        !           445: 
        !           446:        s = timeout / 1000;
        !           447:        ms = timeout % 1000;
        !           448: 
        !           449:        tv.tv_sec += s;
        !           450:        timeval_add_ms(&tv, ms);
        !           451: 
        !           452:        return timed_wait_abs(this, lock, tv);
        !           453: }
        !           454: 
        !           455: METHOD(rwlock_condvar_t, signal_, void,
        !           456:        private_rwlock_condvar_t *this)
        !           457: {
        !           458:        this->mutex->lock(this->mutex);
        !           459:        this->condvar->signal(this->condvar);
        !           460:        this->mutex->unlock(this->mutex);
        !           461: }
        !           462: 
        !           463: METHOD(rwlock_condvar_t, broadcast, void,
        !           464:        private_rwlock_condvar_t *this)
        !           465: {
        !           466:        this->mutex->lock(this->mutex);
        !           467:        this->condvar->broadcast(this->condvar);
        !           468:        this->mutex->unlock(this->mutex);
        !           469: }
        !           470: 
        !           471: METHOD(rwlock_condvar_t, condvar_destroy, void,
        !           472:        private_rwlock_condvar_t *this)
        !           473: {
        !           474:        this->condvar->destroy(this->condvar);
        !           475:        this->mutex->destroy(this->mutex);
        !           476:        free(this);
        !           477: }
        !           478: 
        !           479: /*
        !           480:  * see header file
        !           481:  */
        !           482: rwlock_condvar_t *rwlock_condvar_create()
        !           483: {
        !           484:        private_rwlock_condvar_t *this;
        !           485: 
        !           486:        INIT(this,
        !           487:                .public = {
        !           488:                        .wait = _wait_,
        !           489:                        .timed_wait = _timed_wait,
        !           490:                        .timed_wait_abs = _timed_wait_abs,
        !           491:                        .signal = _signal_,
        !           492:                        .broadcast = _broadcast,
        !           493:                        .destroy = _condvar_destroy,
        !           494:                },
        !           495:                .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
        !           496:                .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
        !           497:        );
        !           498:        return &this->public;
        !           499: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>