chiark / gitweb /
eglibc (2.11.3-4+deb6u3) squeeze-lts; urgency=medium
[eglibc.git] / linuxthreads / oldsemaphore.c
1 /*
2  * This file contains the old semaphore code that we need to
3  * preserve for glibc-2.0 backwards compatibility. Port to glibc 2.1
4  * done by Cristian Gafton.
5  */
6
7 /* Linuxthreads - a simple clone()-based implementation of Posix        */
8 /* threads for Linux.                                                   */
9 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
10 /*                                                                      */
11 /* This program is free software; you can redistribute it and/or        */
12 /* modify it under the terms of the GNU Library General Public License  */
13 /* as published by the Free Software Foundation; either version 2       */
14 /* of the License, or (at your option) any later version.               */
15 /*                                                                      */
16 /* This program is distributed in the hope that it will be useful,      */
17 /* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
18 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
19 /* GNU Library General Public License for more details.                 */
20
21 /* Semaphores a la POSIX 1003.1b */
22 #include <shlib-compat.h>
23 #if SHLIB_COMPAT(libpthread, GLIBC_2_0, GLIBC_2_1)
24
25 #include <errno.h>
26 #include "pthread.h"
27 #include "internals.h"
28 #include "spinlock.h"
29 #include "restart.h"
30 #include "queue.h"
31
32 typedef struct {
33     long int sem_status;
34     int sem_spinlock;
35 } old_sem_t;
36
37 extern int __old_sem_init (old_sem_t *__sem, int __pshared, unsigned int __value);
38 extern int __old_sem_wait (old_sem_t *__sem);
39 extern int __old_sem_trywait (old_sem_t *__sem);
40 extern int __old_sem_post (old_sem_t *__sem);
41 extern int __old_sem_getvalue (old_sem_t *__sem, int *__sval);
42 extern int __old_sem_destroy (old_sem_t *__sem);
43
44 static inline int sem_compare_and_swap(old_sem_t *sem, long oldval, long newval)
45 {
46     return compare_and_swap(&sem->sem_status, oldval, newval, &sem->sem_spinlock);
47 }
48
49 /* The state of a semaphore is represented by a long int encoding
50    either the semaphore count if >= 0 and no thread is waiting on it,
51    or the head of the list of threads waiting for the semaphore.
52    To distinguish the two cases, we encode the semaphore count N
53    as 2N+1, so that it has the lowest bit set.
54
55    A sequence of sem_wait operations on a semaphore initialized to N
56    result in the following successive states:
57      2N+1, 2N-1, ..., 3, 1, &first_waiting_thread, &second_waiting_thread, ...
58 */
59
60 static void sem_restart_list(pthread_descr waiting);
61
62 int __old_sem_init(old_sem_t *sem, int pshared, unsigned int value)
63 {
64     if (value > SEM_VALUE_MAX) {
65         errno = EINVAL;
66         return -1;
67     }
68     if (pshared) {
69         errno = ENOSYS;
70         return -1;
71     }
72   sem->sem_spinlock = __LT_SPINLOCK_INIT;
73   sem->sem_status = ((long)value << 1) + 1;
74   return 0;
75 }
76
77 /* Function called by pthread_cancel to remove the thread from
78    waiting inside __old_sem_wait. Here we simply unconditionally
79    indicate that the thread is to be woken, by returning 1. */
80
81 static int old_sem_extricate_func(void *obj, pthread_descr th)
82 {
83     return 1;
84 }
85
86 int __old_sem_wait(old_sem_t * sem)
87 {
88     long oldstatus, newstatus;
89     volatile pthread_descr self = thread_self();
90     pthread_descr * th;
91     pthread_extricate_if extr;
92
93     /* Set up extrication interface */
94     extr.pu_object = 0;
95     extr.pu_extricate_func = old_sem_extricate_func;
96
97     while (1) {
98         /* Register extrication interface */
99         __pthread_set_own_extricate_if(self, &extr);
100         do {
101             oldstatus = sem->sem_status;
102             if ((oldstatus & 1) && (oldstatus != 1))
103                 newstatus = oldstatus - 2;
104             else {
105                 newstatus = (long) self;
106                 self->p_nextwaiting = (pthread_descr) oldstatus;
107             }
108         }
109         while (! sem_compare_and_swap(sem, oldstatus, newstatus));
110         if (newstatus & 1) {
111             /* We got the semaphore. */
112             __pthread_set_own_extricate_if(self, 0);
113             self->p_nextwaiting = NULL;
114             return 0;
115         }
116         /* Wait for sem_post or cancellation */
117         suspend(self);
118         __pthread_set_own_extricate_if(self, 0);
119
120         /* This is a cancellation point */
121         if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
122             /* Remove ourselves from the waiting list if we're still on it */
123             /* First check if we're at the head of the list. */
124             do {
125                 oldstatus = sem->sem_status;
126                 if (oldstatus != (long) self) break;
127                 newstatus = (long) self->p_nextwaiting;
128             }
129             while (! sem_compare_and_swap(sem, oldstatus, newstatus));
130             /* Now, check if we're somewhere in the list.
131                There's a race condition with sem_post here, but it does not matter:
132                the net result is that at the time pthread_exit is called,
133                self is no longer reachable from sem->sem_status. */
134             if (oldstatus != (long) self && (oldstatus & 1) == 0) {
135                 for (th = &(((pthread_descr) oldstatus)->p_nextwaiting);
136                      *th != NULL && *th != (pthread_descr) 1;
137                      th = &((*th)->p_nextwaiting)) {
138                     if (*th == self) {
139                         *th = self->p_nextwaiting;
140                         break;
141                     }
142                 }
143             }
144             __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
145         }
146     }
147 }
148
149 int __old_sem_trywait(old_sem_t * sem)
150 {
151   long oldstatus, newstatus;
152
153   do {
154     oldstatus = sem->sem_status;
155     if ((oldstatus & 1) == 0 || (oldstatus == 1)) {
156       errno = EAGAIN;
157       return -1;
158     }
159     newstatus = oldstatus - 2;
160   }
161   while (! sem_compare_and_swap(sem, oldstatus, newstatus));
162   return 0;
163 }
164
165 int __old_sem_post(old_sem_t * sem)
166 {
167   long oldstatus, newstatus;
168
169   do {
170     oldstatus = sem->sem_status;
171     if ((oldstatus & 1) == 0)
172       newstatus = 3;
173     else {
174       if (oldstatus >= SEM_VALUE_MAX) {
175         /* Overflow */
176         errno = ERANGE;
177         return -1;
178       }
179       newstatus = oldstatus + 2;
180     }
181   }
182   while (! sem_compare_and_swap(sem, oldstatus, newstatus));
183   if ((oldstatus & 1) == 0)
184     sem_restart_list((pthread_descr) oldstatus);
185   return 0;
186 }
187
188 int __old_sem_getvalue(old_sem_t * sem, int * sval)
189 {
190   long status = sem->sem_status;
191   if (status & 1)
192     *sval = (int)((unsigned long) status >> 1);
193   else
194     *sval = 0;
195   return 0;
196 }
197
198 int __old_sem_destroy(old_sem_t * sem)
199 {
200   if ((sem->sem_status & 1) == 0) {
201     errno = EBUSY;
202     return -1;
203   }
204   return 0;
205 }
206
207 /* Auxiliary function for restarting all threads on a waiting list,
208    in priority order. */
209
210 static void sem_restart_list(pthread_descr waiting)
211 {
212   pthread_descr th, towake, *p;
213
214   /* Sort list of waiting threads by decreasing priority (insertion sort) */
215   towake = NULL;
216   while (waiting != (pthread_descr) 1) {
217     th = waiting;
218     waiting = waiting->p_nextwaiting;
219     p = &towake;
220     while (*p != NULL && th->p_priority < (*p)->p_priority)
221       p = &((*p)->p_nextwaiting);
222     th->p_nextwaiting = *p;
223     *p = th;
224   }
225   /* Wake up threads in priority order */
226   while (towake != NULL) {
227     th = towake;
228     towake = towake->p_nextwaiting;
229     th->p_nextwaiting = NULL;
230     restart(th);
231   }
232 }
233
234 compat_symbol (libpthread, __old_sem_init, sem_init, GLIBC_2_0);
235 compat_symbol (libpthread, __old_sem_wait, sem_wait, GLIBC_2_0);
236 compat_symbol (libpthread, __old_sem_trywait, sem_trywait, GLIBC_2_0);
237 compat_symbol (libpthread, __old_sem_post, sem_post, GLIBC_2_0);
238 compat_symbol (libpthread, __old_sem_getvalue, sem_getvalue, GLIBC_2_0);
239 compat_symbol (libpthread, __old_sem_destroy, sem_destroy, GLIBC_2_0);
240
241 #endif