Optimize mutexes to wake up only one thread.
[kopensolaris-gnu/glibc.git] / linuxthreads / mutex.c
1 /* Linuxthreads - a simple clone()-based implementation of Posix        */
2 /* threads for Linux.                                                   */
3 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
4 /*                                                                      */
5 /* This program is free software; you can redistribute it and/or        */
6 /* modify it under the terms of the GNU Library General Public License  */
7 /* as published by the Free Software Foundation; either version 2       */
8 /* of the License, or (at your option) any later version.               */
9 /*                                                                      */
10 /* This program 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        */
13 /* GNU Library General Public License for more details.                 */
14
15 /* Mutexes */
16
17 #include <errno.h>
18 #include <sched.h>
19 #include <stddef.h>
20 #include "pthread.h"
21 #include "internals.h"
22 #include "spinlock.h"
23 #include "queue.h"
24 #include "restart.h"
25
26 int __pthread_mutex_init(pthread_mutex_t * mutex,
27                        const pthread_mutexattr_t * mutex_attr)
28 {
29   mutex->m_spinlock = 0;
30   mutex->m_count = 0;
31   mutex->m_owner = NULL;
32   mutex->m_kind =
33     mutex_attr == NULL ? PTHREAD_MUTEX_FAST_NP : mutex_attr->mutexkind;
34   queue_init(&mutex->m_waiting);
35   return 0;
36 }
37 weak_alias (__pthread_mutex_init, pthread_mutex_init)
38
39 int __pthread_mutex_destroy(pthread_mutex_t * mutex)
40 {
41   int count;
42   acquire(&mutex->m_spinlock);
43   count = mutex->m_count;
44   release(&mutex->m_spinlock);
45   if (count > 0) return EBUSY;
46   return 0;
47 }
48 weak_alias (__pthread_mutex_destroy, pthread_mutex_destroy)
49
50 int __pthread_mutex_trylock(pthread_mutex_t * mutex)
51 {
52   pthread_descr self;
53
54   acquire(&mutex->m_spinlock);
55   switch(mutex->m_kind) {
56   case PTHREAD_MUTEX_FAST_NP:
57     if (mutex->m_count == 0) {
58       mutex->m_count = 1;
59       release(&mutex->m_spinlock);
60       return 0;
61     }
62     break;
63   case PTHREAD_MUTEX_RECURSIVE_NP:
64     self = thread_self();
65     if (mutex->m_count == 0 || mutex->m_owner == self) {
66       mutex->m_count++;
67       mutex->m_owner = self;
68       release(&mutex->m_spinlock);
69       return 0;
70     }
71     break;
72   case PTHREAD_MUTEX_ERRORCHECK_NP:
73     self = thread_self();
74     if (mutex->m_count == 0) {
75       mutex->m_count = 1;
76       mutex->m_owner = self;
77       release(&mutex->m_spinlock);
78       return 0;
79     }
80     break;
81   default:
82     release(&mutex->m_spinlock);
83     return EINVAL;
84   }
85   release(&mutex->m_spinlock);
86   return EBUSY;
87 }
88 weak_alias (__pthread_mutex_trylock, pthread_mutex_trylock)
89
90 int __pthread_mutex_lock(pthread_mutex_t * mutex)
91 {
92   pthread_descr self;
93
94   acquire(&mutex->m_spinlock);
95   switch(mutex->m_kind) {
96   case PTHREAD_MUTEX_FAST_NP:
97     if (mutex->m_count == 0) {
98       mutex->m_count = 1;
99       release(&mutex->m_spinlock);
100       return 0;
101     }
102     self = thread_self();
103     break;
104   case PTHREAD_MUTEX_RECURSIVE_NP:
105     self = thread_self();
106     if (mutex->m_count == 0 || mutex->m_owner == self) {
107       mutex->m_count++;
108       mutex->m_owner = self;
109       release(&mutex->m_spinlock);
110       return 0;
111     }
112     break;
113   case PTHREAD_MUTEX_ERRORCHECK_NP:
114     self = thread_self();
115     if (mutex->m_count == 0) {
116       mutex->m_count = 1;
117       mutex->m_owner = self;
118       release(&mutex->m_spinlock);
119       return 0;
120     } else if (mutex->m_owner == self) {
121       release(&mutex->m_spinlock);
122       return EDEADLK;
123     }
124     break;
125   default:
126     release(&mutex->m_spinlock);
127     return EINVAL;
128   }
129   /* Suspend ourselves */
130   enqueue(&mutex->m_waiting, self);
131   release(&mutex->m_spinlock);
132   suspend(self); /* This is not a cancellation point */
133   /* Now we own the mutex */
134   ASSERT(mutex->m_count == 1);
135   mutex->m_owner = self;        /* for recursive and errorcheck mutexes */
136   return 0;
137 }
138 weak_alias (__pthread_mutex_lock, pthread_mutex_lock)
139
140 int __pthread_mutex_unlock(pthread_mutex_t * mutex)
141 {
142   pthread_descr th;
143
144   acquire(&mutex->m_spinlock);
145   switch (mutex->m_kind) {
146   case PTHREAD_MUTEX_FAST_NP:
147     break;
148   case PTHREAD_MUTEX_RECURSIVE_NP:
149     if (mutex->m_count >= 2) {
150       mutex->m_count--;
151       release(&mutex->m_spinlock);
152       return 0;
153     }
154     break;
155   case PTHREAD_MUTEX_ERRORCHECK_NP:
156     if (mutex->m_count == 0 || mutex->m_owner != thread_self()) {
157       release(&mutex->m_spinlock);
158       return EPERM;
159     }
160     break;
161   default:
162     release(&mutex->m_spinlock);
163     return EINVAL;
164   }
165   th = dequeue(&mutex->m_waiting);
166   /* If no waiters, unlock the mutex */
167   if (th == NULL) mutex->m_count = 0;
168   release(&mutex->m_spinlock);
169   /* If there is a waiter, restart it with the mutex still locked */
170   if (th != NULL) {
171     mutex->m_owner = NULL;      /* we no longer own the mutex */
172     restart(th);
173   }
174   return 0;
175 }
176 weak_alias (__pthread_mutex_unlock, pthread_mutex_unlock)
177
178 int __pthread_mutexattr_init(pthread_mutexattr_t *attr)
179 {
180   attr->mutexkind = PTHREAD_MUTEX_FAST_NP;
181   return 0;
182 }
183 weak_alias (__pthread_mutexattr_init, pthread_mutexattr_init)
184
185 int __pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
186 {
187   return 0;
188 }
189 weak_alias (__pthread_mutexattr_destroy, pthread_mutexattr_destroy)
190
191 int __pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind)
192 {
193   if (kind != PTHREAD_MUTEX_FAST_NP
194       && kind != PTHREAD_MUTEX_RECURSIVE_NP
195       && kind != PTHREAD_MUTEX_ERRORCHECK_NP)
196     return EINVAL;
197   attr->mutexkind = kind;
198   return 0;
199 }
200 weak_alias (__pthread_mutexattr_setkind_np, pthread_mutexattr_setkind_np)
201
202 int __pthread_mutexattr_getkind_np(const pthread_mutexattr_t *attr, int *kind)
203 {
204   *kind = attr->mutexkind;
205   return 0;
206 }
207 weak_alias (__pthread_mutexattr_getkind_np, pthread_mutexattr_getkind_np)
208
209 /* Once-only execution */
210
211 static pthread_mutex_t once_masterlock = PTHREAD_MUTEX_INITIALIZER;
212 static pthread_cond_t once_finished = PTHREAD_COND_INITIALIZER;
213
214 enum { NEVER = 0, IN_PROGRESS = 1, DONE = 2 };
215
216 int __pthread_once(pthread_once_t * once_control, void (*init_routine)(void))
217 {
218   /* Test without locking first for speed */
219   if (*once_control == DONE) return 0;
220   /* Lock and test again */
221   pthread_mutex_lock(&once_masterlock);
222   /* If init_routine is being called from another routine, wait until
223      it completes. */
224   while (*once_control == IN_PROGRESS) {
225     pthread_cond_wait(&once_finished, &once_masterlock);
226   }
227   /* Here *once_control is stable and either NEVER or DONE. */
228   if (*once_control == NEVER) {
229     *once_control = IN_PROGRESS;
230     pthread_mutex_unlock(&once_masterlock);
231     init_routine();
232     pthread_mutex_lock(&once_masterlock);
233     *once_control = DONE;
234     pthread_cond_broadcast(&once_finished);
235   }
236   pthread_mutex_unlock(&once_masterlock);
237   return 0;
238 }
239 weak_alias (__pthread_once, pthread_once)