(main): Fix loop printing cleanup output.
[kopensolaris-gnu/glibc.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
45 /* Maximum value the semaphore can have.  */
46 #define SEM_VALUE_MAX   ((int) ((~0u) >> 1))
47
48 static inline int sem_compare_and_swap(old_sem_t *sem, long oldval, long newval)
49 {
50     return compare_and_swap(&sem->sem_status, oldval, newval, &sem->sem_spinlock);
51 }
52
53 /* The state of a semaphore is represented by a long int encoding
54    either the semaphore count if >= 0 and no thread is waiting on it,
55    or the head of the list of threads waiting for the semaphore.
56    To distinguish the two cases, we encode the semaphore count N
57    as 2N+1, so that it has the lowest bit set.
58
59    A sequence of sem_wait operations on a semaphore initialized to N
60    result in the following successive states:
61      2N+1, 2N-1, ..., 3, 1, &first_waiting_thread, &second_waiting_thread, ...
62 */
63
64 static void sem_restart_list(pthread_descr waiting);
65
66 int __old_sem_init(old_sem_t *sem, int pshared, unsigned int value)
67 {
68     if (value > SEM_VALUE_MAX) {
69         errno = EINVAL;
70         return -1;
71     }
72     if (pshared) {
73         errno = ENOSYS;
74         return -1;
75     }
76   sem->sem_spinlock = __LT_SPINLOCK_INIT;
77   sem->sem_status = ((long)value << 1) + 1;
78   return 0;
79 }
80
81 /* Function called by pthread_cancel to remove the thread from
82    waiting inside __old_sem_wait. Here we simply unconditionally
83    indicate that the thread is to be woken, by returning 1. */
84
85 static int old_sem_extricate_func(void *obj, pthread_descr th)
86 {
87     return 1;
88 }
89
90 int __old_sem_wait(old_sem_t * sem)
91 {
92     long oldstatus, newstatus;
93     volatile pthread_descr self = thread_self();
94     pthread_descr * th;
95     pthread_extricate_if extr;
96
97     /* Set up extrication interface */
98     extr.pu_object = 0;
99     extr.pu_extricate_func = old_sem_extricate_func;
100
101     while (1) {
102         /* Register extrication interface */
103         __pthread_set_own_extricate_if(self, &extr);
104         do {
105             oldstatus = sem->sem_status;
106             if ((oldstatus & 1) && (oldstatus != 1))
107                 newstatus = oldstatus - 2;
108             else {
109                 newstatus = (long) self;
110                 self->p_nextwaiting = (pthread_descr) oldstatus;
111             }
112         }
113         while (! sem_compare_and_swap(sem, oldstatus, newstatus));
114         if (newstatus & 1) {
115             /* We got the semaphore. */
116           __pthread_set_own_extricate_if(self, 0);
117             return 0;
118         }
119         /* Wait for sem_post or cancellation */
120         suspend(self);
121         __pthread_set_own_extricate_if(self, 0);
122
123         /* This is a cancellation point */
124         if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
125             /* Remove ourselves from the waiting list if we're still on it */
126             /* First check if we're at the head of the list. */
127             do {
128                 oldstatus = sem->sem_status;
129                 if (oldstatus != (long) self) break;
130                 newstatus = (long) self->p_nextwaiting;
131             }
132             while (! sem_compare_and_swap(sem, oldstatus, newstatus));
133             /* Now, check if we're somewhere in the list.
134                There's a race condition with sem_post here, but it does not matter:
135                the net result is that at the time pthread_exit is called,
136                self is no longer reachable from sem->sem_status. */
137             if (oldstatus != (long) self && (oldstatus & 1) == 0) {
138                 for (th = &(((pthread_descr) oldstatus)->p_nextwaiting);
139                      *th != NULL && *th != (pthread_descr) 1;
140                      th = &((*th)->p_nextwaiting)) {
141                     if (*th == self) {
142                         *th = self->p_nextwaiting;
143                         break;
144                     }
145                 }
146             }
147             __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
148         }
149     }
150 }
151
152 int __old_sem_trywait(old_sem_t * sem)
153 {
154   long oldstatus, newstatus;
155
156   do {
157     oldstatus = sem->sem_status;
158     if ((oldstatus & 1) == 0 || (oldstatus == 1)) {
159       errno = EAGAIN;
160       return -1;
161     }
162     newstatus = oldstatus - 2;
163   }
164   while (! sem_compare_and_swap(sem, oldstatus, newstatus));
165   return 0;
166 }
167
168 int __old_sem_post(old_sem_t * sem)
169 {
170   long oldstatus, newstatus;
171
172   do {
173     oldstatus = sem->sem_status;
174     if ((oldstatus & 1) == 0)
175       newstatus = 3;
176     else {
177       if (oldstatus >= SEM_VALUE_MAX) {
178         /* Overflow */
179         errno = ERANGE;
180         return -1;
181       }
182       newstatus = oldstatus + 2;
183     }
184   }
185   while (! sem_compare_and_swap(sem, oldstatus, newstatus));
186   if ((oldstatus & 1) == 0)
187     sem_restart_list((pthread_descr) oldstatus);
188   return 0;
189 }
190
191 int __old_sem_getvalue(old_sem_t * sem, int * sval)
192 {
193   long status = sem->sem_status;
194   if (status & 1)
195     *sval = (int)((unsigned long) status >> 1);
196   else
197     *sval = 0;
198   return 0;
199 }
200
201 int __old_sem_destroy(old_sem_t * sem)
202 {
203   if ((sem->sem_status & 1) == 0) {
204     errno = EBUSY;
205     return -1;
206   }
207   return 0;
208 }
209
210 /* Auxiliary function for restarting all threads on a waiting list,
211    in priority order. */
212
213 static void sem_restart_list(pthread_descr waiting)
214 {
215   pthread_descr th, towake, *p;
216
217   /* Sort list of waiting threads by decreasing priority (insertion sort) */
218   towake = NULL;
219   while (waiting != (pthread_descr) 1) {
220     th = waiting;
221     waiting = waiting->p_nextwaiting;
222     p = &towake;
223     while (*p != NULL && th->p_priority < (*p)->p_priority)
224       p = &((*p)->p_nextwaiting);
225     th->p_nextwaiting = *p;
226     *p = th;
227   }
228   /* Wake up threads in priority order */
229   while (towake != NULL) {
230     th = towake;
231     towake = towake->p_nextwaiting;
232     th->p_nextwaiting = NULL;
233     restart(th);
234   }
235 }
236
237 compat_symbol (libpthread, __old_sem_init, sem_init, GLIBC_2_0);
238 compat_symbol (libpthread, __old_sem_wait, sem_wait, GLIBC_2_0);
239 compat_symbol (libpthread, __old_sem_trywait, sem_trywait, GLIBC_2_0);
240 compat_symbol (libpthread, __old_sem_post, sem_post, GLIBC_2_0);
241 compat_symbol (libpthread, __old_sem_getvalue, sem_getvalue, GLIBC_2_0);
242 compat_symbol (libpthread, __old_sem_destroy, sem_destroy, GLIBC_2_0);
243
244 #endif