[BACK]Return to pthread_rwlock.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / lib / libpthread

Annotation of src/lib/libpthread/pthread_rwlock.c, Revision 1.7

1.7     ! cl          1: /*     $NetBSD: pthread_rwlock.c,v 1.6 2003/11/24 23:54:13 cl Exp $ */
1.2       thorpej     2:
                      3: /*-
                      4:  * Copyright (c) 2002 The NetBSD Foundation, Inc.
                      5:  * All rights reserved.
                      6:  *
                      7:  * This code is derived from software contributed to The NetBSD Foundation
                      8:  * by Nathan J. Williams.
                      9:  *
                     10:  * Redistribution and use in source and binary forms, with or without
                     11:  * modification, are permitted provided that the following conditions
                     12:  * are met:
                     13:  * 1. Redistributions of source code must retain the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer.
                     15:  * 2. Redistributions in binary form must reproduce the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer in the
                     17:  *    documentation and/or other materials provided with the distribution.
                     18:  * 3. All advertising materials mentioning features or use of this software
                     19:  *    must display the following acknowledgement:
                     20:  *        This product includes software developed by the NetBSD
                     21:  *        Foundation, Inc. and its contributors.
                     22:  * 4. Neither the name of The NetBSD Foundation nor the names of its
                     23:  *    contributors may be used to endorse or promote products derived
                     24:  *    from this software without specific prior written permission.
                     25:  *
                     26:  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
                     27:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
                     28:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     29:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
                     30:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     31:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     32:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     33:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     34:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     35:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     36:  * POSSIBILITY OF SUCH DAMAGE.
                     37:  */
                     38:
1.5       lukem      39: #include <sys/cdefs.h>
1.7     ! cl         40: __RCSID("$NetBSD: pthread_rwlock.c,v 1.6 2003/11/24 23:54:13 cl Exp $");
1.5       lukem      41:
1.2       thorpej    42: #include <errno.h>
                     43:
                     44: #include "pthread.h"
                     45: #include "pthread_int.h"
                     46:
                     47: static void pthread_rwlock__callback(void *);
                     48:
                     49: __strong_alias(__libc_rwlock_init,pthread_rwlock_init)
                     50: __strong_alias(__libc_rwlock_rdlock,pthread_rwlock_rdlock)
                     51: __strong_alias(__libc_rwlock_wrlock,pthread_rwlock_wrlock)
                     52: __strong_alias(__libc_rwlock_tryrdlock,pthread_rwlock_tryrdlock)
                     53: __strong_alias(__libc_rwlock_trywrlock,pthread_rwlock_trywrlock)
                     54: __strong_alias(__libc_rwlock_unlock,pthread_rwlock_unlock)
                     55: __strong_alias(__libc_rwlock_destroy,pthread_rwlock_destroy)
                     56:
                     57: int
                     58: pthread_rwlock_init(pthread_rwlock_t *rwlock,
                     59:            const pthread_rwlockattr_t *attr)
                     60: {
                     61: #ifdef ERRORCHECK
                     62:        if ((rwlock == NULL) ||
                     63:            (attr && (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC)))
                     64:                return EINVAL;
                     65: #endif
                     66:        rwlock->ptr_magic = _PT_RWLOCK_MAGIC;
                     67:        pthread_lockinit(&rwlock->ptr_interlock);
                     68:        PTQ_INIT(&rwlock->ptr_rblocked);
                     69:        PTQ_INIT(&rwlock->ptr_wblocked);
                     70:        rwlock->ptr_nreaders = 0;
                     71:        rwlock->ptr_writer = NULL;
                     72:
                     73:        return 0;
                     74: }
                     75:
                     76:
                     77: int
                     78: pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
                     79: {
                     80: #ifdef ERRORCHECK
                     81:        if ((rwlock == NULL) ||
                     82:            (rwlock->ptr_magic != _PT_RWLOCK_MAGIC) ||
                     83:            (!PTQ_EMPTY(&rwlock->ptr_rblocked)) ||
                     84:            (!PTQ_EMPTY(&rwlock->ptr_wblocked)) ||
                     85:            (rwlock->ptr_nreaders != 0) ||
                     86:            (rwlock->ptr_writer != NULL))
                     87:                return EINVAL;
                     88: #endif
                     89:        rwlock->ptr_magic = _PT_RWLOCK_DEAD;
                     90:
                     91:        return 0;
                     92: }
                     93:
                     94:
                     95: int
                     96: pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
                     97: {
                     98:        pthread_t self;
                     99: #ifdef ERRORCHECK
                    100:        if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
                    101:                return EINVAL;
                    102: #endif
                    103:        self = pthread__self();
                    104:
                    105:        pthread_spinlock(self, &rwlock->ptr_interlock);
                    106: #ifdef ERRORCHECK
                    107:        if (rwlock->ptr_writer == self) {
                    108:                pthread_spinunlock(self, &rwlock->ptr_interlock);
                    109:                return EDEADLK;
                    110:        }
                    111: #endif
                    112:        /*
                    113:         * Don't get a readlock if there is a writer or if there are waiting
                    114:         * writers; i.e. prefer writers to readers. This strategy is dictated
                    115:         * by SUSv3.
                    116:         */
                    117:        while ((rwlock->ptr_writer != NULL) ||
                    118:            (!PTQ_EMPTY(&rwlock->ptr_wblocked))) {
                    119:                PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep);
                    120:                /* Locking a rwlock is not a cancellation point; don't check */
                    121:                pthread_spinlock(self, &self->pt_statelock);
                    122:                self->pt_state = PT_STATE_BLOCKED_QUEUE;
                    123:                self->pt_sleepobj = rwlock;
                    124:                self->pt_sleepq = &rwlock->ptr_rblocked;
                    125:                self->pt_sleeplock = &rwlock->ptr_interlock;
                    126:                pthread_spinunlock(self, &self->pt_statelock);
                    127:                pthread__block(self, &rwlock->ptr_interlock);
                    128:                /* interlock is not held when we return */
                    129:                pthread_spinlock(self, &rwlock->ptr_interlock);
                    130:        }
                    131:
                    132:        rwlock->ptr_nreaders++;
                    133:        pthread_spinunlock(self, &rwlock->ptr_interlock);
                    134:
                    135:        return 0;
                    136: }
                    137:
                    138:
                    139: int
                    140: pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
                    141: {
                    142:        pthread_t self;
                    143: #ifdef ERRORCHECK
                    144:        if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
                    145:                return EINVAL;
                    146: #endif
                    147:        self = pthread__self();
                    148:
                    149:        pthread_spinlock(self, &rwlock->ptr_interlock);
                    150:        /*
                    151:         * Don't get a readlock if there is a writer or if there are waiting
                    152:         * writers; i.e. prefer writers to readers. This strategy is dictated
                    153:         * by SUSv3.
                    154:         */
                    155:        if ((rwlock->ptr_writer != NULL) ||
                    156:            (!PTQ_EMPTY(&rwlock->ptr_wblocked))) {
                    157:                pthread_spinunlock(self, &rwlock->ptr_interlock);
                    158:                return EBUSY;
                    159:        }
                    160:
                    161:        rwlock->ptr_nreaders++;
                    162:        pthread_spinunlock(self, &rwlock->ptr_interlock);
                    163:
                    164:        return 0;
                    165: }
                    166:
                    167:
                    168: int
                    169: pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
                    170: {
                    171:        pthread_t self;
                    172: #ifdef ERRORCHECK
                    173:        if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
                    174:                return EINVAL;
                    175: #endif
                    176:        self = pthread__self();
                    177:
                    178:        pthread_spinlock(self, &rwlock->ptr_interlock);
1.7     ! cl        179: #ifdef ERRORCHECK
        !           180:        if (rwlock->ptr_writer == self) {
        !           181:                pthread_spinunlock(self, &rwlock->ptr_interlock);
        !           182:                return EDEADLK;
        !           183:        }
        !           184: #endif
1.2       thorpej   185:        /*
                    186:         * Prefer writers to readers here; permit writers even if there are
                    187:         * waiting readers.
                    188:         */
                    189:        while ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) {
                    190:                PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep);
                    191:                /* Locking a rwlock is not a cancellation point; don't check */
                    192:                pthread_spinlock(self, &self->pt_statelock);
                    193:                self->pt_state = PT_STATE_BLOCKED_QUEUE;
                    194:                self->pt_sleepobj = rwlock;
                    195:                self->pt_sleepq = &rwlock->ptr_wblocked;
                    196:                self->pt_sleeplock = &rwlock->ptr_interlock;
                    197:                pthread_spinunlock(self, &self->pt_statelock);
                    198:                pthread__block(self, &rwlock->ptr_interlock);
                    199:                /* interlock is not held when we return */
                    200:                pthread_spinlock(self, &rwlock->ptr_interlock);
                    201:        }
                    202:
                    203:        rwlock->ptr_writer = self;
                    204:        pthread_spinunlock(self, &rwlock->ptr_interlock);
                    205:
                    206:        return 0;
                    207: }
                    208:
                    209:
                    210: int
                    211: pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
                    212: {
                    213:        pthread_t self;
                    214: #ifdef ERRORCHECK
                    215:        if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
                    216:                return EINVAL;
                    217: #endif
                    218:        self = pthread__self();
                    219:
                    220:        pthread_spinlock(self, &rwlock->ptr_interlock);
                    221:        /*
                    222:         * Prefer writers to readers here; permit writers even if there are
                    223:         * waiting readers.
                    224:         */
                    225:        if ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) {
                    226:                pthread_spinunlock(self, &rwlock->ptr_interlock);
                    227:                return EBUSY;
                    228:        }
                    229:
                    230:        rwlock->ptr_writer = self;
                    231:        pthread_spinunlock(self, &rwlock->ptr_interlock);
                    232:
                    233:        return 0;
                    234: }
                    235:
                    236:
                    237: struct pthread_rwlock__waitarg {
                    238:        pthread_t ptw_thread;
                    239:        pthread_rwlock_t *ptw_rwlock;
                    240:        struct pthread_queue_t *ptw_queue;
                    241: };
                    242:
                    243: int
                    244: pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock,
                    245:            const struct timespec *abs_timeout)
                    246: {
                    247:        pthread_t self;
                    248:        struct pthread_rwlock__waitarg wait;
                    249:        struct pt_alarm_t alarm;
                    250:        int retval;
                    251: #ifdef ERRORCHECK
                    252:        if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
                    253:                return EINVAL;
                    254:        if ((abs_timeout == NULL) || (abs_timeout->tv_nsec >= 1000000000))
                    255:                return EINVAL;
                    256: #endif
                    257:        self = pthread__self();
                    258:
                    259:        pthread_spinlock(self, &rwlock->ptr_interlock);
                    260: #ifdef ERRORCHECK
                    261:        if (rwlock->ptr_writer == self) {
                    262:                pthread_spinlock(self, &rwlock->ptr_interlock);
                    263:                return EDEADLK;
                    264:        }
                    265: #endif
                    266:        /*
                    267:         * Don't get a readlock if there is a writer or if there are waiting
                    268:         * writers; i.e. prefer writers to readers. This strategy is dictated
                    269:         * by SUSv3.
                    270:         */
                    271:        retval = 0;
                    272:        while ((retval == 0) && ((rwlock->ptr_writer != NULL) ||
                    273:            (!PTQ_EMPTY(&rwlock->ptr_wblocked)))) {
                    274:                wait.ptw_thread = self;
                    275:                wait.ptw_rwlock = rwlock;
                    276:                wait.ptw_queue = &rwlock->ptr_rblocked;
                    277:                pthread__alarm_add(self, &alarm, abs_timeout,
                    278:                    pthread_rwlock__callback, &wait);
                    279:                PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep);
                    280:                /* Locking a rwlock is not a cancellation point; don't check */
                    281:                pthread_spinlock(self, &self->pt_statelock);
                    282:                self->pt_state = PT_STATE_BLOCKED_QUEUE;
                    283:                self->pt_sleepobj = rwlock;
                    284:                self->pt_sleepq = &rwlock->ptr_rblocked;
                    285:                self->pt_sleeplock = &rwlock->ptr_interlock;
                    286:                pthread_spinunlock(self, &self->pt_statelock);
                    287:                pthread__block(self, &rwlock->ptr_interlock);
                    288:                /* interlock is not held when we return */
                    289:                pthread__alarm_del(self, &alarm);
                    290:                if (pthread__alarm_fired(&alarm))
                    291:                        retval = ETIMEDOUT;
                    292:                pthread_spinlock(self, &rwlock->ptr_interlock);
                    293:        }
                    294:
                    295:        if (retval == 0)
                    296:                rwlock->ptr_nreaders++;
                    297:        pthread_spinunlock(self, &rwlock->ptr_interlock);
                    298:
                    299:        return retval;
                    300: }
                    301:
                    302:
                    303: int
                    304: pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock,
                    305:            const struct timespec *abs_timeout)
                    306: {
                    307:        struct pthread_rwlock__waitarg wait;
                    308:        struct pt_alarm_t alarm;
                    309:        int retval;
                    310:        pthread_t self;
                    311: #ifdef ERRORCHECK
                    312:        if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
                    313:                return EINVAL;
                    314: #endif
                    315:        self = pthread__self();
                    316:
                    317:        pthread_spinlock(self, &rwlock->ptr_interlock);
1.7     ! cl        318: #ifdef ERRORCHECK
        !           319:        if (rwlock->ptr_writer == self) {
        !           320:                pthread_spinlock(self, &rwlock->ptr_interlock);
        !           321:                return EDEADLK;
        !           322:        }
        !           323: #endif
1.2       thorpej   324:        /*
                    325:         * Prefer writers to readers here; permit writers even if there are
                    326:         * waiting readers.
                    327:         */
                    328:        retval = 0;
                    329:        while (retval == 0 &&
                    330:            ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL))) {
                    331:                wait.ptw_thread = self;
                    332:                wait.ptw_rwlock = rwlock;
                    333:                wait.ptw_queue = &rwlock->ptr_wblocked;
                    334:                pthread__alarm_add(self, &alarm, abs_timeout,
                    335:                    pthread_rwlock__callback, &wait);
                    336:                PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep);
                    337:                /* Locking a rwlock is not a cancellation point; don't check */
                    338:                pthread_spinlock(self, &self->pt_statelock);
                    339:                self->pt_state = PT_STATE_BLOCKED_QUEUE;
                    340:                self->pt_sleepobj = rwlock;
                    341:                self->pt_sleepq = &rwlock->ptr_wblocked;
                    342:                self->pt_sleeplock = &rwlock->ptr_interlock;
                    343:                pthread_spinunlock(self, &self->pt_statelock);
                    344:                pthread__block(self, &rwlock->ptr_interlock);
                    345:                /* interlock is not held when we return */
                    346:                pthread__alarm_del(self, &alarm);
                    347:                if (pthread__alarm_fired(&alarm))
                    348:                        retval = ETIMEDOUT;
                    349:                pthread_spinlock(self, &rwlock->ptr_interlock);
                    350:        }
                    351:
                    352:        if (retval == 0)
                    353:                rwlock->ptr_writer = self;
                    354:        pthread_spinunlock(self, &rwlock->ptr_interlock);
                    355:
                    356:        return 0;
                    357: }
                    358:
                    359:
                    360: static void
                    361: pthread_rwlock__callback(void *arg)
                    362: {
                    363:        struct pthread_rwlock__waitarg *a;
                    364:        pthread_t self;
                    365:
                    366:        a = arg;
                    367:        self = pthread__self();
                    368:
                    369:        pthread_spinlock(self, &a->ptw_rwlock->ptr_interlock);
                    370:        /*
                    371:         * Don't dequeue and schedule the thread if it's already been
                    372:         * queued up by a signal or broadcast (but hasn't yet run as far
                    373:         * as pthread__alarm_del(), or we wouldn't be here, and hence can't
                    374:         * have become blocked on some *other* queue).
                    375:         */
                    376:        if (a->ptw_thread->pt_state == PT_STATE_BLOCKED_QUEUE) {
                    377:                PTQ_REMOVE(a->ptw_queue, a->ptw_thread, pt_sleep);
                    378:                pthread__sched(self, a->ptw_thread);
                    379:        }
                    380:        pthread_spinunlock(self, &a->ptw_rwlock->ptr_interlock);
                    381:
                    382: }
                    383:
                    384:
                    385: int
                    386: pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
                    387: {
1.3       nathanw   388:        pthread_t self, writer;
1.2       thorpej   389:        struct pthread_queue_t blockedq;
                    390: #ifdef ERRORCHECK
                    391:        if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
                    392:                return EINVAL;
                    393: #endif
                    394:        writer = NULL;
                    395:        PTQ_INIT(&blockedq);
                    396:        self = pthread__self();
                    397:
                    398:        pthread_spinlock(self, &rwlock->ptr_interlock);
                    399:        if (rwlock->ptr_writer != NULL) {
                    400:                /* Releasing a write lock. */
                    401: #ifdef ERRORCHECK
                    402:                if (rwlock->ptr_writer != self) {
                    403:                        pthread_spinunlock(self, &rwlock->ptr_interlock);
                    404:                        return EPERM;
                    405:                }
                    406: #endif
                    407:                rwlock->ptr_writer = NULL;
                    408:                writer = PTQ_FIRST(&rwlock->ptr_wblocked);
                    409:                if (writer != NULL) {
                    410:                        PTQ_REMOVE(&rwlock->ptr_wblocked, writer, pt_sleep);
                    411:                } else {
                    412:                        blockedq = rwlock->ptr_rblocked;
                    413:                        PTQ_INIT(&rwlock->ptr_rblocked);
                    414:                }
1.7     ! cl        415:        } else
        !           416: #ifdef ERRORCHECK
        !           417:        if (rwlock->ptr_nreaders > 0)
        !           418: #endif
        !           419:        {
1.2       thorpej   420:                /* Releasing a read lock. */
                    421:                rwlock->ptr_nreaders--;
                    422:                if (rwlock->ptr_nreaders == 0) {
                    423:                        writer = PTQ_FIRST(&rwlock->ptr_wblocked);
                    424:                        if (writer != NULL)
                    425:                                PTQ_REMOVE(&rwlock->ptr_wblocked, writer,
                    426:                                    pt_sleep);
                    427:                }
1.7     ! cl        428: #ifdef ERRORCHECK
        !           429:        } else {
        !           430:                pthread_spinunlock(self, &rwlock->ptr_interlock);
        !           431:                return EPERM;
        !           432: #endif
1.2       thorpej   433:        }
                    434:
                    435:        if (writer != NULL)
                    436:                pthread__sched(self, writer);
                    437:        else
1.3       nathanw   438:                pthread__sched_sleepers(self, &blockedq);
1.2       thorpej   439:
1.6       cl        440:        pthread_spinunlock(self, &rwlock->ptr_interlock);
                    441:
1.2       thorpej   442:        return 0;
                    443: }
                    444:
                    445:
                    446: int
                    447: pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
                    448: {
                    449: #ifdef ERRORCHECK
                    450:        if (attr == NULL)
                    451:                return EINVAL;
                    452: #endif
                    453:        attr->ptra_magic = _PT_RWLOCKATTR_MAGIC;
                    454:
                    455:        return 0;
                    456: }
                    457:
                    458:
                    459: int
                    460: pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
                    461: {
                    462: #ifdef ERRORCHECK
                    463:        if ((attr == NULL) ||
                    464:            (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC))
                    465:                return EINVAL;
                    466: #endif
                    467:        attr->ptra_magic = _PT_RWLOCKATTR_DEAD;
                    468:
                    469:        return 0;
                    470: }

CVSweb <webmaster@jp.NetBSD.org>