Updated to fedora-glibc-20060328T0900
[kopensolaris-gnu/glibc.git] / nptl / pthread_mutex_timedlock.c
1 /* Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <assert.h>
21 #include <errno.h>
22 #include "pthreadP.h"
23 #include <lowlevellock.h>
24
25
26 int
27 pthread_mutex_timedlock (mutex, abstime)
28      pthread_mutex_t *mutex;
29      const struct timespec *abstime;
30 {
31   int oldval;
32   pid_t id = THREAD_GETMEM (THREAD_SELF, tid);
33   int result = 0;
34
35   /* We must not check ABSTIME here.  If the thread does not block
36      abstime must not be checked for a valid value.  */
37
38   switch (mutex->__data.__kind)
39     {
40       /* Recursive mutex.  */
41     case PTHREAD_MUTEX_RECURSIVE_NP:
42       /* Check whether we already hold the mutex.  */
43       if (mutex->__data.__owner == id)
44         {
45           /* Just bump the counter.  */
46           if (__builtin_expect (mutex->__data.__count + 1 == 0, 0))
47             /* Overflow of the counter.  */
48             return EAGAIN;
49
50           ++mutex->__data.__count;
51
52           goto out;
53         }
54
55       /* We have to get the mutex.  */
56       result = lll_mutex_timedlock (mutex->__data.__lock, abstime);
57
58       if (result != 0)
59         goto out;
60
61       /* Only locked once so far.  */
62       mutex->__data.__count = 1;
63       break;
64
65       /* Error checking mutex.  */
66     case PTHREAD_MUTEX_ERRORCHECK_NP:
67       /* Check whether we already hold the mutex.  */
68       if (mutex->__data.__owner == id)
69         return EDEADLK;
70
71       /* FALLTHROUGH */
72
73     case PTHREAD_MUTEX_TIMED_NP:
74     simple:
75       /* Normal mutex.  */
76       result = lll_mutex_timedlock (mutex->__data.__lock, abstime);
77       break;
78
79     case PTHREAD_MUTEX_ADAPTIVE_NP:
80       if (! __is_smp)
81         goto simple;
82
83       if (lll_mutex_trylock (mutex->__data.__lock) != 0)
84         {
85           int cnt = 0;
86           int max_cnt = MIN (MAX_ADAPTIVE_COUNT,
87                              mutex->__data.__spins * 2 + 10);
88           do
89             {
90               if (cnt++ >= max_cnt)
91                 {
92                   result = lll_mutex_timedlock (mutex->__data.__lock, abstime);
93                   break;
94                 }
95
96 #ifdef BUSY_WAIT_NOP
97               BUSY_WAIT_NOP;
98 #endif
99             }
100           while (lll_mutex_trylock (mutex->__data.__lock) != 0);
101
102           mutex->__data.__spins += (cnt - mutex->__data.__spins) / 8;
103         }
104       break;
105
106     case PTHREAD_MUTEX_ROBUST_RECURSIVE_NP:
107     case PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP:
108     case PTHREAD_MUTEX_ROBUST_NORMAL_NP:
109     case PTHREAD_MUTEX_ROBUST_ADAPTIVE_NP:
110       THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
111                      &mutex->__data.__list.__next);
112
113       oldval = mutex->__data.__lock;
114       do
115         {
116         again:
117           if ((oldval & FUTEX_OWNER_DIED) != 0)
118             {
119               /* The previous owner died.  Try locking the mutex.  */
120               int newval
121                 = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
122                                                        id, oldval);
123               if (newval != oldval)
124                 {
125                   oldval = newval;
126                   goto again;
127                 }
128
129               /* We got the mutex.  */
130               mutex->__data.__count = 1;
131               /* But it is inconsistent unless marked otherwise.  */
132               mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
133
134               ENQUEUE_MUTEX (mutex);
135               THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
136
137               /* Note that we deliberately exist here.  If we fall
138                  through to the end of the function __nusers would be
139                  incremented which is not correct because the old
140                  owner has to be discounted.  */
141               return EOWNERDEAD;
142             }
143
144           /* Check whether we already hold the mutex.  */
145           if (__builtin_expect ((oldval & FUTEX_TID_MASK) == id, 0))
146             {
147               if (mutex->__data.__kind
148                   == PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP)
149                 {
150                   THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
151                                  NULL);
152                   return EDEADLK;
153                 }
154
155               if (mutex->__data.__kind
156                   == PTHREAD_MUTEX_ROBUST_RECURSIVE_NP)
157                 {
158                   THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
159                                  NULL);
160
161                   /* Just bump the counter.  */
162                   if (__builtin_expect (mutex->__data.__count + 1 == 0, 0))
163                     /* Overflow of the counter.  */
164                     return EAGAIN;
165
166                   ++mutex->__data.__count;
167
168                   return 0;
169                 }
170             }
171
172           result = lll_robust_mutex_timedlock (mutex->__data.__lock, abstime,
173                                                id);
174
175           if (__builtin_expect (mutex->__data.__owner
176                                 == PTHREAD_MUTEX_NOTRECOVERABLE, 0))
177             {
178               /* This mutex is now not recoverable.  */
179               mutex->__data.__count = 0;
180               lll_mutex_unlock (mutex->__data.__lock);
181               THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
182               return ENOTRECOVERABLE;
183             }
184
185           if (result == ETIMEDOUT || result == EINVAL)
186             goto out;
187
188           oldval = result;
189         }
190       while ((oldval & FUTEX_OWNER_DIED) != 0);
191
192       mutex->__data.__count = 1;
193       ENQUEUE_MUTEX (mutex);
194       THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
195       break;
196
197     default:
198       /* Correct code cannot set any other type.  */
199       return EINVAL;
200     }
201
202   if (result == 0)
203     {
204       /* Record the ownership.  */
205       mutex->__data.__owner = id;
206       ++mutex->__data.__nusers;
207     }
208
209  out:
210   return result;
211 }