LinuxThreads library.
[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   while(1) {
95     acquire(&mutex->m_spinlock);
96     switch(mutex->m_kind) {
97     case PTHREAD_MUTEX_FAST_NP:
98       if (mutex->m_count == 0) {
99         mutex->m_count = 1;
100         release(&mutex->m_spinlock);
101         return 0;
102       }
103       self = thread_self();
104       break;
105     case PTHREAD_MUTEX_RECURSIVE_NP:
106       self = thread_self();
107       if (mutex->m_count == 0 || mutex->m_owner == self) {
108         mutex->m_count++;
109         mutex->m_owner = self;
110         release(&mutex->m_spinlock);
111         return 0;
112       }
113       break;
114     case PTHREAD_MUTEX_ERRORCHECK_NP:
115       self = thread_self();
116       if (mutex->m_count == 0) {
117         mutex->m_count = 1;
118         mutex->m_owner = self;
119         release(&mutex->m_spinlock);
120         return 0;
121       } else if (mutex->m_owner == self) {
122         release(&mutex->m_spinlock);
123         return EDEADLK;
124       }
125       break;
126     default:
127       release(&mutex->m_spinlock);
128       return EINVAL;
129     }
130     /* Suspend ourselves, then try again */
131     enqueue(&mutex->m_waiting, self);
132     release(&mutex->m_spinlock);
133     suspend(self); /* This is not a cancellation point */
134   }
135 }
136 weak_alias (__pthread_mutex_lock, pthread_mutex_lock)
137
138 int __pthread_mutex_unlock(pthread_mutex_t * mutex)
139 {
140   pthread_descr th;
141
142   acquire(&mutex->m_spinlock);
143   switch (mutex->m_kind) {
144   case PTHREAD_MUTEX_FAST_NP:
145     mutex->m_count = 0;
146     break;
147   case PTHREAD_MUTEX_RECURSIVE_NP:
148     mutex->m_count--;
149     if (mutex->m_count > 0) {
150       release(&mutex->m_spinlock);
151       return 0;
152     }
153     mutex->m_count = 0; /* so that excess unlocks do not break everything */
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     mutex->m_count = 0;
161     break;
162   default:
163     release(&mutex->m_spinlock);
164     return EINVAL;
165   }
166   th = dequeue(&mutex->m_waiting);
167   release(&mutex->m_spinlock);
168   if (th != NULL) restart(th);
169   return 0;
170 }
171 weak_alias (__pthread_mutex_unlock, pthread_mutex_unlock)
172
173 int __pthread_mutexattr_init(pthread_mutexattr_t *attr)
174 {
175   attr->mutexkind = PTHREAD_MUTEX_FAST_NP;
176   return 0;
177 }
178 weak_alias (__pthread_mutexattr_init, pthread_mutexattr_init)
179
180 int __pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
181 {
182   return 0;
183 }
184 weak_alias (__pthread_mutexattr_destroy, pthread_mutexattr_destroy)
185
186 int __pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind)
187 {
188   if (kind != PTHREAD_MUTEX_FAST_NP
189       && kind != PTHREAD_MUTEX_RECURSIVE_NP
190       && kind != PTHREAD_MUTEX_ERRORCHECK_NP)
191     return EINVAL;
192   attr->mutexkind = kind;
193   return 0;
194 }
195 weak_alias (__pthread_mutexattr_setkind_np, pthread_mutexattr_setkind_np)
196
197 int __pthread_mutexattr_getkind_np(const pthread_mutexattr_t *attr, int *kind)
198 {
199   *kind = attr->mutexkind;
200   return 0;
201 }
202 weak_alias (__pthread_mutexattr_getkind_np, pthread_mutexattr_getkind_np)
203
204 /* Once-only execution */
205
206 static pthread_mutex_t once_masterlock = PTHREAD_MUTEX_INITIALIZER;
207 static pthread_cond_t once_finished = PTHREAD_COND_INITIALIZER;
208
209 enum { NEVER = 0, IN_PROGRESS = 1, DONE = 2 };
210
211 int __pthread_once(pthread_once_t * once_control, void (*init_routine)(void))
212 {
213   /* Test without locking first for speed */
214   if (*once_control == DONE) return 0;
215   /* Lock and test again */
216   pthread_mutex_lock(&once_masterlock);
217   /* If init_routine is being called from another routine, wait until
218      it completes. */
219   while (*once_control == IN_PROGRESS) {
220     pthread_cond_wait(&once_finished, &once_masterlock);
221   }
222   /* Here *once_control is stable and either NEVER or DONE. */
223   if (*once_control == NEVER) {
224     *once_control = IN_PROGRESS;
225     pthread_mutex_unlock(&once_masterlock);
226     init_routine();
227     pthread_mutex_lock(&once_masterlock);
228     *once_control = DONE;
229     pthread_cond_broadcast(&once_finished);
230   }
231   pthread_mutex_unlock(&once_masterlock);
232   return 0;
233 }
234 weak_alias (__pthread_once, pthread_once)