1 /*        $NetBSD: pthread_mutex.c,v 1.83 2022/04/10 10:38:33 riastradh Exp $   */
2 
3 /*-
4  * Copyright (c) 2001, 2003, 2006, 2007, 2008, 2020 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, by Jason R. Thorpe, and by Andrew Doran.
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  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * To track threads waiting for mutexes to be released, we use lockless
34  * lists built on atomic operations and memory barriers.
35  *
36  * A simple spinlock would be faster and make the code easier to
37  * follow, but spinlocks are problematic in userspace.  If a thread is
38  * preempted by the kernel while holding a spinlock, any other thread
39  * attempting to acquire that spinlock will needlessly busy wait.
40  *
41  * There is no good way to know that the holding thread is no longer
42  * running, nor to request a wake-up once it has begun running again.
43  * Of more concern, threads in the SCHED_FIFO class do not have a
44  * limited time quantum and so could spin forever, preventing the
45  * thread holding the spinlock from getting CPU time: it would never
46  * be released.
47  */
48 
49 #include <sys/cdefs.h>
50 __RCSID("$NetBSD: pthread_mutex.c,v 1.83 2022/04/10 10:38:33 riastradh Exp $");
51 
52 /* Need to use libc-private names for atomic operations. */
53 #include "../../common/lib/libc/atomic/atomic_op_namespace.h"
54 
55 #include <sys/types.h>
56 #include <sys/lwpctl.h>
57 #include <sys/sched.h>
58 #include <sys/lock.h>
59 
60 #include <errno.h>
61 #include <limits.h>
62 #include <stdlib.h>
63 #include <time.h>
64 #include <string.h>
65 #include <stdio.h>
66 
67 #include "pthread.h"
68 #include "pthread_int.h"
69 #include "reentrant.h"
70 
71 #define   MUTEX_RECURSIVE_BIT           ((uintptr_t)0x02)
72 #define   MUTEX_PROTECT_BIT             ((uintptr_t)0x08)
73 #define   MUTEX_THREAD                            ((uintptr_t)~0x0f)
74 
75 #define   MUTEX_RECURSIVE(x)            ((uintptr_t)(x) & MUTEX_RECURSIVE_BIT)
76 #define   MUTEX_PROTECT(x)              ((uintptr_t)(x) & MUTEX_PROTECT_BIT)
77 #define   MUTEX_OWNER(x)                          ((uintptr_t)(x) & MUTEX_THREAD)
78 
79 #define   MUTEX_GET_TYPE(x)             \
80     ((int)(((uintptr_t)(x) & 0x000000ff) >> 0))
81 #define   MUTEX_SET_TYPE(x, t)                    \
82     (x) = (void *)(((uintptr_t)(x) & ~0x000000ff) | ((t) << 0))
83 #define   MUTEX_GET_PROTOCOL(x)                   \
84     ((int)(((uintptr_t)(x) & 0x0000ff00) >> 8))
85 #define   MUTEX_SET_PROTOCOL(x, p)      \
86     (x) = (void *)(((uintptr_t)(x) & ~0x0000ff00) | ((p) << 8))
87 #define   MUTEX_GET_CEILING(x)                    \
88     ((int)(((uintptr_t)(x) & 0x00ff0000) >> 16))
89 #define   MUTEX_SET_CEILING(x, c)       \
90     (x) = (void *)(((uintptr_t)(x) & ~0x00ff0000) | ((c) << 16))
91 
92 #if __GNUC_PREREQ__(3, 0)
93 #define   NOINLINE            __attribute ((noinline))
94 #else
95 #define   NOINLINE            /* nothing */
96 #endif
97 
98 struct waiter {
99           struct waiter       *volatile next;
100           lwpid_t             volatile lid;
101 };
102 
103 static void         pthread__mutex_wakeup(pthread_t, struct pthread__waiter *);
104 static int          pthread__mutex_lock_slow(pthread_mutex_t *,
105     const struct timespec *);
106 static void         pthread__mutex_pause(void);
107 
108 int                 _pthread_mutex_held_np(pthread_mutex_t *);
109 pthread_t _pthread_mutex_owner_np(pthread_mutex_t *);
110 
__weak_alias(pthread_mutex_held_np,_pthread_mutex_held_np)111 __weak_alias(pthread_mutex_held_np,_pthread_mutex_held_np)
112 __weak_alias(pthread_mutex_owner_np,_pthread_mutex_owner_np)
113 
114 __strong_alias(__libc_mutex_init,pthread_mutex_init)
115 __strong_alias(__libc_mutex_lock,pthread_mutex_lock)
116 __strong_alias(__libc_mutex_trylock,pthread_mutex_trylock)
117 __strong_alias(__libc_mutex_unlock,pthread_mutex_unlock)
118 __strong_alias(__libc_mutex_destroy,pthread_mutex_destroy)
119 
120 __strong_alias(__libc_mutexattr_init,pthread_mutexattr_init)
121 __strong_alias(__libc_mutexattr_destroy,pthread_mutexattr_destroy)
122 __strong_alias(__libc_mutexattr_settype,pthread_mutexattr_settype)
123 
124 int
125 pthread_mutex_init(pthread_mutex_t *ptm, const pthread_mutexattr_t *attr)
126 {
127           uintptr_t type, proto, val, ceil;
128 
129 #if 0
130           /*
131            * Always initialize the mutex structure, maybe be used later
132            * and the cost should be minimal.
133            */
134           if (__predict_false(__uselibcstub))
135                     return __libc_mutex_init_stub(ptm, attr);
136 #endif
137 
138           pthread__error(EINVAL, "Invalid mutes attribute",
139               attr == NULL || attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
140 
141           if (attr == NULL) {
142                     type = PTHREAD_MUTEX_NORMAL;
143                     proto = PTHREAD_PRIO_NONE;
144                     ceil = 0;
145           } else {
146                     val = (uintptr_t)attr->ptma_private;
147 
148                     type = MUTEX_GET_TYPE(val);
149                     proto = MUTEX_GET_PROTOCOL(val);
150                     ceil = MUTEX_GET_CEILING(val);
151           }
152           switch (type) {
153           case PTHREAD_MUTEX_ERRORCHECK:
154                     __cpu_simple_lock_set(&ptm->ptm_errorcheck);
155                     ptm->ptm_owner = NULL;
156                     break;
157           case PTHREAD_MUTEX_RECURSIVE:
158                     __cpu_simple_lock_clear(&ptm->ptm_errorcheck);
159                     ptm->ptm_owner = (void *)MUTEX_RECURSIVE_BIT;
160                     break;
161           default:
162                     __cpu_simple_lock_clear(&ptm->ptm_errorcheck);
163                     ptm->ptm_owner = NULL;
164                     break;
165           }
166           switch (proto) {
167           case PTHREAD_PRIO_PROTECT:
168                     val = (uintptr_t)ptm->ptm_owner;
169                     val |= MUTEX_PROTECT_BIT;
170                     ptm->ptm_owner = (void *)val;
171                     break;
172 
173           }
174           ptm->ptm_magic = _PT_MUTEX_MAGIC;
175           ptm->ptm_waiters = NULL;
176           ptm->ptm_recursed = 0;
177           ptm->ptm_ceiling = (unsigned char)ceil;
178 
179           return 0;
180 }
181 
182 int
pthread_mutex_destroy(pthread_mutex_t * ptm)183 pthread_mutex_destroy(pthread_mutex_t *ptm)
184 {
185 
186           if (__predict_false(__uselibcstub))
187                     return __libc_mutex_destroy_stub(ptm);
188 
189           pthread__error(EINVAL, "Invalid mutex",
190               ptm->ptm_magic == _PT_MUTEX_MAGIC);
191           pthread__error(EBUSY, "Destroying locked mutex",
192               MUTEX_OWNER(ptm->ptm_owner) == 0);
193 
194           ptm->ptm_magic = _PT_MUTEX_DEAD;
195           return 0;
196 }
197 
198 int
pthread_mutex_lock(pthread_mutex_t * ptm)199 pthread_mutex_lock(pthread_mutex_t *ptm)
200 {
201           pthread_t self;
202           void *val;
203 
204           if (__predict_false(__uselibcstub))
205                     return __libc_mutex_lock_stub(ptm);
206 
207           pthread__error(EINVAL, "Invalid mutex",
208               ptm->ptm_magic == _PT_MUTEX_MAGIC);
209 
210           self = pthread__self();
211           val = atomic_cas_ptr(&ptm->ptm_owner, NULL, self);
212           if (__predict_true(val == NULL)) {
213 #ifndef PTHREAD__ATOMIC_IS_MEMBAR
214                     membar_enter();
215 #endif
216                     return 0;
217           }
218           return pthread__mutex_lock_slow(ptm, NULL);
219 }
220 
221 int
pthread_mutex_timedlock(pthread_mutex_t * ptm,const struct timespec * ts)222 pthread_mutex_timedlock(pthread_mutex_t* ptm, const struct timespec *ts)
223 {
224           pthread_t self;
225           void *val;
226 
227           pthread__error(EINVAL, "Invalid mutex",
228               ptm->ptm_magic == _PT_MUTEX_MAGIC);
229 
230           self = pthread__self();
231           val = atomic_cas_ptr(&ptm->ptm_owner, NULL, self);
232           if (__predict_true(val == NULL)) {
233 #ifndef PTHREAD__ATOMIC_IS_MEMBAR
234                     membar_enter();
235 #endif
236                     return 0;
237           }
238           return pthread__mutex_lock_slow(ptm, ts);
239 }
240 
241 /* We want function call overhead. */
242 NOINLINE static void
pthread__mutex_pause(void)243 pthread__mutex_pause(void)
244 {
245 
246           pthread__smt_pause();
247 }
248 
249 /*
250  * Spin while the holder is running.  'lwpctl' gives us the true
251  * status of the thread.
252  */
253 NOINLINE static void *
pthread__mutex_spin(pthread_mutex_t * ptm,pthread_t owner)254 pthread__mutex_spin(pthread_mutex_t *ptm, pthread_t owner)
255 {
256           pthread_t thread;
257           unsigned int count, i;
258 
259           for (count = 2;; owner = ptm->ptm_owner) {
260                     thread = (pthread_t)MUTEX_OWNER(owner);
261                     if (thread == NULL)
262                               break;
263                     if (thread->pt_lwpctl->lc_curcpu == LWPCTL_CPU_NONE)
264                               break;
265                     if (count < 128)
266                               count += count;
267                     for (i = count; i != 0; i--)
268                               pthread__mutex_pause();
269           }
270 
271           return owner;
272 }
273 
274 NOINLINE static int
pthread__mutex_lock_slow(pthread_mutex_t * ptm,const struct timespec * ts)275 pthread__mutex_lock_slow(pthread_mutex_t *ptm, const struct timespec *ts)
276 {
277           void *newval, *owner, *next;
278           struct waiter waiter;
279           pthread_t self;
280           int serrno;
281           int error;
282 
283           owner = ptm->ptm_owner;
284           self = pthread__self();
285           serrno = errno;
286 
287           pthread__assert(self->pt_lid != 0);
288 
289           /* Recursive or errorcheck? */
290           if (MUTEX_OWNER(owner) == (uintptr_t)self) {
291                     if (MUTEX_RECURSIVE(owner)) {
292                               if (ptm->ptm_recursed == INT_MAX)
293                                         return EAGAIN;
294                               ptm->ptm_recursed++;
295                               return 0;
296                     }
297                     if (__SIMPLELOCK_LOCKED_P(&ptm->ptm_errorcheck))
298                               return EDEADLK;
299           }
300 
301           /* priority protect */
302           if (MUTEX_PROTECT(owner) && _sched_protect(ptm->ptm_ceiling) == -1) {
303                     error = errno;
304                     errno = serrno;
305                     return error;
306           }
307 
308           for (;;) {
309                     /* If it has become free, try to acquire it again. */
310                     if (MUTEX_OWNER(owner) == 0) {
311                               newval = (void *)((uintptr_t)self | (uintptr_t)owner);
312                               next = atomic_cas_ptr(&ptm->ptm_owner, owner, newval);
313                               if (__predict_false(next != owner)) {
314                                         owner = next;
315                                         continue;
316                               }
317                               errno = serrno;
318 #ifndef PTHREAD__ATOMIC_IS_MEMBAR
319                               membar_enter();
320 #endif
321                               return 0;
322                     } else if (MUTEX_OWNER(owner) != (uintptr_t)self) {
323                               /* Spin while the owner is running. */
324                               owner = pthread__mutex_spin(ptm, owner);
325                               if (MUTEX_OWNER(owner) == 0) {
326                                         continue;
327                               }
328                     }
329 
330                     /*
331                      * Nope, still held.  Add thread to the list of waiters.
332                      * Issue a memory barrier to ensure stores to 'waiter'
333                      * are visible before we enter the list.
334                      */
335                     waiter.next = ptm->ptm_waiters;
336                     waiter.lid = self->pt_lid;
337 #ifndef PTHREAD__ATOMIC_IS_MEMBAR
338                     membar_producer();
339 #endif
340                     next = atomic_cas_ptr(&ptm->ptm_waiters, waiter.next, &waiter);
341                     if (next != waiter.next) {
342                               owner = ptm->ptm_owner;
343                               continue;
344                     }
345 
346                     /*
347                      * If the mutex has become free since entering self onto the
348                      * waiters list, need to wake everybody up (including self)
349                      * and retry.  It's possible to race with an unlocking
350                      * thread, so self may have already been awoken.
351                      */
352 #ifndef PTHREAD__ATOMIC_IS_MEMBAR
353                     membar_enter();
354 #endif
355                     if (MUTEX_OWNER(ptm->ptm_owner) == 0) {
356                               pthread__mutex_wakeup(self,
357                                   atomic_swap_ptr(&ptm->ptm_waiters, NULL));
358                     }
359 
360                     /*
361                      * We must not proceed until told that we are no longer
362                      * waiting (via waiter.lid being set to zero).  Otherwise
363                      * it's unsafe to re-enter "waiter" onto the waiters list.
364                      */
365                     while (waiter.lid != 0) {
366                               error = _lwp_park(CLOCK_REALTIME, TIMER_ABSTIME,
367                                   __UNCONST(ts), 0, NULL, NULL);
368                               if (error < 0 && errno == ETIMEDOUT) {
369                                         /* Remove self from waiters list */
370                                         pthread__mutex_wakeup(self,
371                                             atomic_swap_ptr(&ptm->ptm_waiters, NULL));
372 
373                                         /*
374                                          * Might have raced with another thread to
375                                          * do the wakeup.  In any case there will be
376                                          * a wakeup for sure.  Eat it and wait for
377                                          * waiter.lid to clear.
378                                          */
379                                         while (waiter.lid != 0) {
380                                                   (void)_lwp_park(CLOCK_MONOTONIC, 0,
381                                                       NULL, 0, NULL, NULL);
382                                         }
383 
384                                         /* Priority protect */
385                                         if (MUTEX_PROTECT(owner))
386                                                   (void)_sched_protect(-1);
387                                         errno = serrno;
388                                         return ETIMEDOUT;
389                               }
390                     }
391                     owner = ptm->ptm_owner;
392           }
393 }
394 
395 int
pthread_mutex_trylock(pthread_mutex_t * ptm)396 pthread_mutex_trylock(pthread_mutex_t *ptm)
397 {
398           pthread_t self;
399           void *val, *new, *next;
400 
401           if (__predict_false(__uselibcstub))
402                     return __libc_mutex_trylock_stub(ptm);
403 
404           pthread__error(EINVAL, "Invalid mutex",
405               ptm->ptm_magic == _PT_MUTEX_MAGIC);
406 
407           self = pthread__self();
408           val = atomic_cas_ptr(&ptm->ptm_owner, NULL, self);
409           if (__predict_true(val == NULL)) {
410 #ifndef PTHREAD__ATOMIC_IS_MEMBAR
411                     membar_enter();
412 #endif
413                     return 0;
414           }
415 
416           if (MUTEX_RECURSIVE(val)) {
417                     if (MUTEX_OWNER(val) == 0) {
418                               new = (void *)((uintptr_t)self | (uintptr_t)val);
419                               next = atomic_cas_ptr(&ptm->ptm_owner, val, new);
420                               if (__predict_true(next == val)) {
421 #ifndef PTHREAD__ATOMIC_IS_MEMBAR
422                                         membar_enter();
423 #endif
424                                         return 0;
425                               }
426                     }
427                     if (MUTEX_OWNER(val) == (uintptr_t)self) {
428                               if (ptm->ptm_recursed == INT_MAX)
429                                         return EAGAIN;
430                               ptm->ptm_recursed++;
431                               return 0;
432                     }
433           }
434 
435           return EBUSY;
436 }
437 
438 int
pthread_mutex_unlock(pthread_mutex_t * ptm)439 pthread_mutex_unlock(pthread_mutex_t *ptm)
440 {
441           pthread_t self;
442           void *val, *newval;
443           int error;
444 
445           if (__predict_false(__uselibcstub))
446                     return __libc_mutex_unlock_stub(ptm);
447 
448           pthread__error(EINVAL, "Invalid mutex",
449               ptm->ptm_magic == _PT_MUTEX_MAGIC);
450 
451 #ifndef PTHREAD__ATOMIC_IS_MEMBAR
452           membar_exit();
453 #endif
454           error = 0;
455           self = pthread__self();
456           newval = NULL;
457 
458           val = atomic_cas_ptr(&ptm->ptm_owner, self, newval);
459           if (__predict_false(val != self)) {
460                     bool weown = (MUTEX_OWNER(val) == (uintptr_t)self);
461                     if (__SIMPLELOCK_LOCKED_P(&ptm->ptm_errorcheck)) {
462                               if (!weown) {
463                                         error = EPERM;
464                                         newval = val;
465                               } else {
466                                         newval = NULL;
467                               }
468                     } else if (MUTEX_RECURSIVE(val)) {
469                               if (!weown) {
470                                         error = EPERM;
471                                         newval = val;
472                               } else if (ptm->ptm_recursed) {
473                                         ptm->ptm_recursed--;
474                                         newval = val;
475                               } else {
476                                         newval = (pthread_t)MUTEX_RECURSIVE_BIT;
477                               }
478                     } else {
479                               pthread__error(EPERM,
480                                   "Unlocking unlocked mutex", (val != NULL));
481                               pthread__error(EPERM,
482                                   "Unlocking mutex owned by another thread", weown);
483                               newval = NULL;
484                     }
485 
486                     /*
487                      * Release the mutex.  If there appear to be waiters, then
488                      * wake them up.
489                      */
490                     if (newval != val) {
491                               val = atomic_swap_ptr(&ptm->ptm_owner, newval);
492                               if (__predict_false(MUTEX_PROTECT(val))) {
493                                         /* restore elevated priority */
494                                         (void)_sched_protect(-1);
495                               }
496                     }
497           }
498 
499           /*
500            * Finally, wake any waiters and return.
501            */
502 #ifndef PTHREAD__ATOMIC_IS_MEMBAR
503           membar_enter();
504 #endif
505           if (MUTEX_OWNER(newval) == 0 && ptm->ptm_waiters != NULL) {
506                     pthread__mutex_wakeup(self,
507                         atomic_swap_ptr(&ptm->ptm_waiters, NULL));
508           }
509           return error;
510 }
511 
512 /*
513  * pthread__mutex_wakeup: unpark threads waiting for us
514  */
515 
516 static void
pthread__mutex_wakeup(pthread_t self,struct pthread__waiter * cur)517 pthread__mutex_wakeup(pthread_t self, struct pthread__waiter *cur)
518 {
519           lwpid_t lids[PTHREAD__UNPARK_MAX];
520           const size_t mlid = pthread__unpark_max;
521           struct pthread__waiter *next;
522           size_t nlid;
523 
524           /*
525            * Pull waiters from the queue and add to our list.  Use a memory
526            * barrier to ensure that we safely read the value of waiter->next
527            * before the awoken thread sees waiter->lid being cleared.
528            */
529           membar_datadep_consumer(); /* for alpha */
530           for (nlid = 0; cur != NULL; cur = next) {
531                     if (nlid == mlid) {
532                               (void)_lwp_unpark_all(lids, nlid, NULL);
533                               nlid = 0;
534                     }
535                     next = cur->next;
536                     pthread__assert(cur->lid != 0);
537                     lids[nlid++] = cur->lid;
538                     membar_exit();
539                     cur->lid = 0;
540                     /* No longer safe to touch 'cur' */
541           }
542           if (nlid == 1) {
543                     (void)_lwp_unpark(lids[0], NULL);
544           } else if (nlid > 1) {
545                     (void)_lwp_unpark_all(lids, nlid, NULL);
546           }
547 }
548 
549 int
pthread_mutexattr_init(pthread_mutexattr_t * attr)550 pthread_mutexattr_init(pthread_mutexattr_t *attr)
551 {
552 #if 0
553           if (__predict_false(__uselibcstub))
554                     return __libc_mutexattr_init_stub(attr);
555 #endif
556 
557           attr->ptma_magic = _PT_MUTEXATTR_MAGIC;
558           attr->ptma_private = (void *)PTHREAD_MUTEX_DEFAULT;
559           return 0;
560 }
561 
562 int
pthread_mutexattr_destroy(pthread_mutexattr_t * attr)563 pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
564 {
565           if (__predict_false(__uselibcstub))
566                     return __libc_mutexattr_destroy_stub(attr);
567 
568           pthread__error(EINVAL, "Invalid mutex attribute",
569               attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
570 
571           attr->ptma_magic = _PT_MUTEXATTR_DEAD;
572 
573           return 0;
574 }
575 
576 int
pthread_mutexattr_gettype(const pthread_mutexattr_t * attr,int * typep)577 pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *typep)
578 {
579 
580           pthread__error(EINVAL, "Invalid mutex attribute",
581               attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
582 
583           *typep = MUTEX_GET_TYPE(attr->ptma_private);
584           return 0;
585 }
586 
587 int
pthread_mutexattr_settype(pthread_mutexattr_t * attr,int type)588 pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
589 {
590 
591           if (__predict_false(__uselibcstub))
592                     return __libc_mutexattr_settype_stub(attr, type);
593 
594           pthread__error(EINVAL, "Invalid mutex attribute",
595               attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
596 
597           switch (type) {
598           case PTHREAD_MUTEX_NORMAL:
599           case PTHREAD_MUTEX_ERRORCHECK:
600           case PTHREAD_MUTEX_RECURSIVE:
601                     MUTEX_SET_TYPE(attr->ptma_private, type);
602                     return 0;
603           default:
604                     return EINVAL;
605           }
606 }
607 
608 int
pthread_mutexattr_getprotocol(const pthread_mutexattr_t * attr,int * proto)609 pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr, int*proto)
610 {
611 
612           pthread__error(EINVAL, "Invalid mutex attribute",
613               attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
614 
615           *proto = MUTEX_GET_PROTOCOL(attr->ptma_private);
616           return 0;
617 }
618 
619 int
pthread_mutexattr_setprotocol(pthread_mutexattr_t * attr,int proto)620 pthread_mutexattr_setprotocol(pthread_mutexattr_t* attr, int proto)
621 {
622 
623           pthread__error(EINVAL, "Invalid mutex attribute",
624               attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
625 
626           switch (proto) {
627           case PTHREAD_PRIO_NONE:
628           case PTHREAD_PRIO_PROTECT:
629                     MUTEX_SET_PROTOCOL(attr->ptma_private, proto);
630                     return 0;
631           case PTHREAD_PRIO_INHERIT:
632                     return ENOTSUP;
633           default:
634                     return EINVAL;
635           }
636 }
637 
638 int
pthread_mutexattr_getprioceiling(const pthread_mutexattr_t * attr,int * ceil)639 pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *attr, int *ceil)
640 {
641 
642           pthread__error(EINVAL, "Invalid mutex attribute",
643                     attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
644 
645           *ceil = MUTEX_GET_CEILING(attr->ptma_private);
646           return 0;
647 }
648 
649 int
pthread_mutexattr_setprioceiling(pthread_mutexattr_t * attr,int ceil)650 pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr, int ceil)
651 {
652 
653           pthread__error(EINVAL, "Invalid mutex attribute",
654                     attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
655 
656           if (ceil & ~0xff)
657                     return EINVAL;
658 
659           MUTEX_SET_CEILING(attr->ptma_private, ceil);
660           return 0;
661 }
662 
663 #ifdef _PTHREAD_PSHARED
664 int
pthread_mutexattr_getpshared(const pthread_mutexattr_t * __restrict attr,int * __restrict pshared)665 pthread_mutexattr_getpshared(const pthread_mutexattr_t * __restrict attr,
666     int * __restrict pshared)
667 {
668 
669           pthread__error(EINVAL, "Invalid mutex attribute",
670                     attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
671 
672           *pshared = PTHREAD_PROCESS_PRIVATE;
673           return 0;
674 }
675 
676 int
pthread_mutexattr_setpshared(pthread_mutexattr_t * attr,int pshared)677 pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared)
678 {
679 
680           pthread__error(EINVAL, "Invalid mutex attribute",
681                     attr->ptma_magic == _PT_MUTEXATTR_MAGIC);
682 
683           switch(pshared) {
684           case PTHREAD_PROCESS_PRIVATE:
685                     return 0;
686           case PTHREAD_PROCESS_SHARED:
687                     return ENOSYS;
688           }
689           return EINVAL;
690 }
691 #endif
692 
693 /*
694  * In order to avoid unnecessary contention on interlocking mutexes, we try
695  * to defer waking up threads until we unlock the mutex.  The threads will
696  * be woken up when the calling thread (self) releases the mutex.
697  */
698 void
pthread__mutex_deferwake(pthread_t self,pthread_mutex_t * ptm,struct pthread__waiter * head)699 pthread__mutex_deferwake(pthread_t self, pthread_mutex_t *ptm,
700     struct pthread__waiter *head)
701 {
702           struct pthread__waiter *tail, *n, *o;
703 
704           pthread__assert(head != NULL);
705 
706           if (__predict_false(ptm == NULL ||
707               MUTEX_OWNER(ptm->ptm_owner) != (uintptr_t)self)) {
708                     pthread__mutex_wakeup(self, head);
709                     return;
710           }
711 
712           /* This is easy if no existing waiters on mutex. */
713           if (atomic_cas_ptr(&ptm->ptm_waiters, NULL, head) == NULL) {
714                     return;
715           }
716 
717           /* Oops need to append.  Find the tail of the new queue. */
718           for (tail = head; tail->next != NULL; tail = tail->next) {
719                     /* nothing */
720           }
721 
722           /* Append atomically. */
723           for (o = ptm->ptm_waiters;; o = n) {
724                     tail->next = o;
725 #ifndef PTHREAD__ATOMIC_IS_MEMBAR
726                     membar_producer();
727 #endif
728                     n = atomic_cas_ptr(&ptm->ptm_waiters, o, head);
729                     if (__predict_true(n == o)) {
730                               break;
731                     }
732           }
733 }
734 
735 int
pthread_mutex_getprioceiling(const pthread_mutex_t * ptm,int * ceil)736 pthread_mutex_getprioceiling(const pthread_mutex_t *ptm, int *ceil)
737 {
738 
739           pthread__error(EINVAL, "Invalid mutex",
740               ptm->ptm_magic == _PT_MUTEX_MAGIC);
741 
742           *ceil = ptm->ptm_ceiling;
743           return 0;
744 }
745 
746 int
pthread_mutex_setprioceiling(pthread_mutex_t * ptm,int ceil,int * old_ceil)747 pthread_mutex_setprioceiling(pthread_mutex_t *ptm, int ceil, int *old_ceil)
748 {
749           int error;
750 
751           pthread__error(EINVAL, "Invalid mutex",
752               ptm->ptm_magic == _PT_MUTEX_MAGIC);
753 
754           error = pthread_mutex_lock(ptm);
755           if (error == 0) {
756                     *old_ceil = ptm->ptm_ceiling;
757                     /*check range*/
758                     ptm->ptm_ceiling = ceil;
759                     pthread_mutex_unlock(ptm);
760           }
761           return error;
762 }
763 
764 int
_pthread_mutex_held_np(pthread_mutex_t * ptm)765 _pthread_mutex_held_np(pthread_mutex_t *ptm)
766 {
767 
768           return MUTEX_OWNER(ptm->ptm_owner) == (uintptr_t)pthread__self();
769 }
770 
771 pthread_t
_pthread_mutex_owner_np(pthread_mutex_t * ptm)772 _pthread_mutex_owner_np(pthread_mutex_t *ptm)
773 {
774 
775           return (pthread_t)MUTEX_OWNER(ptm->ptm_owner);
776 }
777