Update.
[glibc.git] / linuxthreads / oldsemaphore.c
blob72d12d20c3884250327bbc209d0508d6e2af1116
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 */
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. */
21 /* Semaphores a la POSIX 1003.1b */
23 #include <errno.h>
24 #include "pthread.h"
25 #include "internals.h"
26 #include "spinlock.h"
27 #include "restart.h"
28 #include "queue.h"
30 typedef struct {
31 long int sem_status;
32 int sem_spinlock;
33 } old_sem_t;
35 /* Maximum value the semaphore can have. */
36 #define SEM_VALUE_MAX ((int) ((~0u) >> 1))
38 static inline int sem_compare_and_swap(old_sem_t *sem, long oldval, long newval)
40 return compare_and_swap(&sem->sem_status, oldval, newval, &sem->sem_spinlock);
43 /* The state of a semaphore is represented by a long int encoding
44 either the semaphore count if >= 0 and no thread is waiting on it,
45 or the head of the list of threads waiting for the semaphore.
46 To distinguish the two cases, we encode the semaphore count N
47 as 2N+1, so that it has the lowest bit set.
49 A sequence of sem_wait operations on a semaphore initialized to N
50 result in the following successive states:
51 2N+1, 2N-1, ..., 3, 1, &first_waiting_thread, &second_waiting_thread, ...
54 static void sem_restart_list(pthread_descr waiting);
56 int __old_sem_init(old_sem_t *sem, int pshared, unsigned int value)
58 if (value > SEM_VALUE_MAX) {
59 errno = EINVAL;
60 return -1;
62 if (pshared) {
63 errno = ENOSYS;
64 return -1;
66 sem->sem_spinlock = 0;
67 sem->sem_status = ((long)value << 1) + 1;
68 return 0;
71 int __old_sem_wait(old_sem_t * sem)
73 long oldstatus, newstatus;
74 volatile pthread_descr self = thread_self();
75 pthread_descr * th;
77 while (1) {
78 do {
79 oldstatus = sem->sem_status;
80 if ((oldstatus & 1) && (oldstatus != 1))
81 newstatus = oldstatus - 2;
82 else {
83 newstatus = (long) self;
84 self->p_nextwaiting = (pthread_descr) oldstatus;
87 while (! sem_compare_and_swap(sem, oldstatus, newstatus));
88 if (newstatus & 1)
89 /* We got the semaphore. */
90 return 0;
91 /* Wait for sem_post or cancellation */
92 suspend_with_cancellation(self);
93 /* This is a cancellation point */
94 if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
95 /* Remove ourselves from the waiting list if we're still on it */
96 /* First check if we're at the head of the list. */
97 do {
98 oldstatus = sem->sem_status;
99 if (oldstatus != (long) self) break;
100 newstatus = (long) self->p_nextwaiting;
102 while (! sem_compare_and_swap(sem, oldstatus, newstatus));
103 /* Now, check if we're somewhere in the list.
104 There's a race condition with sem_post here, but it does not matter:
105 the net result is that at the time pthread_exit is called,
106 self is no longer reachable from sem->sem_status. */
107 if (oldstatus != (long) self && (oldstatus & 1) == 0) {
108 for (th = &(((pthread_descr) oldstatus)->p_nextwaiting);
109 *th != NULL && *th != (pthread_descr) 1;
110 th = &((*th)->p_nextwaiting)) {
111 if (*th == self) {
112 *th = self->p_nextwaiting;
113 break;
117 pthread_exit(PTHREAD_CANCELED);
122 int __old_sem_trywait(old_sem_t * sem)
124 long oldstatus, newstatus;
126 do {
127 oldstatus = sem->sem_status;
128 if ((oldstatus & 1) == 0 || (oldstatus == 1)) {
129 errno = EAGAIN;
130 return -1;
132 newstatus = oldstatus - 2;
134 while (! sem_compare_and_swap(sem, oldstatus, newstatus));
135 return 0;
138 int __old_sem_post(old_sem_t * sem)
140 long oldstatus, newstatus;
142 do {
143 oldstatus = sem->sem_status;
144 if ((oldstatus & 1) == 0)
145 newstatus = 3;
146 else {
147 if (oldstatus >= SEM_VALUE_MAX) {
148 /* Overflow */
149 errno = ERANGE;
150 return -1;
152 newstatus = oldstatus + 2;
155 while (! sem_compare_and_swap(sem, oldstatus, newstatus));
156 if ((oldstatus & 1) == 0)
157 sem_restart_list((pthread_descr) oldstatus);
158 return 0;
161 int __old_sem_getvalue(old_sem_t * sem, int * sval)
163 long status = sem->sem_status;
164 if (status & 1)
165 *sval = (int)((unsigned long) status >> 1);
166 else
167 *sval = 0;
168 return 0;
171 int __old_sem_destroy(old_sem_t * sem)
173 if ((sem->sem_status & 1) == 0) {
174 errno = EBUSY;
175 return -1;
177 return 0;
180 /* Auxiliary function for restarting all threads on a waiting list,
181 in priority order. */
183 static void sem_restart_list(pthread_descr waiting)
185 pthread_descr th, towake, *p;
187 /* Sort list of waiting threads by decreasing priority (insertion sort) */
188 towake = NULL;
189 while (waiting != (pthread_descr) 1) {
190 th = waiting;
191 waiting = waiting->p_nextwaiting;
192 p = &towake;
193 while (*p != NULL && th->p_priority < (*p)->p_priority)
194 p = &((*p)->p_nextwaiting);
195 th->p_nextwaiting = *p;
196 *p = th;
198 /* Wake up threads in priority order */
199 while (towake != NULL) {
200 th = towake;
201 towake = towake->p_nextwaiting;
202 th->p_nextwaiting = NULL;
203 restart(th);
207 #if defined PIC && DO_VERSIONING
208 symbol_version (__old_sem_init, sem_init, GLIBC_2.0);
209 symbol_version (__old_sem_wait, sem_wait, GLIBC_2.0);
210 symbol_version (__old_sem_trywait, sem_trywait, GLIBC_2.0);
211 symbol_version (__old_sem_post, sem_post, GLIBC_2.0);
212 symbol_version (__old_sem_getvalue, sem_getvalue, GLIBC_2.0);
213 symbol_version (__old_sem_destroy, sem_destroy, GLIBC_2.0);
214 #endif