2 |
|
* sheepthreads.c - Minimal pthreads implementation (libpthreads doesn't |
3 |
|
* like nonstandard stacks) |
4 |
|
* |
5 |
< |
* SheepShaver (C) 1997-2002 Christian Bauer and Marc Hellwig |
5 |
> |
* SheepShaver (C) 1997-2005 Christian Bauer and Marc Hellwig |
6 |
|
* |
7 |
|
* This program is free software; you can redistribute it and/or modify |
8 |
|
* it under the terms of the GNU General Public License as published by |
33 |
|
#include <errno.h> |
34 |
|
#include <unistd.h> |
35 |
|
#include <signal.h> |
36 |
– |
#include <sched.h> |
36 |
|
#include <pthread.h> |
38 |
– |
#include <semaphore.h> |
37 |
|
|
38 |
|
|
39 |
|
/* Thread stack size */ |
49 |
|
extern int __clone(int (*fn)(void *), void *, int, void *); |
50 |
|
|
51 |
|
/* struct sem_t */ |
52 |
+ |
typedef struct { |
53 |
+ |
struct _pthread_fastlock __sem_lock; |
54 |
+ |
int __sem_value; |
55 |
+ |
_pthread_descr __sem_waiting; |
56 |
+ |
} sem_t; |
57 |
+ |
|
58 |
+ |
#define SEM_VALUE_MAX 64 |
59 |
+ |
#define status __status |
60 |
+ |
#define spinlock __spinlock |
61 |
|
#define sem_lock __sem_lock |
62 |
|
#define sem_value __sem_value |
63 |
|
#define sem_waiting __sem_waiting |
64 |
|
|
65 |
+ |
/* Wait for "clone" children only (Linux 2.4+ specific) */ |
66 |
+ |
#ifndef __WCLONE |
67 |
+ |
#define __WCLONE 0 |
68 |
+ |
#endif |
69 |
+ |
|
70 |
|
|
71 |
|
/* |
72 |
|
* Return pthread ID of self |
147 |
|
int pthread_join(pthread_t thread, void **ret) |
148 |
|
{ |
149 |
|
do { |
150 |
< |
if (waitpid(thread, NULL, 0) >= 0) |
150 |
> |
if (waitpid(thread, NULL, __WCLONE) >= 0); |
151 |
|
break; |
152 |
|
} while (errno == EINTR); |
153 |
|
if (ret) |
180 |
|
* Spinlocks |
181 |
|
*/ |
182 |
|
|
183 |
< |
static void acquire_spinlock(volatile int *lock) |
183 |
> |
/* For multiprocessor systems, we want to ensure all memory accesses |
184 |
> |
are completed before we reset a lock. On other systems, we still |
185 |
> |
need to make sure that the compiler has flushed everything to memory. */ |
186 |
> |
#define MEMORY_BARRIER() __asm__ __volatile__ ("sync" : : : "memory") |
187 |
> |
|
188 |
> |
static void fastlock_init(struct _pthread_fastlock *lock) |
189 |
|
{ |
190 |
< |
do { |
191 |
< |
while (*lock) ; |
192 |
< |
} while (test_and_set((int *)lock, 1) != 0); |
190 |
> |
lock->status = 0; |
191 |
> |
lock->spinlock = 0; |
192 |
> |
} |
193 |
> |
|
194 |
> |
static int fastlock_try_acquire(struct _pthread_fastlock *lock) |
195 |
> |
{ |
196 |
> |
int res = EBUSY; |
197 |
> |
if (test_and_set(&lock->spinlock, 1) == 0) { |
198 |
> |
if (lock->status == 0) { |
199 |
> |
lock->status = 1; |
200 |
> |
MEMORY_BARRIER(); |
201 |
> |
res = 0; |
202 |
> |
} |
203 |
> |
lock->spinlock = 0; |
204 |
> |
} |
205 |
> |
return res; |
206 |
> |
} |
207 |
> |
|
208 |
> |
static void fastlock_acquire(struct _pthread_fastlock *lock) |
209 |
> |
{ |
210 |
> |
MEMORY_BARRIER(); |
211 |
> |
while (test_and_set(&lock->spinlock, 1)) |
212 |
> |
usleep(0); |
213 |
> |
} |
214 |
> |
|
215 |
> |
static void fastlock_release(struct _pthread_fastlock *lock) |
216 |
> |
{ |
217 |
> |
MEMORY_BARRIER(); |
218 |
> |
lock->spinlock = 0; |
219 |
> |
__asm__ __volatile__ ("" : "=m" (lock->spinlock) : "m" (lock->spinlock)); |
220 |
> |
} |
221 |
> |
|
222 |
> |
|
223 |
> |
/* |
224 |
> |
* Initialize mutex |
225 |
> |
*/ |
226 |
> |
|
227 |
> |
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutex_attr) |
228 |
> |
{ |
229 |
> |
fastlock_init(&mutex->__m_lock); |
230 |
> |
mutex->__m_kind = mutex_attr ? mutex_attr->__mutexkind : PTHREAD_MUTEX_TIMED_NP; |
231 |
> |
mutex->__m_count = 0; |
232 |
> |
mutex->__m_owner = NULL; |
233 |
> |
return 0; |
234 |
> |
} |
235 |
> |
|
236 |
> |
|
237 |
> |
/* |
238 |
> |
* Destroy mutex |
239 |
> |
*/ |
240 |
> |
|
241 |
> |
int pthread_mutex_destroy(pthread_mutex_t *mutex) |
242 |
> |
{ |
243 |
> |
switch (mutex->__m_kind) { |
244 |
> |
case PTHREAD_MUTEX_TIMED_NP: |
245 |
> |
return (mutex->__m_lock.__status != 0) ? EBUSY : 0; |
246 |
> |
default: |
247 |
> |
return EINVAL; |
248 |
> |
} |
249 |
> |
} |
250 |
> |
|
251 |
> |
|
252 |
> |
/* |
253 |
> |
* Lock mutex |
254 |
> |
*/ |
255 |
> |
|
256 |
> |
int pthread_mutex_lock(pthread_mutex_t *mutex) |
257 |
> |
{ |
258 |
> |
switch (mutex->__m_kind) { |
259 |
> |
case PTHREAD_MUTEX_TIMED_NP: |
260 |
> |
fastlock_acquire(&mutex->__m_lock); |
261 |
> |
return 0; |
262 |
> |
default: |
263 |
> |
return EINVAL; |
264 |
> |
} |
265 |
|
} |
266 |
|
|
267 |
< |
static void release_spinlock(int *lock) |
267 |
> |
|
268 |
> |
/* |
269 |
> |
* Try to lock mutex |
270 |
> |
*/ |
271 |
> |
|
272 |
> |
int pthread_mutex_trylock(pthread_mutex_t *mutex) |
273 |
|
{ |
274 |
< |
*lock = 0; |
274 |
> |
switch (mutex->__m_kind) { |
275 |
> |
case PTHREAD_MUTEX_TIMED_NP: |
276 |
> |
return fastlock_try_acquire(&mutex->__m_lock); |
277 |
> |
default: |
278 |
> |
return EINVAL; |
279 |
> |
} |
280 |
> |
} |
281 |
> |
|
282 |
> |
|
283 |
> |
/* |
284 |
> |
* Unlock mutex |
285 |
> |
*/ |
286 |
> |
|
287 |
> |
int pthread_mutex_unlock(pthread_mutex_t *mutex) |
288 |
> |
{ |
289 |
> |
switch (mutex->__m_kind) { |
290 |
> |
case PTHREAD_MUTEX_TIMED_NP: |
291 |
> |
fastlock_release(&mutex->__m_lock); |
292 |
> |
return 0; |
293 |
> |
default: |
294 |
> |
return EINVAL; |
295 |
> |
} |
296 |
> |
} |
297 |
> |
|
298 |
> |
|
299 |
> |
/* |
300 |
> |
* Create mutex attribute |
301 |
> |
*/ |
302 |
> |
|
303 |
> |
int pthread_mutexattr_init(pthread_mutexattr_t *attr) |
304 |
> |
{ |
305 |
> |
attr->__mutexkind = PTHREAD_MUTEX_TIMED_NP; |
306 |
> |
return 0; |
307 |
> |
} |
308 |
> |
|
309 |
> |
|
310 |
> |
/* |
311 |
> |
* Destroy mutex attribute |
312 |
> |
*/ |
313 |
> |
|
314 |
> |
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) |
315 |
> |
{ |
316 |
> |
return 0; |
317 |
|
} |
318 |
|
|
319 |
|
|
323 |
|
|
324 |
|
int sem_init(sem_t *sem, int pshared, unsigned int value) |
325 |
|
{ |
326 |
< |
sem->sem_lock.status = 0; |
327 |
< |
sem->sem_lock.spinlock = 0; |
326 |
> |
if (sem == NULL || value > SEM_VALUE_MAX) { |
327 |
> |
errno = EINVAL; |
328 |
> |
return -1; |
329 |
> |
} |
330 |
> |
if (pshared) { |
331 |
> |
errno = ENOSYS; |
332 |
> |
return -1; |
333 |
> |
} |
334 |
> |
fastlock_init(&sem->sem_lock); |
335 |
|
sem->sem_value = value; |
336 |
|
sem->sem_waiting = NULL; |
337 |
|
return 0; |
344 |
|
|
345 |
|
int sem_destroy(sem_t *sem) |
346 |
|
{ |
347 |
+ |
if (sem == NULL) { |
348 |
+ |
errno = EINVAL; |
349 |
+ |
return -1; |
350 |
+ |
} |
351 |
+ |
if (sem->sem_waiting) { |
352 |
+ |
errno = EBUSY; |
353 |
+ |
return -1; |
354 |
+ |
} |
355 |
+ |
sem->sem_value = 0; |
356 |
+ |
sem->sem_waiting = NULL; |
357 |
|
return 0; |
358 |
|
} |
359 |
|
|
362 |
|
* Wait on semaphore |
363 |
|
*/ |
364 |
|
|
212 |
– |
void null_handler(int sig) |
213 |
– |
{ |
214 |
– |
} |
215 |
– |
|
365 |
|
int sem_wait(sem_t *sem) |
366 |
|
{ |
367 |
< |
acquire_spinlock(&sem->sem_lock.spinlock); |
368 |
< |
if (atomic_add((int *)&sem->sem_value, -1) >= 0) { |
369 |
< |
sigset_t mask; |
221 |
< |
if (!sem->sem_lock.status) { |
222 |
< |
struct sigaction sa; |
223 |
< |
sem->sem_lock.status = SIGUSR2; |
224 |
< |
sa.sa_handler = null_handler; |
225 |
< |
sa.sa_flags = SA_RESTART; |
226 |
< |
sigemptyset(&sa.sa_mask); |
227 |
< |
sigaction(sem->sem_lock.status, &sa, NULL); |
228 |
< |
} |
229 |
< |
sem->sem_waiting = (struct _pthread_descr_struct *)getpid(); |
230 |
< |
sigemptyset(&mask); |
231 |
< |
sigsuspend(&mask); |
232 |
< |
sem->sem_waiting = NULL; |
367 |
> |
if (sem == NULL) { |
368 |
> |
errno = EINVAL; |
369 |
> |
return -1; |
370 |
|
} |
371 |
< |
release_spinlock(&sem->sem_lock.spinlock); |
371 |
> |
fastlock_acquire(&sem->sem_lock); |
372 |
> |
if (sem->sem_value > 0) { |
373 |
> |
sem->sem_value--; |
374 |
> |
fastlock_release(&sem->sem_lock); |
375 |
> |
return 0; |
376 |
> |
} |
377 |
> |
sem->sem_waiting = (struct _pthread_descr_struct *)((long)sem->sem_waiting + 1); |
378 |
> |
while (sem->sem_value == 0) { |
379 |
> |
fastlock_release(&sem->sem_lock); |
380 |
> |
usleep(0); |
381 |
> |
fastlock_acquire(&sem->sem_lock); |
382 |
> |
} |
383 |
> |
sem->sem_value--; |
384 |
> |
fastlock_release(&sem->sem_lock); |
385 |
|
return 0; |
386 |
|
} |
387 |
|
|
392 |
|
|
393 |
|
int sem_post(sem_t *sem) |
394 |
|
{ |
395 |
< |
acquire_spinlock(&sem->sem_lock.spinlock); |
396 |
< |
atomic_add((int *)&sem->sem_value, 1); |
395 |
> |
if (sem == NULL) { |
396 |
> |
errno = EINVAL; |
397 |
> |
return -1; |
398 |
> |
} |
399 |
> |
fastlock_acquire(&sem->sem_lock); |
400 |
|
if (sem->sem_waiting) |
401 |
< |
kill((pid_t)sem->sem_waiting, sem->sem_lock.status); |
402 |
< |
release_spinlock(&sem->sem_lock.spinlock); |
401 |
> |
sem->sem_waiting = (struct _pthread_descr_struct *)((long)sem->sem_waiting - 1); |
402 |
> |
else { |
403 |
> |
if (sem->sem_value >= SEM_VALUE_MAX) { |
404 |
> |
errno = ERANGE; |
405 |
> |
fastlock_release(&sem->sem_lock); |
406 |
> |
return -1; |
407 |
> |
} |
408 |
> |
} |
409 |
> |
sem->sem_value++; |
410 |
> |
fastlock_release(&sem->sem_lock); |
411 |
> |
return 0; |
412 |
> |
} |
413 |
> |
|
414 |
> |
|
415 |
> |
/* |
416 |
> |
* Simple producer/consumer test program |
417 |
> |
*/ |
418 |
> |
|
419 |
> |
#ifdef TEST |
420 |
> |
#include <stdio.h> |
421 |
> |
|
422 |
> |
static sem_t p_sem, c_sem; |
423 |
> |
static int data = 0; |
424 |
> |
|
425 |
> |
static void *producer_func(void *arg) |
426 |
> |
{ |
427 |
> |
int i, n = (int)arg; |
428 |
> |
for (i = 0; i < n; i++) { |
429 |
> |
sem_wait(&p_sem); |
430 |
> |
data++; |
431 |
> |
sem_post(&c_sem); |
432 |
> |
} |
433 |
> |
return NULL; |
434 |
> |
} |
435 |
> |
|
436 |
> |
static void *consumer_func(void *arg) |
437 |
> |
{ |
438 |
> |
int i, n = (int)arg; |
439 |
> |
for (i = 0; i < n; i++) { |
440 |
> |
sem_wait(&c_sem); |
441 |
> |
printf("data: %d\n", data); |
442 |
> |
sem_post(&p_sem); |
443 |
> |
} |
444 |
> |
sleep(1); // for testing pthread_join() |
445 |
> |
return NULL; |
446 |
> |
} |
447 |
> |
|
448 |
> |
int main(void) |
449 |
> |
{ |
450 |
> |
pthread_t producer_thread, consumer_thread; |
451 |
> |
static const int N = 5; |
452 |
> |
|
453 |
> |
if (sem_init(&c_sem, 0, 0) < 0) |
454 |
> |
return 1; |
455 |
> |
if (sem_init(&p_sem, 0, 1) < 0) |
456 |
> |
return 2; |
457 |
> |
if (pthread_create(&producer_thread, NULL, producer_func, (void *)N) != 0) |
458 |
> |
return 3; |
459 |
> |
if (pthread_create(&consumer_thread, NULL, consumer_func, (void *)N) != 0) |
460 |
> |
return 4; |
461 |
> |
pthread_join(producer_thread, NULL); |
462 |
> |
pthread_join(consumer_thread, NULL); |
463 |
> |
sem_destroy(&p_sem); |
464 |
> |
sem_destroy(&c_sem); |
465 |
> |
if (data != N) |
466 |
> |
return 5; |
467 |
|
return 0; |
468 |
|
} |
469 |
+ |
#endif |