blob: 2a7b40accbb67203740005f1d4387513dbc85230 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/*
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
23#include <errno.h>
24#include "pthread.h"
25#include "internals.h"
26#include "spinlock.h"
27#include "restart.h"
28#include "queue.h"
29
30typedef struct {
31 long int sem_status;
32 int sem_spinlock;
33} old_sem_t;
34
35/* Maximum value the semaphore can have. */
36#define SEM_VALUE_MAX ((int) ((~0u) >> 1))
37
38static __inline__ int sem_compare_and_swap(old_sem_t *sem, long oldval, long newval)
39{
40 return compare_and_swap(&sem->sem_status, oldval, newval, &sem->sem_spinlock);
41}
42
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.
48
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, ...
52*/
53
54static void sem_restart_list(pthread_descr waiting);
55
56int __old_sem_init(old_sem_t *sem, int pshared, unsigned int value);
57int __old_sem_init(old_sem_t *sem, int pshared, unsigned int value)
58{
59 if (value > SEM_VALUE_MAX) {
60 errno = EINVAL;
61 return -1;
62 }
63 if (pshared) {
64 errno = ENOSYS;
65 return -1;
66 }
67 sem->sem_spinlock = 0;
68 sem->sem_status = ((long)value << 1) + 1;
69 return 0;
70}
71
72/* Function called by pthread_cancel to remove the thread from
73 waiting inside __old_sem_wait. Here we simply unconditionally
74 indicate that the thread is to be woken, by returning 1. */
75
76static int old_sem_extricate_func(void *obj attribute_unused, pthread_descr th attribute_unused)
77{
78 return 1;
79}
80
81int __old_sem_wait(old_sem_t * sem);
82int __old_sem_wait(old_sem_t * sem)
83{
84 long oldstatus, newstatus;
85 volatile pthread_descr self = thread_self();
86 pthread_descr * th;
87 pthread_extricate_if extr;
88
89 /* Set up extrication interface */
90 extr.pu_object = 0;
91 extr.pu_extricate_func = old_sem_extricate_func;
92
93 while (1) {
94 /* Register extrication interface */
95 __pthread_set_own_extricate_if(self, &extr);
96 do {
97 oldstatus = sem->sem_status;
98 if ((oldstatus & 1) && (oldstatus != 1))
99 newstatus = oldstatus - 2;
100 else {
101 newstatus = (long) self;
102 self->p_nextwaiting = (pthread_descr) oldstatus;
103 }
104 }
105 while (! sem_compare_and_swap(sem, oldstatus, newstatus));
106 if (newstatus & 1) {
107 /* We got the semaphore. */
108 __pthread_set_own_extricate_if(self, 0);
109 return 0;
110 }
111 /* Wait for sem_post or cancellation */
112 suspend(self);
113 __pthread_set_own_extricate_if(self, 0);
114
115 /* This is a cancellation point */
116 if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
117 /* Remove ourselves from the waiting list if we're still on it */
118 /* First check if we're at the head of the list. */
119 do {
120 oldstatus = sem->sem_status;
121 if (oldstatus != (long) self) break;
122 newstatus = (long) self->p_nextwaiting;
123 }
124 while (! sem_compare_and_swap(sem, oldstatus, newstatus));
125 /* Now, check if we're somewhere in the list.
126 There's a race condition with sem_post here, but it does not matter:
127 the net result is that at the time pthread_exit is called,
128 self is no longer reachable from sem->sem_status. */
129 if (oldstatus != (long) self && (oldstatus & 1) == 0) {
130 for (th = &(((pthread_descr) oldstatus)->p_nextwaiting);
131 *th != NULL && *th != (pthread_descr) 1;
132 th = &((*th)->p_nextwaiting)) {
133 if (*th == self) {
134 *th = self->p_nextwaiting;
135 break;
136 }
137 }
138 }
139 __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
140 }
141 }
142}
143
144int __old_sem_trywait(old_sem_t * sem);
145int __old_sem_trywait(old_sem_t * sem)
146{
147 long oldstatus, newstatus;
148
149 do {
150 oldstatus = sem->sem_status;
151 if ((oldstatus & 1) == 0 || (oldstatus == 1)) {
152 errno = EAGAIN;
153 return -1;
154 }
155 newstatus = oldstatus - 2;
156 }
157 while (! sem_compare_and_swap(sem, oldstatus, newstatus));
158 return 0;
159}
160
161int __old_sem_post(old_sem_t * sem);
162int __old_sem_post(old_sem_t * sem)
163{
164 long oldstatus, newstatus;
165
166 do {
167 oldstatus = sem->sem_status;
168 if ((oldstatus & 1) == 0)
169 newstatus = 3;
170 else {
171 if (oldstatus >= SEM_VALUE_MAX) {
172 /* Overflow */
173 errno = ERANGE;
174 return -1;
175 }
176 newstatus = oldstatus + 2;
177 }
178 }
179 while (! sem_compare_and_swap(sem, oldstatus, newstatus));
180 if ((oldstatus & 1) == 0)
181 sem_restart_list((pthread_descr) oldstatus);
182 return 0;
183}
184
185int __old_sem_getvalue(old_sem_t * sem, int * sval);
186int __old_sem_getvalue(old_sem_t * sem, int * sval)
187{
188 long status = sem->sem_status;
189 if (status & 1)
190 *sval = (int)((unsigned long) status >> 1);
191 else
192 *sval = 0;
193 return 0;
194}
195
196int __old_sem_destroy(old_sem_t * sem);
197int __old_sem_destroy(old_sem_t * sem)
198{
199 if ((sem->sem_status & 1) == 0) {
200 errno = EBUSY;
201 return -1;
202 }
203 return 0;
204}
205
206/* Auxiliary function for restarting all threads on a waiting list,
207 in priority order. */
208
209static void sem_restart_list(pthread_descr waiting)
210{
211 pthread_descr th, towake, *p;
212
213 /* Sort list of waiting threads by decreasing priority (insertion sort) */
214 towake = NULL;
215 while (waiting != (pthread_descr) 1) {
216 th = waiting;
217 waiting = waiting->p_nextwaiting;
218 p = &towake;
219 while (*p != NULL && th->p_priority < (*p)->p_priority)
220 p = &((*p)->p_nextwaiting);
221 th->p_nextwaiting = *p;
222 *p = th;
223 }
224 /* Wake up threads in priority order */
225 while (towake != NULL) {
226 th = towake;
227 towake = towake->p_nextwaiting;
228 th->p_nextwaiting = NULL;
229 restart(th);
230 }
231}
232
233#if defined __PIC__ && defined DO_VERSIONING
234symbol_version (__old_sem_init, sem_init, GLIBC_2.0);
235symbol_version (__old_sem_wait, sem_wait, GLIBC_2.0);
236symbol_version (__old_sem_trywait, sem_trywait, GLIBC_2.0);
237symbol_version (__old_sem_post, sem_post, GLIBC_2.0);
238symbol_version (__old_sem_getvalue, sem_getvalue, GLIBC_2.0);
239symbol_version (__old_sem_destroy, sem_destroy, GLIBC_2.0);
240#endif
241