import less(1)
[unleashed/tickless.git] / usr / src / lib / libc / port / threads / rwlock.c
blob9654b7daaf1ff5bcef2e6b7d522d470a7b2fa811
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
27 #include "lint.h"
28 #include "thr_uberdata.h"
29 #include <sys/sdt.h>
31 #define TRY_FLAG 0x10
32 #define READ_LOCK 0
33 #define WRITE_LOCK 1
34 #define READ_LOCK_TRY (READ_LOCK | TRY_FLAG)
35 #define WRITE_LOCK_TRY (WRITE_LOCK | TRY_FLAG)
37 #define NLOCKS 4 /* initial number of readlock_t structs allocated */
39 #define ASSERT_CONSISTENT_STATE(readers) \
40 ASSERT(!((readers) & URW_WRITE_LOCKED) || \
41 ((readers) & ~URW_HAS_WAITERS) == URW_WRITE_LOCKED)
44 * Find/allocate an entry for rwlp in our array of rwlocks held for reading.
45 * We must be deferring signals for this to be safe.
46 * Else if we are returning an entry with ul_rdlockcnt == 0,
47 * it could be reassigned behind our back in a signal handler.
49 static readlock_t *
50 rwl_entry(rwlock_t *rwlp)
52 ulwp_t *self = curthread;
53 readlock_t *remembered = NULL;
54 readlock_t *readlockp;
55 uint_t nlocks;
57 /* we must be deferring signals */
58 ASSERT((self->ul_critical + self->ul_sigdefer) != 0);
60 if ((nlocks = self->ul_rdlockcnt) != 0)
61 readlockp = self->ul_readlock.array;
62 else {
63 nlocks = 1;
64 readlockp = &self->ul_readlock.single;
67 for (; nlocks; nlocks--, readlockp++) {
68 if (readlockp->rd_rwlock == rwlp)
69 return (readlockp);
70 if (readlockp->rd_count == 0 && remembered == NULL)
71 remembered = readlockp;
73 if (remembered != NULL) {
74 remembered->rd_rwlock = rwlp;
75 return (remembered);
79 * No entry available. Allocate more space, converting the single
80 * readlock_t entry into an array of readlock_t entries if necessary.
82 if ((nlocks = self->ul_rdlockcnt) == 0) {
84 * Initial allocation of the readlock_t array.
85 * Convert the single entry into an array.
87 self->ul_rdlockcnt = nlocks = NLOCKS;
88 readlockp = lmalloc(nlocks * sizeof (readlock_t));
90 * The single readlock_t becomes the first entry in the array.
92 *readlockp = self->ul_readlock.single;
93 self->ul_readlock.single.rd_count = 0;
94 self->ul_readlock.array = readlockp;
96 * Return the next available entry in the array.
98 (++readlockp)->rd_rwlock = rwlp;
99 return (readlockp);
102 * Reallocate the array, double the size each time.
104 readlockp = lmalloc(nlocks * 2 * sizeof (readlock_t));
105 (void) memcpy(readlockp, self->ul_readlock.array,
106 nlocks * sizeof (readlock_t));
107 lfree(self->ul_readlock.array, nlocks * sizeof (readlock_t));
108 self->ul_readlock.array = readlockp;
109 self->ul_rdlockcnt *= 2;
111 * Return the next available entry in the newly allocated array.
113 (readlockp += nlocks)->rd_rwlock = rwlp;
114 return (readlockp);
118 * Free the array of rwlocks held for reading.
120 void
121 rwl_free(ulwp_t *ulwp)
123 uint_t nlocks;
125 if ((nlocks = ulwp->ul_rdlockcnt) != 0)
126 lfree(ulwp->ul_readlock.array, nlocks * sizeof (readlock_t));
127 ulwp->ul_rdlockcnt = 0;
128 ulwp->ul_readlock.single.rd_rwlock = NULL;
129 ulwp->ul_readlock.single.rd_count = 0;
133 * Check if a reader version of the lock is held by the current thread.
135 #pragma weak _rw_read_held = rw_read_held
137 rw_read_held(rwlock_t *rwlp)
139 volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
140 uint32_t readers;
141 ulwp_t *self = curthread;
142 readlock_t *readlockp;
143 uint_t nlocks;
144 int rval = 0;
146 no_preempt(self);
148 readers = *rwstate;
149 ASSERT_CONSISTENT_STATE(readers);
150 if (!(readers & URW_WRITE_LOCKED) &&
151 (readers & URW_READERS_MASK) != 0) {
153 * The lock is held for reading by some thread.
154 * Search our array of rwlocks held for reading for a match.
156 if ((nlocks = self->ul_rdlockcnt) != 0)
157 readlockp = self->ul_readlock.array;
158 else {
159 nlocks = 1;
160 readlockp = &self->ul_readlock.single;
162 for (; nlocks; nlocks--, readlockp++) {
163 if (readlockp->rd_rwlock == rwlp) {
164 if (readlockp->rd_count)
165 rval = 1;
166 break;
171 preempt(self);
172 return (rval);
176 * Check if a writer version of the lock is held by the current thread.
178 #pragma weak _rw_write_held = rw_write_held
180 rw_write_held(rwlock_t *rwlp)
182 volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
183 uint32_t readers;
184 ulwp_t *self = curthread;
185 int rval;
187 no_preempt(self);
189 readers = *rwstate;
190 ASSERT_CONSISTENT_STATE(readers);
191 rval = ((readers & URW_WRITE_LOCKED) &&
192 rwlp->rwlock_owner == (uintptr_t)self &&
193 (rwlp->rwlock_type == USYNC_THREAD ||
194 rwlp->rwlock_ownerpid == self->ul_uberdata->pid));
196 preempt(self);
197 return (rval);
200 #pragma weak _rwlock_init = rwlock_init
201 /* ARGSUSED2 */
203 rwlock_init(rwlock_t *rwlp, int type, void *arg)
205 ulwp_t *self = curthread;
207 if (type != USYNC_THREAD && type != USYNC_PROCESS)
208 return (EINVAL);
210 * Once reinitialized, we can no longer be holding a read or write lock.
211 * We can do nothing about other threads that are holding read locks.
213 sigoff(self);
214 rwl_entry(rwlp)->rd_count = 0;
215 sigon(self);
216 (void) memset(rwlp, 0, sizeof (*rwlp));
217 rwlp->rwlock_type = (uint16_t)type;
218 rwlp->rwlock_magic = RWL_MAGIC;
219 rwlp->mutex.mutex_type = (uint8_t)type;
220 rwlp->mutex.mutex_flag = LOCK_INITED;
221 rwlp->mutex.mutex_magic = MUTEX_MAGIC;
224 * This should be at the beginning of the function,
225 * but for the sake of old broken applications that
226 * do not have proper alignment for their rwlocks
227 * (and don't check the return code from rwlock_init),
228 * we put it here, after initializing the rwlock regardless.
230 if (((uintptr_t)rwlp & (_LONG_LONG_ALIGNMENT - 1)) &&
231 self->ul_misaligned == 0)
232 return (EINVAL);
234 return (0);
237 #pragma weak pthread_rwlock_destroy = rwlock_destroy
238 #pragma weak _rwlock_destroy = rwlock_destroy
240 rwlock_destroy(rwlock_t *rwlp)
242 ulwp_t *self = curthread;
245 * Once destroyed, we can no longer be holding a read or write lock.
246 * We can do nothing about other threads that are holding read locks.
248 sigoff(self);
249 rwl_entry(rwlp)->rd_count = 0;
250 sigon(self);
251 rwlp->rwlock_magic = 0;
252 tdb_sync_obj_deregister(rwlp);
253 return (0);
257 * The following four functions:
258 * read_lock_try()
259 * read_unlock_try()
260 * write_lock_try()
261 * write_unlock_try()
262 * lie at the heart of the fast-path code for rwlocks,
263 * both process-private and process-shared.
265 * They are called once without recourse to any other locking primitives.
266 * If they succeed, we are done and the fast-path code was successful.
267 * If they fail, we have to deal with lock queues, either to enqueue
268 * ourself and sleep or to dequeue and wake up someone else (slow paths).
270 * Unless 'ignore_waiters_flag' is true (a condition that applies only
271 * when read_lock_try() or write_lock_try() is called from code that
272 * is already in the slow path and has already acquired the queue lock),
273 * these functions will always fail if the waiters flag, URW_HAS_WAITERS,
274 * is set in the 'rwstate' word. Thus, setting the waiters flag on the
275 * rwlock and acquiring the queue lock guarantees exclusive access to
276 * the rwlock (and is the only way to guarantee exclusive access).
280 * Attempt to acquire a readers lock. Return true on success.
282 static int
283 read_lock_try(rwlock_t *rwlp, int ignore_waiters_flag)
285 volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
286 uint32_t mask = ignore_waiters_flag?
287 URW_WRITE_LOCKED : (URW_HAS_WAITERS | URW_WRITE_LOCKED);
288 uint32_t readers;
289 ulwp_t *self = curthread;
291 no_preempt(self);
292 while (((readers = *rwstate) & mask) == 0) {
293 if (atomic_cas_32(rwstate, readers, readers + 1) == readers) {
294 preempt(self);
295 return (1);
298 preempt(self);
299 return (0);
303 * Attempt to release a reader lock. Return true on success.
305 static int
306 read_unlock_try(rwlock_t *rwlp)
308 volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
309 uint32_t readers;
310 ulwp_t *self = curthread;
312 no_preempt(self);
313 while (((readers = *rwstate) & URW_HAS_WAITERS) == 0) {
314 if (atomic_cas_32(rwstate, readers, readers - 1) == readers) {
315 preempt(self);
316 return (1);
319 preempt(self);
320 return (0);
324 * Attempt to acquire a writer lock. Return true on success.
326 static int
327 write_lock_try(rwlock_t *rwlp, int ignore_waiters_flag)
329 volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
330 uint32_t mask = ignore_waiters_flag?
331 (URW_WRITE_LOCKED | URW_READERS_MASK) :
332 (URW_HAS_WAITERS | URW_WRITE_LOCKED | URW_READERS_MASK);
333 ulwp_t *self = curthread;
334 uint32_t readers;
336 no_preempt(self);
337 while (((readers = *rwstate) & mask) == 0) {
338 if (atomic_cas_32(rwstate, readers, readers | URW_WRITE_LOCKED)
339 == readers) {
340 preempt(self);
341 return (1);
344 preempt(self);
345 return (0);
349 * Attempt to release a writer lock. Return true on success.
351 static int
352 write_unlock_try(rwlock_t *rwlp)
354 volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
355 uint32_t readers;
356 ulwp_t *self = curthread;
358 no_preempt(self);
359 while (((readers = *rwstate) & URW_HAS_WAITERS) == 0) {
360 if (atomic_cas_32(rwstate, readers, 0) == readers) {
361 preempt(self);
362 return (1);
365 preempt(self);
366 return (0);
370 * Release a process-private rwlock and wake up any thread(s) sleeping on it.
371 * This is called when a thread releases a lock that appears to have waiters.
373 static void
374 rw_queue_release(rwlock_t *rwlp)
376 volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
377 queue_head_t *qp;
378 uint32_t readers;
379 uint32_t writer;
380 ulwp_t **ulwpp;
381 ulwp_t *ulwp;
382 ulwp_t *prev;
383 int nlwpid = 0;
384 int more;
385 int maxlwps = MAXLWPS;
386 lwpid_t buffer[MAXLWPS];
387 lwpid_t *lwpid = buffer;
389 qp = queue_lock(rwlp, MX);
392 * Here is where we actually drop the lock,
393 * but we retain the URW_HAS_WAITERS flag, if it is already set.
395 readers = *rwstate;
396 ASSERT_CONSISTENT_STATE(readers);
397 if (readers & URW_WRITE_LOCKED) /* drop the writer lock */
398 atomic_and_32(rwstate, ~URW_WRITE_LOCKED);
399 else /* drop the readers lock */
400 atomic_dec_32(rwstate);
401 if (!(readers & URW_HAS_WAITERS)) { /* no waiters */
402 queue_unlock(qp);
403 return;
407 * The presence of the URW_HAS_WAITERS flag causes all rwlock
408 * code to go through the slow path, acquiring queue_lock(qp).
409 * Therefore, the rest of this code is safe because we are
410 * holding the queue lock and the URW_HAS_WAITERS flag is set.
413 readers = *rwstate; /* must fetch the value again */
414 ASSERT_CONSISTENT_STATE(readers);
415 ASSERT(readers & URW_HAS_WAITERS);
416 readers &= URW_READERS_MASK; /* count of current readers */
417 writer = 0; /* no current writer */
420 * Examine the queue of waiters in priority order and prepare
421 * to wake up as many readers as we encounter before encountering
422 * a writer. If the highest priority thread on the queue is a
423 * writer, stop there and wake it up.
425 * We keep track of lwpids that are to be unparked in lwpid[].
426 * __lwp_unpark_all() is called to unpark all of them after
427 * they have been removed from the sleep queue and the sleep
428 * queue lock has been dropped. If we run out of space in our
429 * on-stack buffer, we need to allocate more but we can't call
430 * lmalloc() because we are holding a queue lock when the overflow
431 * occurs and lmalloc() acquires a lock. We can't use alloca()
432 * either because the application may have allocated a small
433 * stack and we don't want to overrun the stack. So we call
434 * alloc_lwpids() to allocate a bigger buffer using the mmap()
435 * system call directly since that path acquires no locks.
437 while ((ulwpp = queue_slot(qp, &prev, &more)) != NULL) {
438 ulwp = *ulwpp;
439 ASSERT(ulwp->ul_wchan == rwlp);
440 if (ulwp->ul_writer) {
441 if (writer != 0 || readers != 0)
442 break;
443 /* one writer to wake */
444 writer++;
445 } else {
446 if (writer != 0)
447 break;
448 /* at least one reader to wake */
449 readers++;
450 if (nlwpid == maxlwps)
451 lwpid = alloc_lwpids(lwpid, &nlwpid, &maxlwps);
453 queue_unlink(qp, ulwpp, prev);
454 ulwp->ul_sleepq = NULL;
455 ulwp->ul_wchan = NULL;
456 if (writer) {
458 * Hand off the lock to the writer we will be waking.
460 ASSERT((*rwstate & ~URW_HAS_WAITERS) == 0);
461 atomic_or_32(rwstate, URW_WRITE_LOCKED);
462 rwlp->rwlock_owner = (uintptr_t)ulwp;
464 lwpid[nlwpid++] = ulwp->ul_lwpid;
468 * This modification of rwstate must be done last.
469 * The presence of the URW_HAS_WAITERS flag causes all rwlock
470 * code to go through the slow path, acquiring queue_lock(qp).
471 * Otherwise the read_lock_try() and write_lock_try() fast paths
472 * are effective.
474 if (ulwpp == NULL)
475 atomic_and_32(rwstate, ~URW_HAS_WAITERS);
477 if (nlwpid == 0) {
478 queue_unlock(qp);
479 } else {
480 ulwp_t *self = curthread;
481 no_preempt(self);
482 queue_unlock(qp);
483 if (nlwpid == 1)
484 (void) __lwp_unpark(lwpid[0]);
485 else
486 (void) __lwp_unpark_all(lwpid, nlwpid);
487 preempt(self);
489 if (lwpid != buffer)
490 (void) munmap((caddr_t)lwpid, maxlwps * sizeof (lwpid_t));
494 * Common code for rdlock, timedrdlock, wrlock, timedwrlock, tryrdlock,
495 * and trywrlock for process-shared (USYNC_PROCESS) rwlocks.
497 * Note: if the lock appears to be contended we call __lwp_rwlock_rdlock()
498 * or __lwp_rwlock_wrlock() holding the mutex. These return with the mutex
499 * released, and if they need to sleep will release the mutex first. In the
500 * event of a spurious wakeup, these will return EAGAIN (because it is much
501 * easier for us to re-acquire the mutex here).
504 shared_rwlock_lock(rwlock_t *rwlp, timespec_t *tsp, int rd_wr)
506 volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
507 mutex_t *mp = &rwlp->mutex;
508 uint32_t readers;
509 int try_flag;
510 int error;
512 try_flag = (rd_wr & TRY_FLAG);
513 rd_wr &= ~TRY_FLAG;
514 ASSERT(rd_wr == READ_LOCK || rd_wr == WRITE_LOCK);
516 if (!try_flag) {
517 DTRACE_PROBE2(plockstat, rw__block, rwlp, rd_wr);
520 do {
521 if (try_flag && (*rwstate & URW_WRITE_LOCKED)) {
522 error = EBUSY;
523 break;
525 if ((error = mutex_lock(mp)) != 0)
526 break;
527 if (rd_wr == READ_LOCK) {
528 if (read_lock_try(rwlp, 0)) {
529 (void) mutex_unlock(mp);
530 break;
532 } else {
533 if (write_lock_try(rwlp, 0)) {
534 (void) mutex_unlock(mp);
535 break;
538 atomic_or_32(rwstate, URW_HAS_WAITERS);
539 readers = *rwstate;
540 ASSERT_CONSISTENT_STATE(readers);
542 * The calls to __lwp_rwlock_*() below will release the mutex,
543 * so we need a dtrace probe here. The owner field of the
544 * mutex is cleared in the kernel when the mutex is released,
545 * so we should not clear it here.
547 DTRACE_PROBE2(plockstat, mutex__release, mp, 0);
549 * The waiters bit may be inaccurate.
550 * Only the kernel knows for sure.
552 if (rd_wr == READ_LOCK) {
553 if (try_flag)
554 error = __lwp_rwlock_tryrdlock(rwlp);
555 else
556 error = __lwp_rwlock_rdlock(rwlp, tsp);
557 } else {
558 if (try_flag)
559 error = __lwp_rwlock_trywrlock(rwlp);
560 else
561 error = __lwp_rwlock_wrlock(rwlp, tsp);
563 } while (error == EAGAIN || error == EINTR);
565 if (!try_flag) {
566 DTRACE_PROBE3(plockstat, rw__blocked, rwlp, rd_wr, error == 0);
569 return (error);
573 * Common code for rdlock, timedrdlock, wrlock, timedwrlock, tryrdlock,
574 * and trywrlock for process-private (USYNC_THREAD) rwlocks.
577 rwlock_lock(rwlock_t *rwlp, timespec_t *tsp, int rd_wr)
579 volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
580 uint32_t readers;
581 ulwp_t *self = curthread;
582 queue_head_t *qp;
583 ulwp_t *ulwp;
584 int try_flag;
585 int ignore_waiters_flag;
586 int error = 0;
588 try_flag = (rd_wr & TRY_FLAG);
589 rd_wr &= ~TRY_FLAG;
590 ASSERT(rd_wr == READ_LOCK || rd_wr == WRITE_LOCK);
592 if (!try_flag) {
593 DTRACE_PROBE2(plockstat, rw__block, rwlp, rd_wr);
596 qp = queue_lock(rwlp, MX);
597 /* initial attempt to acquire the lock fails if there are waiters */
598 ignore_waiters_flag = 0;
599 while (error == 0) {
600 if (rd_wr == READ_LOCK) {
601 if (read_lock_try(rwlp, ignore_waiters_flag))
602 break;
603 } else {
604 if (write_lock_try(rwlp, ignore_waiters_flag))
605 break;
607 /* subsequent attempts do not fail due to waiters */
608 ignore_waiters_flag = 1;
609 atomic_or_32(rwstate, URW_HAS_WAITERS);
610 readers = *rwstate;
611 ASSERT_CONSISTENT_STATE(readers);
612 if ((readers & URW_WRITE_LOCKED) ||
613 (rd_wr == WRITE_LOCK &&
614 (readers & URW_READERS_MASK) != 0))
615 /* EMPTY */; /* somebody holds the lock */
616 else if ((ulwp = queue_waiter(qp)) == NULL) {
617 atomic_and_32(rwstate, ~URW_HAS_WAITERS);
618 ignore_waiters_flag = 0;
619 continue; /* no queued waiters, start over */
620 } else {
622 * Do a priority check on the queued waiter (the
623 * highest priority thread on the queue) to see
624 * if we should defer to it or just grab the lock.
626 int our_pri = real_priority(self);
627 int his_pri = real_priority(ulwp);
629 if (rd_wr == WRITE_LOCK) {
631 * We defer to a queued thread that has
632 * a higher priority than ours.
634 if (his_pri <= our_pri) {
636 * Don't defer, just grab the lock.
638 continue;
640 } else {
642 * We defer to a queued thread that has
643 * a higher priority than ours or that
644 * is a writer whose priority equals ours.
646 if (his_pri < our_pri ||
647 (his_pri == our_pri && !ulwp->ul_writer)) {
649 * Don't defer, just grab the lock.
651 continue;
656 * We are about to block.
657 * If we're doing a trylock, return EBUSY instead.
659 if (try_flag) {
660 error = EBUSY;
661 break;
664 * Enqueue writers ahead of readers.
666 self->ul_writer = rd_wr; /* *must* be 0 or 1 */
667 enqueue(qp, self, 0);
668 set_parking_flag(self, 1);
669 queue_unlock(qp);
670 if ((error = __lwp_park(tsp, 0)) == EINTR)
671 error = 0;
672 set_parking_flag(self, 0);
673 qp = queue_lock(rwlp, MX);
674 if (self->ul_sleepq && dequeue_self(qp) == 0) {
675 atomic_and_32(rwstate, ~URW_HAS_WAITERS);
676 ignore_waiters_flag = 0;
678 self->ul_writer = 0;
679 if (rd_wr == WRITE_LOCK &&
680 (*rwstate & URW_WRITE_LOCKED) &&
681 rwlp->rwlock_owner == (uintptr_t)self) {
683 * We acquired the lock by hand-off
684 * from the previous owner,
686 error = 0; /* timedlock did not fail */
687 break;
692 * Make one final check to see if there are any threads left
693 * on the rwlock queue. Clear the URW_HAS_WAITERS flag if not.
695 if (qp->qh_root == NULL || qp->qh_root->qr_head == NULL)
696 atomic_and_32(rwstate, ~URW_HAS_WAITERS);
698 queue_unlock(qp);
700 if (!try_flag) {
701 DTRACE_PROBE3(plockstat, rw__blocked, rwlp, rd_wr, error == 0);
704 return (error);
708 rw_rdlock_impl(rwlock_t *rwlp, timespec_t *tsp)
710 ulwp_t *self = curthread;
711 uberdata_t *udp = self->ul_uberdata;
712 readlock_t *readlockp;
713 tdb_rwlock_stats_t *rwsp = RWLOCK_STATS(rwlp, udp);
714 int error;
717 * If we already hold a readers lock on this rwlock,
718 * just increment our reference count and return.
720 sigoff(self);
721 readlockp = rwl_entry(rwlp);
722 if (readlockp->rd_count != 0) {
723 if (readlockp->rd_count == READ_LOCK_MAX) {
724 sigon(self);
725 error = EAGAIN;
726 goto out;
728 sigon(self);
729 error = 0;
730 goto out;
732 sigon(self);
735 * If we hold the writer lock, bail out.
737 if (rw_write_held(rwlp)) {
738 if (self->ul_error_detection)
739 rwlock_error(rwlp, "rwlock_rdlock",
740 "calling thread owns the writer lock");
741 error = EDEADLK;
742 goto out;
745 if (read_lock_try(rwlp, 0))
746 error = 0;
747 else if (rwlp->rwlock_type == USYNC_PROCESS) /* kernel-level */
748 error = shared_rwlock_lock(rwlp, tsp, READ_LOCK);
749 else /* user-level */
750 error = rwlock_lock(rwlp, tsp, READ_LOCK);
752 out:
753 if (error == 0) {
754 sigoff(self);
755 rwl_entry(rwlp)->rd_count++;
756 sigon(self);
757 if (rwsp)
758 tdb_incr(rwsp->rw_rdlock);
759 DTRACE_PROBE2(plockstat, rw__acquire, rwlp, READ_LOCK);
760 } else {
761 DTRACE_PROBE3(plockstat, rw__error, rwlp, READ_LOCK, error);
764 return (error);
767 #pragma weak pthread_rwlock_rdlock = rw_rdlock
768 #pragma weak _rw_rdlock = rw_rdlock
770 rw_rdlock(rwlock_t *rwlp)
772 ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
773 return (rw_rdlock_impl(rwlp, NULL));
776 void
777 lrw_rdlock(rwlock_t *rwlp)
779 enter_critical(curthread);
780 (void) rw_rdlock_impl(rwlp, NULL);
784 pthread_rwlock_reltimedrdlock_np(pthread_rwlock_t *_RESTRICT_KYWD rwlp,
785 const struct timespec *_RESTRICT_KYWD reltime)
787 timespec_t tslocal = *reltime;
788 int error;
790 ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
791 error = rw_rdlock_impl((rwlock_t *)rwlp, &tslocal);
792 if (error == ETIME)
793 error = ETIMEDOUT;
794 return (error);
798 pthread_rwlock_timedrdlock(pthread_rwlock_t *_RESTRICT_KYWD rwlp,
799 const struct timespec *_RESTRICT_KYWD abstime)
801 timespec_t tslocal;
802 int error;
804 ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
805 abstime_to_reltime(CLOCK_REALTIME, abstime, &tslocal);
806 error = rw_rdlock_impl((rwlock_t *)rwlp, &tslocal);
807 if (error == ETIME)
808 error = ETIMEDOUT;
809 return (error);
813 rw_wrlock_impl(rwlock_t *rwlp, timespec_t *tsp)
815 ulwp_t *self = curthread;
816 uberdata_t *udp = self->ul_uberdata;
817 tdb_rwlock_stats_t *rwsp = RWLOCK_STATS(rwlp, udp);
818 int error;
821 * If we hold a readers lock on this rwlock, bail out.
823 if (rw_read_held(rwlp)) {
824 if (self->ul_error_detection)
825 rwlock_error(rwlp, "rwlock_wrlock",
826 "calling thread owns the readers lock");
827 error = EDEADLK;
828 goto out;
832 * If we hold the writer lock, bail out.
834 if (rw_write_held(rwlp)) {
835 if (self->ul_error_detection)
836 rwlock_error(rwlp, "rwlock_wrlock",
837 "calling thread owns the writer lock");
838 error = EDEADLK;
839 goto out;
842 if (write_lock_try(rwlp, 0))
843 error = 0;
844 else if (rwlp->rwlock_type == USYNC_PROCESS) /* kernel-level */
845 error = shared_rwlock_lock(rwlp, tsp, WRITE_LOCK);
846 else /* user-level */
847 error = rwlock_lock(rwlp, tsp, WRITE_LOCK);
849 out:
850 if (error == 0) {
851 rwlp->rwlock_owner = (uintptr_t)self;
852 if (rwlp->rwlock_type == USYNC_PROCESS)
853 rwlp->rwlock_ownerpid = udp->pid;
854 if (rwsp) {
855 tdb_incr(rwsp->rw_wrlock);
856 rwsp->rw_wrlock_begin_hold = gethrtime();
858 DTRACE_PROBE2(plockstat, rw__acquire, rwlp, WRITE_LOCK);
859 } else {
860 DTRACE_PROBE3(plockstat, rw__error, rwlp, WRITE_LOCK, error);
862 return (error);
865 #pragma weak pthread_rwlock_wrlock = rw_wrlock
866 #pragma weak _rw_wrlock = rw_wrlock
868 rw_wrlock(rwlock_t *rwlp)
870 ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
871 return (rw_wrlock_impl(rwlp, NULL));
874 void
875 lrw_wrlock(rwlock_t *rwlp)
877 enter_critical(curthread);
878 (void) rw_wrlock_impl(rwlp, NULL);
882 pthread_rwlock_reltimedwrlock_np(pthread_rwlock_t *_RESTRICT_KYWD rwlp,
883 const struct timespec *_RESTRICT_KYWD reltime)
885 timespec_t tslocal = *reltime;
886 int error;
888 ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
889 error = rw_wrlock_impl((rwlock_t *)rwlp, &tslocal);
890 if (error == ETIME)
891 error = ETIMEDOUT;
892 return (error);
896 pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlp, const timespec_t *abstime)
898 timespec_t tslocal;
899 int error;
901 ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
902 abstime_to_reltime(CLOCK_REALTIME, abstime, &tslocal);
903 error = rw_wrlock_impl((rwlock_t *)rwlp, &tslocal);
904 if (error == ETIME)
905 error = ETIMEDOUT;
906 return (error);
909 #pragma weak pthread_rwlock_tryrdlock = rw_tryrdlock
911 rw_tryrdlock(rwlock_t *rwlp)
913 ulwp_t *self = curthread;
914 uberdata_t *udp = self->ul_uberdata;
915 tdb_rwlock_stats_t *rwsp = RWLOCK_STATS(rwlp, udp);
916 readlock_t *readlockp;
917 int error;
919 ASSERT(!curthread->ul_critical || curthread->ul_bindflags);
921 if (rwsp)
922 tdb_incr(rwsp->rw_rdlock_try);
925 * If we already hold a readers lock on this rwlock,
926 * just increment our reference count and return.
928 sigoff(self);
929 readlockp = rwl_entry(rwlp);
930 if (readlockp->rd_count != 0) {
931 if (readlockp->rd_count == READ_LOCK_MAX) {
932 sigon(self);
933 error = EAGAIN;
934 goto out;
936 sigon(self);
937 error = 0;
938 goto out;
940 sigon(self);
942 if (read_lock_try(rwlp, 0))
943 error = 0;
944 else if (rwlp->rwlock_type == USYNC_PROCESS) /* kernel-level */
945 error = shared_rwlock_lock(rwlp, NULL, READ_LOCK_TRY);
946 else /* user-level */
947 error = rwlock_lock(rwlp, NULL, READ_LOCK_TRY);
949 out:
950 if (error == 0) {
951 sigoff(self);
952 rwl_entry(rwlp)->rd_count++;
953 sigon(self);
954 DTRACE_PROBE2(plockstat, rw__acquire, rwlp, READ_LOCK);
955 } else {
956 if (rwsp)
957 tdb_incr(rwsp->rw_rdlock_try_fail);
958 if (error != EBUSY) {
959 DTRACE_PROBE3(plockstat, rw__error, rwlp, READ_LOCK,
960 error);
964 return (error);
967 #pragma weak pthread_rwlock_trywrlock = rw_trywrlock
969 rw_trywrlock(rwlock_t *rwlp)
971 ulwp_t *self = curthread;
972 uberdata_t *udp = self->ul_uberdata;
973 tdb_rwlock_stats_t *rwsp = RWLOCK_STATS(rwlp, udp);
974 int error;
976 ASSERT(!self->ul_critical || self->ul_bindflags);
978 if (rwsp)
979 tdb_incr(rwsp->rw_wrlock_try);
981 if (write_lock_try(rwlp, 0))
982 error = 0;
983 else if (rwlp->rwlock_type == USYNC_PROCESS) /* kernel-level */
984 error = shared_rwlock_lock(rwlp, NULL, WRITE_LOCK_TRY);
985 else /* user-level */
986 error = rwlock_lock(rwlp, NULL, WRITE_LOCK_TRY);
988 if (error == 0) {
989 rwlp->rwlock_owner = (uintptr_t)self;
990 if (rwlp->rwlock_type == USYNC_PROCESS)
991 rwlp->rwlock_ownerpid = udp->pid;
992 if (rwsp)
993 rwsp->rw_wrlock_begin_hold = gethrtime();
994 DTRACE_PROBE2(plockstat, rw__acquire, rwlp, WRITE_LOCK);
995 } else {
996 if (rwsp)
997 tdb_incr(rwsp->rw_wrlock_try_fail);
998 if (error != EBUSY) {
999 DTRACE_PROBE3(plockstat, rw__error, rwlp, WRITE_LOCK,
1000 error);
1003 return (error);
1006 #pragma weak pthread_rwlock_unlock = rw_unlock
1007 #pragma weak _rw_unlock = rw_unlock
1009 rw_unlock(rwlock_t *rwlp)
1011 volatile uint32_t *rwstate = (volatile uint32_t *)&rwlp->rwlock_readers;
1012 uint32_t readers;
1013 ulwp_t *self = curthread;
1014 uberdata_t *udp = self->ul_uberdata;
1015 tdb_rwlock_stats_t *rwsp;
1016 int rd_wr;
1018 readers = *rwstate;
1019 ASSERT_CONSISTENT_STATE(readers);
1020 if (readers & URW_WRITE_LOCKED) {
1021 rd_wr = WRITE_LOCK;
1022 readers = 0;
1023 } else {
1024 rd_wr = READ_LOCK;
1025 readers &= URW_READERS_MASK;
1028 if (rd_wr == WRITE_LOCK) {
1030 * Since the writer lock is held, we'd better be
1031 * holding it, else we cannot legitimately be here.
1033 if (!rw_write_held(rwlp)) {
1034 if (self->ul_error_detection)
1035 rwlock_error(rwlp, "rwlock_unlock",
1036 "writer lock held, "
1037 "but not by the calling thread");
1038 return (EPERM);
1040 if ((rwsp = RWLOCK_STATS(rwlp, udp)) != NULL) {
1041 if (rwsp->rw_wrlock_begin_hold)
1042 rwsp->rw_wrlock_hold_time +=
1043 gethrtime() - rwsp->rw_wrlock_begin_hold;
1044 rwsp->rw_wrlock_begin_hold = 0;
1046 rwlp->rwlock_owner = 0;
1047 rwlp->rwlock_ownerpid = 0;
1048 } else if (readers > 0) {
1050 * A readers lock is held; if we don't hold one, bail out.
1052 readlock_t *readlockp;
1054 sigoff(self);
1055 readlockp = rwl_entry(rwlp);
1056 if (readlockp->rd_count == 0) {
1057 sigon(self);
1058 if (self->ul_error_detection)
1059 rwlock_error(rwlp, "rwlock_unlock",
1060 "readers lock held, "
1061 "but not by the calling thread");
1062 return (EPERM);
1065 * If we hold more than one readers lock on this rwlock,
1066 * just decrement our reference count and return.
1068 if (--readlockp->rd_count != 0) {
1069 sigon(self);
1070 goto out;
1072 sigon(self);
1073 } else {
1075 * This is a usage error.
1076 * No thread should release an unowned lock.
1078 if (self->ul_error_detection)
1079 rwlock_error(rwlp, "rwlock_unlock", "lock not owned");
1080 return (EPERM);
1083 if (rd_wr == WRITE_LOCK && write_unlock_try(rwlp)) {
1084 /* EMPTY */;
1085 } else if (rd_wr == READ_LOCK && read_unlock_try(rwlp)) {
1086 /* EMPTY */;
1087 } else if (rwlp->rwlock_type == USYNC_PROCESS) {
1088 (void) mutex_lock(&rwlp->mutex);
1089 (void) __lwp_rwlock_unlock(rwlp);
1090 (void) mutex_unlock(&rwlp->mutex);
1091 } else {
1092 rw_queue_release(rwlp);
1095 out:
1096 DTRACE_PROBE2(plockstat, rw__release, rwlp, rd_wr);
1097 return (0);
1100 void
1101 lrw_unlock(rwlock_t *rwlp)
1103 (void) rw_unlock(rwlp);
1104 exit_critical(curthread);