df11933b69713f2f298e78cf1c1fed1b0e6bf64f
[platform/upstream/glibc.git] / nptl / sem_wait.c
1 /* sem_wait -- wait on a semaphore.  Generic futex-using version.
2    Copyright (C) 2003-2015 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Paul Mackerras <paulus@au.ibm.com>, 2003.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, see
18    <http://www.gnu.org/licenses/>.  */
19
20 #include <errno.h>
21 #include <sysdep.h>
22 #include <lowlevellock.h>
23 #include <internaltypes.h>
24 #include <semaphore.h>
25
26 #include <pthreadP.h>
27 #include <shlib-compat.h>
28 #include <atomic.h>
29
30
31 void
32 attribute_hidden
33 __sem_wait_cleanup (void *arg)
34 {
35   struct new_sem *isem = (struct new_sem *) arg;
36
37   atomic_decrement (&isem->nwaiters);
38 }
39
40 /* This is in a seperate function in order to make sure gcc
41    puts the call site into an exception region, and thus the
42    cleanups get properly run.  */
43 static int
44 __attribute__ ((noinline))
45 do_futex_wait (struct new_sem *isem)
46 {
47   int err, oldtype = __pthread_enable_asynccancel ();
48
49   err = lll_futex_wait (&isem->value, 0, isem->private ^ FUTEX_PRIVATE_FLAG);
50
51   __pthread_disable_asynccancel (oldtype);
52   return err;
53 }
54
55 int
56 __new_sem_wait (sem_t *sem)
57 {
58   struct new_sem *isem = (struct new_sem *) sem;
59   int err;
60
61   if (atomic_decrement_if_positive (&isem->value) > 0)
62     return 0;
63
64   atomic_increment (&isem->nwaiters);
65
66   pthread_cleanup_push (__sem_wait_cleanup, isem);
67
68   while (1)
69     {
70       err = do_futex_wait(isem);
71       if (err != 0 && err != -EWOULDBLOCK)
72         {
73           __set_errno (-err);
74           err = -1;
75           break;
76         }
77
78       if (atomic_decrement_if_positive (&isem->value) > 0)
79         {
80           err = 0;
81           break;
82         }
83     }
84
85   pthread_cleanup_pop (0);
86
87   atomic_decrement (&isem->nwaiters);
88
89   return err;
90 }
91 versioned_symbol (libpthread, __new_sem_wait, sem_wait, GLIBC_2_1);
92
93
94 #if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_1)
95 int
96 attribute_compat_text_section
97 __old_sem_wait (sem_t *sem)
98 {
99   int *futex = (int *) sem;
100   int err;
101
102   do
103     {
104       if (atomic_decrement_if_positive (futex) > 0)
105         return 0;
106
107       /* Enable asynchronous cancellation.  Required by the standard.  */
108       int oldtype = __pthread_enable_asynccancel ();
109
110       /* Always assume the semaphore is shared.  */
111       err = lll_futex_wait (futex, 0, LLL_SHARED);
112
113       /* Disable asynchronous cancellation.  */
114       __pthread_disable_asynccancel (oldtype);
115     }
116   while (err == 0 || err == -EWOULDBLOCK);
117
118   __set_errno (-err);
119   return -1;
120 }
121
122 compat_symbol (libpthread, __old_sem_wait, sem_wait, GLIBC_2_0);
123 #endif