mirror of
https://sourceware.org/git/glibc.git
synced 2025-12-24 17:51:17 +03:00
Finish user stack support. Change locking code to be safe in situations with different priorities.
1998-06-25 19:27 Ulrich Drepper <drepper@cygnus.com> * attr.c: Finish user stack support. Change locking code to be safe in situations with different priorities. * cancel.c: Likewise. * condvar.c: Likewise. * internals.h: Likewise. * join.c: Likewise. * manager.c: Likewise. * mutex.c: Likewise. * pthread.c: Likewise. * ptlongjmp.c: Likewise. * queue.h: Likewise. * rwlock.c: Likewise. * semaphore.c: Likewise. * semaphore.h: Likewise. * signals.c: Likewise. * spinlock.c: Likewise. * spinlock.h: Likewise. Patches by Xavier leroy. 1998-06-25 Ulrich Drepper <drepper@cygnus.com> * sysdeps/pthread/pthread.h: Make [sg]et_stacksize and [sg]et_stackaddr prototypes always available. * sysdeps/unix/sysv/linux/bits/posix_opt.h: Define _POSIX_THREAD_ATTR_STACKSIZE and _POSIX_THREAD_ATTR_STACKADDR.
This commit is contained in:
@@ -12,15 +12,130 @@
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU Library General Public License for more details. */
|
||||
|
||||
/* Spin locks */
|
||||
/* Internal locks */
|
||||
|
||||
#include <sched.h>
|
||||
#include <time.h>
|
||||
#include "pthread.h"
|
||||
#include "internals.h"
|
||||
#include "spinlock.h"
|
||||
#include "restart.h"
|
||||
|
||||
/* This function is called if the inlined test-and-set in acquire() failed */
|
||||
/* The status field of a fastlock has the following meaning:
|
||||
0: fastlock is free
|
||||
1: fastlock is taken, no thread is waiting on it
|
||||
ADDR: fastlock is taken, ADDR is address of thread descriptor for
|
||||
first waiting thread, other waiting threads are linked via
|
||||
their p_nextwaiting field.
|
||||
The waiting list is not sorted by priority order.
|
||||
Actually, we always insert at top of list (sole insertion mode
|
||||
that can be performed without locking).
|
||||
For __pthread_unlock, we perform a linear search in the list
|
||||
to find the highest-priority, oldest waiting thread.
|
||||
This is safe because there are no concurrent __pthread_unlock
|
||||
operations -- only the thread that locked the mutex can unlock it. */
|
||||
|
||||
void __pthread_lock(struct _pthread_fastlock * lock)
|
||||
{
|
||||
long oldstatus, newstatus;
|
||||
pthread_descr self = NULL;
|
||||
|
||||
do {
|
||||
oldstatus = lock->status;
|
||||
if (oldstatus == 0) {
|
||||
newstatus = 1;
|
||||
} else {
|
||||
self = thread_self();
|
||||
self->p_nextwaiting = (pthread_descr) oldstatus;
|
||||
newstatus = (long) self;
|
||||
}
|
||||
} while(! compare_and_swap(&lock->status, oldstatus, newstatus,
|
||||
&lock->spinlock));
|
||||
if (oldstatus != 0) suspend(self);
|
||||
}
|
||||
|
||||
int __pthread_trylock(struct _pthread_fastlock * lock)
|
||||
{
|
||||
long oldstatus;
|
||||
|
||||
do {
|
||||
oldstatus = lock->status;
|
||||
if (oldstatus != 0) return EBUSY;
|
||||
} while(! compare_and_swap(&lock->status, 0, 1, &lock->spinlock));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __pthread_unlock(struct _pthread_fastlock * lock)
|
||||
{
|
||||
long oldstatus;
|
||||
pthread_descr thr, * ptr, * maxptr;
|
||||
int maxprio;
|
||||
|
||||
again:
|
||||
oldstatus = lock->status;
|
||||
if (oldstatus == 1) {
|
||||
/* No threads are waiting for this lock */
|
||||
if (! compare_and_swap(&lock->status, 1, 0, &lock->spinlock)) goto again;
|
||||
return;
|
||||
}
|
||||
/* Find thread in waiting queue with maximal priority */
|
||||
ptr = (pthread_descr *) &lock->status;
|
||||
thr = (pthread_descr) oldstatus;
|
||||
maxprio = 0;
|
||||
maxptr = ptr;
|
||||
while (thr != (pthread_descr) 1) {
|
||||
if (thr->p_priority >= maxprio) {
|
||||
maxptr = ptr;
|
||||
maxprio = thr->p_priority;
|
||||
}
|
||||
ptr = &(thr->p_nextwaiting);
|
||||
thr = *ptr;
|
||||
}
|
||||
/* Remove max prio thread from waiting list. */
|
||||
if (maxptr == (pthread_descr *) &lock->status) {
|
||||
/* If max prio thread is at head, remove it with compare-and-swap
|
||||
to guard against concurrent lock operation */
|
||||
thr = (pthread_descr) oldstatus;
|
||||
if (! compare_and_swap(&lock->status,
|
||||
oldstatus, (long)(thr->p_nextwaiting),
|
||||
&lock->spinlock))
|
||||
goto again;
|
||||
} else {
|
||||
/* No risk of concurrent access, remove max prio thread normally */
|
||||
thr = *maxptr;
|
||||
*maxptr = thr->p_nextwaiting;
|
||||
}
|
||||
/* Wake up the selected waiting thread */
|
||||
thr->p_nextwaiting = NULL;
|
||||
restart(thr);
|
||||
}
|
||||
|
||||
/* Compare-and-swap emulation with a spinlock */
|
||||
|
||||
#ifdef TEST_FOR_COMPARE_AND_SWAP
|
||||
int __pthread_has_cas = 0;
|
||||
#endif
|
||||
|
||||
#ifndef HAS_COMPARE_AND_SWAP
|
||||
|
||||
static void __pthread_acquire(int * spinlock);
|
||||
|
||||
int __pthread_compare_and_swap(long * ptr, long oldval, long newval,
|
||||
int * spinlock)
|
||||
{
|
||||
int res;
|
||||
if (testandset(spinlock)) __pthread_acquire(spinlock);
|
||||
if (*ptr == oldval) {
|
||||
*ptr = newval; res = 1;
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
*spinlock = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* This function is called if the inlined test-and-set
|
||||
in __pthread_compare_and_swap() failed */
|
||||
|
||||
/* The retry strategy is as follows:
|
||||
- We test and set the spinlock MAX_SPIN_COUNT times, calling
|
||||
@@ -40,7 +155,7 @@
|
||||
- When nanosleep() returns, we try again, doing MAX_SPIN_COUNT
|
||||
sched_yield(), then sleeping again if needed. */
|
||||
|
||||
void __pthread_acquire(int * spinlock)
|
||||
static void __pthread_acquire(int * spinlock)
|
||||
{
|
||||
int cnt = 0;
|
||||
struct timespec tm;
|
||||
@@ -57,3 +172,5 @@ void __pthread_acquire(int * spinlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user