2 * Copyright (c) 2000-2003, 2007, 2008 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 * Copyright (c) 1998 Alex Nash
25 * All rights reserved.
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
36 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
37 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
40 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
42 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * $FreeBSD: src/lib/libc_r/uthread/uthread_rwlock.c,v 1.6 2001/04/10 04:19:20 deischen Exp $
52 * POSIX Pthread Library
53 * -- Read Write Lock support
59 #include <stdio.h> /* For printf(). */
61 extern int __unix_conforming
;
64 #include "plockstat.h"
65 #else /* !PLOCKSTAT */
66 #define PLOCKSTAT_RW_ERROR(x, y, z)
67 #define PLOCKSTAT_RW_BLOCK(x, y)
68 #define PLOCKSTAT_RW_BLOCKED(x, y, z)
69 #define PLOCKSTAT_RW_ACQUIRE(x, y)
70 #define PLOCKSTAT_RW_RELEASE(x, y)
71 #endif /* PLOCKSTAT */
73 #define READ_LOCK_PLOCKSTAT 0
74 #define WRITE_LOCK_PLOCKSTAT 1
76 #define BLOCK_FAIL_PLOCKSTAT 0
77 #define BLOCK_SUCCESS_PLOCKSTAT 1
79 /* maximum number of times a read lock may be obtained */
80 #define MAX_READ_LOCKS (INT_MAX - 1)
82 #include <platform/string.h>
83 #include <platform/compat.h>
85 __private_extern__
int __pthread_rwlock_init(_pthread_rwlock
*rwlock
, const pthread_rwlockattr_t
*attr
);
86 __private_extern__
void _pthread_rwlock_updateval(_pthread_rwlock
*rwlock
, uint32_t updateval
);
89 RWLOCK_GETSEQ_ADDR(_pthread_rwlock
*rwlock
,
90 volatile uint32_t **lcntaddr
,
91 volatile uint32_t **ucntaddr
,
92 volatile uint32_t **seqaddr
)
94 if (rwlock
->pshared
== PTHREAD_PROCESS_SHARED
) {
95 if (rwlock
->misalign
) {
96 *lcntaddr
= &rwlock
->rw_seq
[1];
97 *seqaddr
= &rwlock
->rw_seq
[2];
98 *ucntaddr
= &rwlock
->rw_seq
[3];
100 *lcntaddr
= &rwlock
->rw_seq
[0];
101 *seqaddr
= &rwlock
->rw_seq
[1];
102 *ucntaddr
= &rwlock
->rw_seq
[2];
105 *lcntaddr
= rwlock
->rw_lcntaddr
;
106 *seqaddr
= rwlock
->rw_seqaddr
;
107 *ucntaddr
= rwlock
->rw_ucntaddr
;
111 #ifndef BUILDING_VARIANT /* [ */
112 static uint32_t modbits(uint32_t lgenval
, uint32_t updateval
, uint32_t savebits
);
115 pthread_rwlockattr_init(pthread_rwlockattr_t
*attr
)
117 attr
->sig
= _PTHREAD_RWLOCK_ATTR_SIG
;
118 attr
->pshared
= _PTHREAD_DEFAULT_PSHARED
;
123 pthread_rwlockattr_destroy(pthread_rwlockattr_t
*attr
)
125 attr
->sig
= _PTHREAD_NO_SIG
;
131 pthread_rwlockattr_getpshared(const pthread_rwlockattr_t
*attr
, int *pshared
)
134 if (attr
->sig
== _PTHREAD_RWLOCK_ATTR_SIG
) {
135 *pshared
= (int)attr
->pshared
;
142 pthread_rwlockattr_setpshared(pthread_rwlockattr_t
* attr
, int pshared
)
145 if (attr
->sig
== _PTHREAD_RWLOCK_ATTR_SIG
) {
147 if (( pshared
== PTHREAD_PROCESS_PRIVATE
) || (pshared
== PTHREAD_PROCESS_SHARED
))
148 #else /* __DARWIN_UNIX03 */
149 if ( pshared
== PTHREAD_PROCESS_PRIVATE
)
150 #endif /* __DARWIN_UNIX03 */
152 attr
->pshared
= pshared
;
159 __private_extern__
int
160 __pthread_rwlock_init(_pthread_rwlock
*rwlock
, const pthread_rwlockattr_t
*attr
)
162 // Force RWLOCK_GETSEQ_ADDR to calculate addresses by setting pshared.
163 rwlock
->pshared
= PTHREAD_PROCESS_SHARED
;
164 rwlock
->misalign
= (((uintptr_t)&rwlock
->rw_seq
[0]) & 0x7) != 0;
165 RWLOCK_GETSEQ_ADDR(rwlock
, &rwlock
->rw_lcntaddr
, &rwlock
->rw_ucntaddr
, &rwlock
->rw_seqaddr
);
166 *rwlock
->rw_lcntaddr
= PTHRW_RWLOCK_INIT
;
167 *rwlock
->rw_seqaddr
= PTHRW_RWS_INIT
;
168 *rwlock
->rw_ucntaddr
= 0;
170 if (attr
!= NULL
&& attr
->pshared
== PTHREAD_PROCESS_SHARED
) {
171 rwlock
->pshared
= PTHREAD_PROCESS_SHARED
;
172 rwlock
->rw_flags
= PTHRW_KERN_PROCESS_SHARED
;
174 rwlock
->pshared
= _PTHREAD_DEFAULT_PSHARED
;
175 rwlock
->rw_flags
= PTHRW_KERN_PROCESS_PRIVATE
;
178 rwlock
->rw_owner
= NULL
;
179 bzero(rwlock
->_reserved
, sizeof(rwlock
->_reserved
));
181 // Ensure all contents are properly set before setting signature.
183 rwlock
->sig
= _PTHREAD_RWLOCK_SIG
;
189 modbits(uint32_t lgenval
, uint32_t updateval
, uint32_t savebits
)
191 uint32_t lval
= lgenval
& PTHRW_BIT_MASK
;
192 uint32_t uval
= updateval
& PTHRW_BIT_MASK
;
193 uint32_t rval
, nlval
;
195 nlval
= (lval
| uval
) & ~(PTH_RWL_MBIT
);
197 /* reconcile bits on the lock with what kernel needs to set */
198 if ((uval
& PTH_RWL_KBIT
) == 0 && (lval
& PTH_RWL_WBIT
) == 0) {
199 nlval
&= ~PTH_RWL_KBIT
;
203 if ((savebits
& PTH_RWS_WSVBIT
) != 0 && (nlval
& PTH_RWL_WBIT
) == 0 && (nlval
& PTH_RWL_EBIT
) == 0) {
204 nlval
|= (PTH_RWL_WBIT
| PTH_RWL_KBIT
);
207 rval
= (lgenval
& PTHRW_COUNT_MASK
) | nlval
;
211 __private_extern__
void
212 _pthread_rwlock_updateval(_pthread_rwlock
*rwlock
, uint32_t updateval
)
214 bool isoverlap
= (updateval
& PTH_RWL_MBIT
) != 0;
216 uint64_t oldval64
, newval64
;
217 volatile uint32_t *lcntaddr
, *ucntaddr
, *seqaddr
;
219 /* TBD: restore U bit */
220 RWLOCK_GETSEQ_ADDR(rwlock
, &lcntaddr
, &ucntaddr
, &seqaddr
);
223 uint32_t lcntval
= *lcntaddr
;
224 uint32_t rw_seq
= *seqaddr
;
226 uint32_t newval
, newsval
;
227 if (isoverlap
|| is_rws_setunlockinit(rw_seq
) != 0) {
228 // Set S word to the specified value
229 uint32_t savebits
= (rw_seq
& PTHRW_RWS_SAVEMASK
);
230 newval
= modbits(lcntval
, updateval
, savebits
);
231 newsval
= rw_seq
+ (updateval
& PTHRW_COUNT_MASK
);
233 newsval
&= PTHRW_COUNT_MASK
;
235 newsval
&= ~PTHRW_RWS_SAVEMASK
;
241 oldval64
= (((uint64_t)rw_seq
) << 32);
243 newval64
= (((uint64_t)newsval
) << 32);
245 } while (OSAtomicCompareAndSwap64Barrier(oldval64
, newval64
, (volatile int64_t *)lcntaddr
) != TRUE
);
248 #endif /* !BUILDING_VARIANT ] */
251 _pthread_rwlock_check_busy(_pthread_rwlock
*rwlock
)
255 volatile uint32_t *lcntaddr
, *ucntaddr
, *seqaddr
;
257 RWLOCK_GETSEQ_ADDR(rwlock
, &lcntaddr
, &ucntaddr
, &seqaddr
);
259 uint32_t rw_lcnt
= *lcntaddr
;
260 uint32_t rw_ucnt
= *ucntaddr
;
262 if ((rw_lcnt
& PTHRW_COUNT_MASK
) != rw_ucnt
) {
270 pthread_rwlock_destroy(pthread_rwlock_t
*orwlock
)
273 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
275 if (rwlock
->sig
== _PTHREAD_RWLOCK_SIG
) {
277 res
= _pthread_rwlock_check_busy(rwlock
);
278 #endif /* __DARWIN_UNIX03 */
279 } else if (rwlock
->sig
!= _PTHREAD_RWLOCK_SIG_init
) {
283 rwlock
->sig
= _PTHREAD_NO_SIG
;
290 pthread_rwlock_init(pthread_rwlock_t
*orwlock
, const pthread_rwlockattr_t
*attr
)
293 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
296 if (attr
&& attr
->sig
!= _PTHREAD_RWLOCK_ATTR_SIG
) {
300 if (res
== 0 && rwlock
->sig
== _PTHREAD_RWLOCK_SIG
) {
301 res
= _pthread_rwlock_check_busy(rwlock
);
305 LOCK_INIT(rwlock
->lock
);
306 res
= __pthread_rwlock_init(rwlock
, attr
);
313 _pthread_rwlock_check_init_slow(pthread_rwlock_t
*orwlock
)
316 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
318 if (rwlock
->sig
== _PTHREAD_RWLOCK_SIG_init
) {
320 if (rwlock
->sig
== _PTHREAD_RWLOCK_SIG_init
) {
321 res
= __pthread_rwlock_init(rwlock
, NULL
);
322 } else if (rwlock
->sig
== _PTHREAD_RWLOCK_SIG
){
325 UNLOCK(rwlock
->lock
);
326 } else if (rwlock
->sig
== _PTHREAD_RWLOCK_SIG
){
330 PLOCKSTAT_RW_ERROR(orwlock
, READ_LOCK_PLOCKSTAT
, res
);
335 PTHREAD_ALWAYS_INLINE
337 _pthread_rwlock_check_init(pthread_rwlock_t
*orwlock
)
340 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
342 if (rwlock
->sig
!= _PTHREAD_RWLOCK_SIG
) {
343 return _pthread_rwlock_check_init_slow(orwlock
);
349 _pthread_rwlock_lock(pthread_rwlock_t
*orwlock
, bool readlock
, bool trylock
)
352 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
354 res
= _pthread_rwlock_check_init(orwlock
);
359 uint64_t oldval64
, newval64
;
360 volatile uint32_t *lcntaddr
, *ucntaddr
, *seqaddr
;
361 RWLOCK_GETSEQ_ADDR(rwlock
, &lcntaddr
, &ucntaddr
, &seqaddr
);
363 uint32_t newval
, newsval
;
364 uint32_t lcntval
, ucntval
, rw_seq
;
379 if (is_rwl_ebit_set(lcntval
)) {
380 if (rwlock
->rw_owner
== pthread_self()) {
385 #endif /* __DARWIN_UNIX03 */
387 oldval64
= (((uint64_t)rw_seq
) << 32);
390 /* if l bit is on or u and k bit is clear, acquire lock in userland */
392 gotlock
= can_rwl_readinuser(lcntval
);
394 gotlock
= (lcntval
& PTH_RWL_RBIT
) != 0;
398 uint32_t mask
= ~0ul;
400 newval
= lcntval
+ PTHRW_INC
;
404 if (diff_genseq(lcntval
, ucntval
) >= PTHRW_MAX_READERS
) {
405 /* since ucntval may be newer, just redo */
407 if (retry_count
> 1024) {
417 // Need to update L (remove R bit) and S word
418 mask
= PTH_RWLOCK_RESET_RBIT
;
420 mask
= PTHRW_COUNT_MASK
;
421 bits
= PTH_RWL_IBIT
| PTH_RWL_KBIT
| PTH_RWL_EBIT
;
423 newsval
= rw_seq
+ PTHRW_INC
;
424 } else if (trylock
) {
429 // Need to block in kernel. Remove R bit.
430 mask
= PTH_RWLOCK_RESET_RBIT
;
432 bits
= PTH_RWL_KBIT
| PTH_RWL_WBIT
;
435 if (is_rws_setseq(rw_seq
)) {
436 newsval
&= PTHRW_SW_Reset_BIT_MASK
;
437 newsval
|= (newval
& PTHRW_COUNT_MASK
);
440 newval
= (newval
& mask
) | bits
;
441 newval64
= (((uint64_t)newsval
) << 32);
444 } while (retry
|| OSAtomicCompareAndSwap64Barrier(oldval64
, newval64
, (volatile int64_t *)lcntaddr
) != TRUE
);
447 int plockstat
= readlock
? READ_LOCK_PLOCKSTAT
: WRITE_LOCK_PLOCKSTAT
;
450 // Unable to acquire in userland, transition to kernel.
451 if (res
== 0 && !gotlock
) {
454 PLOCKSTAT_RW_BLOCK(orwlock
, plockstat
);
458 updateval
= __psynch_rw_rdlock(orwlock
, newval
, ucntval
, newsval
, rwlock
->rw_flags
);
460 updateval
= __psynch_rw_wrlock(orwlock
, newval
, ucntval
, newsval
, rwlock
->rw_flags
);
462 if (updateval
== (uint32_t)-1) {
467 } while (res
== EINTR
);
470 _pthread_rwlock_updateval(rwlock
, updateval
);
471 PLOCKSTAT_RW_BLOCKED(orwlock
, plockstat
, BLOCK_SUCCESS_PLOCKSTAT
);
473 PLOCKSTAT_RW_BLOCKED(orwlock
, plockstat
, BLOCK_FAIL_PLOCKSTAT
);
475 (void)pthread_threadid_np(pthread_self(), &myid
);
476 PTHREAD_ABORT("kernel lock returned unknown error %x with tid %x\n", updateval
, (uint32_t)myid
);
483 rwlock
->rw_owner
= pthread_self();
485 #endif /* __DARWIN_UNIX03 */
486 PLOCKSTAT_RW_ACQUIRE(orwlock
, plockstat
);
488 PLOCKSTAT_RW_ERROR(orwlock
, plockstat
, res
);
495 pthread_rwlock_rdlock(pthread_rwlock_t
*orwlock
)
498 return _pthread_rwlock_lock(orwlock
, true, false);
502 pthread_rwlock_tryrdlock(pthread_rwlock_t
*orwlock
)
504 // read lock, try lock
505 return _pthread_rwlock_lock(orwlock
, true, true);
509 pthread_rwlock_wrlock(pthread_rwlock_t
*orwlock
)
511 // write lock, no try
512 return _pthread_rwlock_lock(orwlock
, false, false);
516 pthread_rwlock_trywrlock(pthread_rwlock_t
*orwlock
)
518 // write lock, try lock
519 return _pthread_rwlock_lock(orwlock
, false, true);
523 pthread_rwlock_unlock(pthread_rwlock_t
*orwlock
)
526 _pthread_rwlock
*rwlock
= (_pthread_rwlock
*)orwlock
;
531 res
= _pthread_rwlock_check_init(orwlock
);
536 uint64_t oldval64
= 0, newval64
= 0;
537 volatile uint32_t *lcntaddr
, *ucntaddr
, *seqaddr
;
538 RWLOCK_GETSEQ_ADDR(rwlock
, &lcntaddr
, &ucntaddr
, &seqaddr
);
542 bool incr_ucnt
= true;
543 bool check_spurious
= true;
544 uint32_t lcntval
, ucntval
, rw_seq
, ulval
= 0, newval
, newsval
;
554 oldval64
= (((uint64_t)rw_seq
) << 32);
557 // check for spurious unlocks
558 if (check_spurious
) {
559 if ((lcntval
& PTH_RWL_RBIT
) != 0) {
565 check_spurious
= false;
568 if (is_rwl_ebit_set(lcntval
)) {
573 rwlock
->rw_owner
= NULL
;
574 #endif /* __DARWIN_UNIX03 */
579 ulval
= (ucntval
+ PTHRW_INC
);
580 incr_ucnt
= (OSAtomicCompareAndSwap32Barrier(ucntval
, ulval
, (volatile int32_t *)ucntaddr
) != TRUE
);
586 // last unlock, note U is already updated ?
587 if ((lcntval
& PTHRW_COUNT_MASK
) == (ulval
& PTHRW_COUNT_MASK
)) {
588 /* Set L with R and init bits and set S to L */
589 newval
= (lcntval
& PTHRW_COUNT_MASK
)| PTHRW_RWLOCK_INIT
;
590 newsval
= (lcntval
& PTHRW_COUNT_MASK
)| PTHRW_RWS_INIT
;
594 /* if it is not exclusive or no Writer/yield pending, skip */
595 if ((lcntval
& (PTH_RWL_EBIT
| PTH_RWL_WBIT
| PTH_RWL_KBIT
)) == 0) {
600 /* kernel transition needed? */
602 if ((ulval
+ PTHRW_INC
) != (rw_seq
& PTHRW_COUNT_MASK
)) {
607 /* reset all bits and set k */
608 newval
= (lcntval
& PTHRW_COUNT_MASK
) | PTH_RWL_KBIT
;
609 /* set I bit on S word */
610 newsval
= rw_seq
| PTH_RWS_IBIT
;
611 if ((lcntval
& PTH_RWL_WBIT
) != 0) {
612 newsval
|= PTH_RWS_WSVBIT
;
616 newval64
= (((uint64_t)newsval
) << 32);
619 } while (OSAtomicCompareAndSwap64Barrier(oldval64
, newval64
, (volatile int64_t *)lcntaddr
) != TRUE
|| reload
);
624 updateval
= __psynch_rw_unlock(orwlock
, lcntval
, ulval
, newsval
, rwlock
->rw_flags
);
625 if (updateval
== (uint32_t)-1) {
630 } while (res
== EINTR
);
634 (void)pthread_threadid_np(pthread_self(), &myid
);
635 PTHREAD_ABORT("rwunlock from kernel with unknown error %x: tid %x\n", res
, (uint32_t)myid
);
639 PLOCKSTAT_RW_RELEASE(orwlock
, wrlock
);