1996-01-22 00:23:58 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
2007-01-12 07:26:21 +00:00
|
|
|
* 3. Neither the name of the author nor the names of any co-contributors
|
1996-01-22 00:23:58 +00:00
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
1999-03-23 05:07:56 +00:00
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
1996-01-22 00:23:58 +00:00
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
1999-08-28 00:22:10 +00:00
|
|
|
* $FreeBSD$
|
1996-01-22 00:23:58 +00:00
|
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <errno.h>
|
1998-06-09 23:21:05 +00:00
|
|
|
#include <string.h>
|
1999-03-23 05:07:56 +00:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/queue.h>
|
1996-01-22 00:23:58 +00:00
|
|
|
#include <pthread.h>
|
|
|
|
#include "pthread_private.h"
|
|
|
|
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
#if defined(_PTHREADS_INVARIANTS)
|
|
|
|
#define _MUTEX_INIT_LINK(m) do { \
|
|
|
|
(m)->m_qe.tqe_prev = NULL; \
|
|
|
|
(m)->m_qe.tqe_next = NULL; \
|
|
|
|
} while (0)
|
|
|
|
#define _MUTEX_ASSERT_IS_OWNED(m) do { \
|
|
|
|
if ((m)->m_qe.tqe_prev == NULL) \
|
|
|
|
PANIC("mutex is not on list"); \
|
|
|
|
} while (0)
|
|
|
|
#define _MUTEX_ASSERT_NOT_OWNED(m) do { \
|
|
|
|
if (((m)->m_qe.tqe_prev != NULL) || \
|
|
|
|
((m)->m_qe.tqe_next != NULL)) \
|
|
|
|
PANIC("mutex is on list"); \
|
|
|
|
} while (0)
|
|
|
|
#else
|
|
|
|
#define _MUTEX_INIT_LINK(m)
|
|
|
|
#define _MUTEX_ASSERT_IS_OWNED(m)
|
|
|
|
#define _MUTEX_ASSERT_NOT_OWNED(m)
|
|
|
|
#endif
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Prototypes
|
|
|
|
*/
|
|
|
|
static inline int mutex_self_trylock(pthread_mutex_t);
|
|
|
|
static inline int mutex_self_lock(pthread_mutex_t);
|
|
|
|
static inline int mutex_unlock_common(pthread_mutex_t *, int);
|
|
|
|
static void mutex_priority_adjust(pthread_mutex_t);
|
|
|
|
static void mutex_rescan_owned (pthread_t, pthread_mutex_t);
|
|
|
|
static inline pthread_t mutex_queue_deq(pthread_mutex_t);
|
|
|
|
static inline void mutex_queue_remove(pthread_mutex_t, pthread_t);
|
|
|
|
static inline void mutex_queue_enq(pthread_mutex_t, pthread_t);
|
|
|
|
|
|
|
|
|
1998-08-02 17:04:25 +00:00
|
|
|
static spinlock_t static_init_lock = _SPINLOCK_INITIALIZER;
|
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
static struct pthread_mutex_attr static_mutex_attr =
|
|
|
|
PTHREAD_MUTEXATTR_STATIC_INITIALIZER;
|
|
|
|
static pthread_mutexattr_t static_mattr = &static_mutex_attr;
|
|
|
|
|
|
|
|
/* Single underscore versions provided for libc internal usage: */
|
2001-04-10 04:19:21 +00:00
|
|
|
__weak_reference(__pthread_mutex_trylock, pthread_mutex_trylock);
|
|
|
|
__weak_reference(__pthread_mutex_lock, pthread_mutex_lock);
|
2001-01-24 13:03:38 +00:00
|
|
|
|
|
|
|
/* No difference between libc and application usage of these: */
|
2001-04-10 04:19:21 +00:00
|
|
|
__weak_reference(_pthread_mutex_init, pthread_mutex_init);
|
|
|
|
__weak_reference(_pthread_mutex_destroy, pthread_mutex_destroy);
|
|
|
|
__weak_reference(_pthread_mutex_unlock, pthread_mutex_unlock);
|
2001-01-24 13:03:38 +00:00
|
|
|
|
|
|
|
|
2002-11-13 18:13:26 +00:00
|
|
|
/*
|
|
|
|
* Reinitialize a private mutex; this is only used for internal mutexes.
|
|
|
|
*/
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
int
|
|
|
|
_mutex_reinit(pthread_mutex_t * mutex)
|
|
|
|
{
|
2000-10-13 22:12:32 +00:00
|
|
|
int ret = 0;
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
|
|
|
|
if (mutex == NULL)
|
|
|
|
ret = EINVAL;
|
|
|
|
else if (*mutex == NULL)
|
2002-11-13 18:13:26 +00:00
|
|
|
ret = _pthread_mutex_init(mutex, NULL);
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* Initialize the mutex structure:
|
|
|
|
*/
|
|
|
|
(*mutex)->m_type = PTHREAD_MUTEX_DEFAULT;
|
|
|
|
(*mutex)->m_protocol = PTHREAD_PRIO_NONE;
|
|
|
|
TAILQ_INIT(&(*mutex)->m_queue);
|
|
|
|
(*mutex)->m_owner = NULL;
|
|
|
|
(*mutex)->m_data.m_count = 0;
|
2002-11-13 18:13:26 +00:00
|
|
|
(*mutex)->m_flags |= MUTEX_FLAGS_INITED | MUTEX_FLAGS_PRIVATE;
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
(*mutex)->m_refcount = 0;
|
|
|
|
(*mutex)->m_prio = 0;
|
|
|
|
(*mutex)->m_saved_prio = 0;
|
|
|
|
_MUTEX_INIT_LINK(*mutex);
|
|
|
|
memset(&(*mutex)->lock, 0, sizeof((*mutex)->lock));
|
|
|
|
}
|
|
|
|
return (ret);
|
|
|
|
}
|
1999-03-23 05:07:56 +00:00
|
|
|
|
1996-01-22 00:23:58 +00:00
|
|
|
int
|
2001-01-24 13:03:38 +00:00
|
|
|
_pthread_mutex_init(pthread_mutex_t * mutex,
|
1996-01-22 00:23:58 +00:00
|
|
|
const pthread_mutexattr_t * mutex_attr)
|
|
|
|
{
|
1999-03-23 05:07:56 +00:00
|
|
|
enum pthread_mutextype type;
|
|
|
|
int protocol;
|
|
|
|
int ceiling;
|
2001-02-26 01:06:52 +00:00
|
|
|
int flags;
|
1996-08-20 08:22:01 +00:00
|
|
|
pthread_mutex_t pmutex;
|
2000-10-13 22:12:32 +00:00
|
|
|
int ret = 0;
|
1996-01-22 00:23:58 +00:00
|
|
|
|
1999-03-23 05:07:56 +00:00
|
|
|
if (mutex == NULL)
|
1997-11-25 01:29:16 +00:00
|
|
|
ret = EINVAL;
|
1998-04-29 09:59:34 +00:00
|
|
|
|
1999-03-23 05:07:56 +00:00
|
|
|
/* Check if default mutex attributes: */
|
2002-11-13 18:13:26 +00:00
|
|
|
if (mutex_attr == NULL || *mutex_attr == NULL) {
|
1999-03-23 05:07:56 +00:00
|
|
|
/* Default to a (error checking) POSIX mutex: */
|
|
|
|
type = PTHREAD_MUTEX_ERRORCHECK;
|
|
|
|
protocol = PTHREAD_PRIO_NONE;
|
|
|
|
ceiling = PTHREAD_MAX_PRIORITY;
|
2001-02-26 01:06:52 +00:00
|
|
|
flags = 0;
|
1999-03-23 05:07:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check mutex type: */
|
|
|
|
else if (((*mutex_attr)->m_type < PTHREAD_MUTEX_ERRORCHECK) ||
|
2005-08-19 21:31:42 +00:00
|
|
|
((*mutex_attr)->m_type >= PTHREAD_MUTEX_TYPE_MAX))
|
1999-03-23 05:07:56 +00:00
|
|
|
/* Return an invalid argument error: */
|
|
|
|
ret = EINVAL;
|
|
|
|
|
|
|
|
/* Check mutex protocol: */
|
|
|
|
else if (((*mutex_attr)->m_protocol < PTHREAD_PRIO_NONE) ||
|
|
|
|
((*mutex_attr)->m_protocol > PTHREAD_MUTEX_RECURSIVE))
|
|
|
|
/* Return an invalid argument error: */
|
|
|
|
ret = EINVAL;
|
|
|
|
|
|
|
|
else {
|
|
|
|
/* Use the requested mutex type and protocol: */
|
|
|
|
type = (*mutex_attr)->m_type;
|
|
|
|
protocol = (*mutex_attr)->m_protocol;
|
|
|
|
ceiling = (*mutex_attr)->m_ceiling;
|
2001-02-26 01:06:52 +00:00
|
|
|
flags = (*mutex_attr)->m_flags;
|
1999-03-23 05:07:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check no errors so far: */
|
|
|
|
if (ret == 0) {
|
|
|
|
if ((pmutex = (pthread_mutex_t)
|
|
|
|
malloc(sizeof(struct pthread_mutex))) == NULL)
|
|
|
|
ret = ENOMEM;
|
|
|
|
else {
|
2001-02-26 01:06:52 +00:00
|
|
|
/* Set the mutex flags: */
|
|
|
|
pmutex->m_flags = flags;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/* Process according to mutex type: */
|
|
|
|
switch (type) {
|
|
|
|
/* case PTHREAD_MUTEX_DEFAULT: */
|
|
|
|
case PTHREAD_MUTEX_ERRORCHECK:
|
|
|
|
case PTHREAD_MUTEX_NORMAL:
|
|
|
|
/* Nothing to do here. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Single UNIX Spec 2 recursive mutex: */
|
|
|
|
case PTHREAD_MUTEX_RECURSIVE:
|
|
|
|
/* Reset the mutex count: */
|
|
|
|
pmutex->m_data.m_count = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Trap invalid mutex types: */
|
|
|
|
default:
|
|
|
|
/* Return an invalid argument error: */
|
|
|
|
ret = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ret == 0) {
|
|
|
|
/* Initialise the rest of the mutex: */
|
|
|
|
TAILQ_INIT(&pmutex->m_queue);
|
|
|
|
pmutex->m_flags |= MUTEX_FLAGS_INITED;
|
|
|
|
pmutex->m_owner = NULL;
|
|
|
|
pmutex->m_type = type;
|
|
|
|
pmutex->m_protocol = protocol;
|
|
|
|
pmutex->m_refcount = 0;
|
|
|
|
if (protocol == PTHREAD_PRIO_PROTECT)
|
|
|
|
pmutex->m_prio = ceiling;
|
|
|
|
else
|
|
|
|
pmutex->m_prio = 0;
|
|
|
|
pmutex->m_saved_prio = 0;
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
_MUTEX_INIT_LINK(pmutex);
|
1999-03-23 05:07:56 +00:00
|
|
|
memset(&pmutex->lock, 0, sizeof(pmutex->lock));
|
|
|
|
*mutex = pmutex;
|
|
|
|
} else {
|
|
|
|
free(pmutex);
|
|
|
|
*mutex = NULL;
|
1996-08-20 08:22:01 +00:00
|
|
|
}
|
1996-01-22 00:23:58 +00:00
|
|
|
}
|
|
|
|
}
|
1996-08-20 08:22:01 +00:00
|
|
|
/* Return the completion status: */
|
2002-11-13 18:13:26 +00:00
|
|
|
return (ret);
|
1996-08-20 08:22:01 +00:00
|
|
|
}
|
1996-01-22 00:23:58 +00:00
|
|
|
|
1996-08-20 08:22:01 +00:00
|
|
|
int
|
2001-01-24 13:03:38 +00:00
|
|
|
_pthread_mutex_destroy(pthread_mutex_t * mutex)
|
1996-08-20 08:22:01 +00:00
|
|
|
{
|
2000-10-13 22:12:32 +00:00
|
|
|
int ret = 0;
|
1996-01-22 00:23:58 +00:00
|
|
|
|
1998-04-29 09:59:34 +00:00
|
|
|
if (mutex == NULL || *mutex == NULL)
|
1997-11-25 01:29:16 +00:00
|
|
|
ret = EINVAL;
|
1998-04-29 09:59:34 +00:00
|
|
|
else {
|
|
|
|
/* Lock the mutex structure: */
|
1998-06-09 23:21:05 +00:00
|
|
|
_SPINLOCK(&(*mutex)->lock);
|
1998-04-29 09:59:34 +00:00
|
|
|
|
|
|
|
/*
|
1999-03-23 05:07:56 +00:00
|
|
|
* Check to see if this mutex is in use:
|
1998-04-29 09:59:34 +00:00
|
|
|
*/
|
1999-03-23 05:07:56 +00:00
|
|
|
if (((*mutex)->m_owner != NULL) ||
|
|
|
|
(TAILQ_FIRST(&(*mutex)->m_queue) != NULL) ||
|
|
|
|
((*mutex)->m_refcount != 0)) {
|
|
|
|
ret = EBUSY;
|
1998-04-29 09:59:34 +00:00
|
|
|
|
1999-03-23 05:07:56 +00:00
|
|
|
/* Unlock the mutex structure: */
|
|
|
|
_SPINUNLOCK(&(*mutex)->lock);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* Free the memory allocated for the mutex
|
|
|
|
* structure:
|
|
|
|
*/
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
_MUTEX_ASSERT_NOT_OWNED(*mutex);
|
1999-03-23 05:07:56 +00:00
|
|
|
free(*mutex);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Leave the caller's pointer NULL now that
|
|
|
|
* the mutex has been destroyed:
|
|
|
|
*/
|
|
|
|
*mutex = NULL;
|
|
|
|
}
|
1996-01-22 00:23:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the completion status: */
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
1998-08-02 17:04:25 +00:00
|
|
|
static int
|
2000-01-12 09:28:58 +00:00
|
|
|
init_static(pthread_mutex_t *mutex)
|
1998-08-02 17:04:25 +00:00
|
|
|
{
|
2000-10-13 22:12:32 +00:00
|
|
|
int ret;
|
1998-08-02 17:04:25 +00:00
|
|
|
|
|
|
|
_SPINLOCK(&static_init_lock);
|
|
|
|
|
1998-08-02 23:07:25 +00:00
|
|
|
if (*mutex == NULL)
|
2002-10-31 18:17:58 +00:00
|
|
|
ret = _pthread_mutex_init(mutex, NULL);
|
1998-08-02 17:04:25 +00:00
|
|
|
else
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
_SPINUNLOCK(&static_init_lock);
|
|
|
|
|
2002-11-13 18:13:26 +00:00
|
|
|
return (ret);
|
1998-08-02 17:04:25 +00:00
|
|
|
}
|
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
static int
|
|
|
|
init_static_private(pthread_mutex_t *mutex)
|
1996-01-22 00:23:58 +00:00
|
|
|
{
|
2001-01-24 13:03:38 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
_SPINLOCK(&static_init_lock);
|
|
|
|
|
|
|
|
if (*mutex == NULL)
|
2002-10-31 18:17:58 +00:00
|
|
|
ret = _pthread_mutex_init(mutex, &static_mattr);
|
2001-01-24 13:03:38 +00:00
|
|
|
else
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
_SPINUNLOCK(&static_init_lock);
|
|
|
|
|
2002-11-13 18:13:26 +00:00
|
|
|
return (ret);
|
2001-01-24 13:03:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
mutex_trylock_common(pthread_mutex_t *mutex)
|
|
|
|
{
|
|
|
|
struct pthread *curthread = _get_curthread();
|
2000-10-13 22:12:32 +00:00
|
|
|
int ret = 0;
|
1996-01-22 00:23:58 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
PTHREAD_ASSERT((mutex != NULL) && (*mutex != NULL),
|
|
|
|
"Uninitialized mutex in pthread_mutex_trylock_basic");
|
1998-04-04 11:33:01 +00:00
|
|
|
|
|
|
|
/*
|
2001-01-24 13:03:38 +00:00
|
|
|
* Defer signals to protect the scheduling queues from
|
|
|
|
* access by the signal handler:
|
1998-04-04 11:33:01 +00:00
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
_thread_kern_sig_defer();
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
/* Lock the mutex structure: */
|
|
|
|
_SPINLOCK(&(*mutex)->lock);
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
/*
|
|
|
|
* If the mutex was statically allocated, properly
|
|
|
|
* initialize the tail queue.
|
|
|
|
*/
|
|
|
|
if (((*mutex)->m_flags & MUTEX_FLAGS_INITED) == 0) {
|
|
|
|
TAILQ_INIT(&(*mutex)->m_queue);
|
|
|
|
_MUTEX_INIT_LINK(*mutex);
|
|
|
|
(*mutex)->m_flags |= MUTEX_FLAGS_INITED;
|
|
|
|
}
|
1999-05-23 10:55:33 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
/* Process according to mutex type: */
|
|
|
|
switch ((*mutex)->m_protocol) {
|
|
|
|
/* Default POSIX mutex: */
|
|
|
|
case PTHREAD_PRIO_NONE:
|
|
|
|
/* Check if this mutex is not locked: */
|
|
|
|
if ((*mutex)->m_owner == NULL) {
|
|
|
|
/* Lock the mutex for the running thread: */
|
|
|
|
(*mutex)->m_owner = curthread;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
/* Add to the list of owned mutexes: */
|
|
|
|
_MUTEX_ASSERT_NOT_OWNED(*mutex);
|
|
|
|
TAILQ_INSERT_TAIL(&curthread->mutexq,
|
|
|
|
(*mutex), m_qe);
|
|
|
|
} else if ((*mutex)->m_owner == curthread)
|
|
|
|
ret = mutex_self_trylock(*mutex);
|
|
|
|
else
|
|
|
|
/* Return a busy error: */
|
|
|
|
ret = EBUSY;
|
|
|
|
break;
|
1996-08-20 08:22:01 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
/* POSIX priority inheritence mutex: */
|
|
|
|
case PTHREAD_PRIO_INHERIT:
|
|
|
|
/* Check if this mutex is not locked: */
|
|
|
|
if ((*mutex)->m_owner == NULL) {
|
|
|
|
/* Lock the mutex for the running thread: */
|
|
|
|
(*mutex)->m_owner = curthread;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
/* Track number of priority mutexes owned: */
|
|
|
|
curthread->priority_mutex_count++;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
/*
|
|
|
|
* The mutex takes on the attributes of the
|
|
|
|
* running thread when there are no waiters.
|
|
|
|
*/
|
|
|
|
(*mutex)->m_prio = curthread->active_priority;
|
|
|
|
(*mutex)->m_saved_prio =
|
|
|
|
curthread->inherited_priority;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
/* Add to the list of owned mutexes: */
|
|
|
|
_MUTEX_ASSERT_NOT_OWNED(*mutex);
|
|
|
|
TAILQ_INSERT_TAIL(&curthread->mutexq,
|
|
|
|
(*mutex), m_qe);
|
|
|
|
} else if ((*mutex)->m_owner == curthread)
|
|
|
|
ret = mutex_self_trylock(*mutex);
|
|
|
|
else
|
|
|
|
/* Return a busy error: */
|
|
|
|
ret = EBUSY;
|
|
|
|
break;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
/* POSIX priority protection mutex: */
|
|
|
|
case PTHREAD_PRIO_PROTECT:
|
|
|
|
/* Check for a priority ceiling violation: */
|
|
|
|
if (curthread->active_priority > (*mutex)->m_prio)
|
|
|
|
ret = EINVAL;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
/* Check if this mutex is not locked: */
|
|
|
|
else if ((*mutex)->m_owner == NULL) {
|
|
|
|
/* Lock the mutex for the running thread: */
|
|
|
|
(*mutex)->m_owner = curthread;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
/* Track number of priority mutexes owned: */
|
|
|
|
curthread->priority_mutex_count++;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
/*
|
|
|
|
* The running thread inherits the ceiling
|
|
|
|
* priority of the mutex and executes at that
|
|
|
|
* priority.
|
|
|
|
*/
|
|
|
|
curthread->active_priority = (*mutex)->m_prio;
|
|
|
|
(*mutex)->m_saved_prio =
|
|
|
|
curthread->inherited_priority;
|
|
|
|
curthread->inherited_priority =
|
|
|
|
(*mutex)->m_prio;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
/* Add to the list of owned mutexes: */
|
|
|
|
_MUTEX_ASSERT_NOT_OWNED(*mutex);
|
|
|
|
TAILQ_INSERT_TAIL(&curthread->mutexq,
|
|
|
|
(*mutex), m_qe);
|
|
|
|
} else if ((*mutex)->m_owner == curthread)
|
|
|
|
ret = mutex_self_trylock(*mutex);
|
|
|
|
else
|
|
|
|
/* Return a busy error: */
|
|
|
|
ret = EBUSY;
|
|
|
|
break;
|
1996-01-22 00:23:58 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
/* Trap invalid mutex types: */
|
|
|
|
default:
|
|
|
|
/* Return an invalid argument error: */
|
|
|
|
ret = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
1996-01-22 00:23:58 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
/* Unlock the mutex structure: */
|
|
|
|
_SPINUNLOCK(&(*mutex)->lock);
|
1999-03-23 05:07:56 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
/*
|
|
|
|
* Undefer and handle pending signals, yielding if
|
|
|
|
* necessary:
|
|
|
|
*/
|
|
|
|
_thread_kern_sig_undefer();
|
1996-01-22 00:23:58 +00:00
|
|
|
|
|
|
|
/* Return the completion status: */
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2001-01-24 13:03:38 +00:00
|
|
|
__pthread_mutex_trylock(pthread_mutex_t *mutex)
|
1996-01-22 00:23:58 +00:00
|
|
|
{
|
2000-10-13 22:12:32 +00:00
|
|
|
int ret = 0;
|
1996-01-22 00:23:58 +00:00
|
|
|
|
1998-04-04 11:33:01 +00:00
|
|
|
if (mutex == NULL)
|
2001-01-24 13:03:38 +00:00
|
|
|
ret = EINVAL;
|
1998-04-04 11:33:01 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the mutex is statically initialized, perform the dynamic
|
|
|
|
* initialization:
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
else if ((*mutex != NULL) || (ret = init_static(mutex)) == 0)
|
|
|
|
ret = mutex_trylock_common(mutex);
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
_pthread_mutex_trylock(pthread_mutex_t *mutex)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (mutex == NULL)
|
|
|
|
ret = EINVAL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the mutex is statically initialized, perform the dynamic
|
|
|
|
* initialization marking the mutex private (delete safe):
|
|
|
|
*/
|
|
|
|
else if ((*mutex != NULL) || (ret = init_static_private(mutex)) == 0)
|
|
|
|
ret = mutex_trylock_common(mutex);
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
mutex_lock_common(pthread_mutex_t * mutex)
|
|
|
|
{
|
|
|
|
struct pthread *curthread = _get_curthread();
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
PTHREAD_ASSERT((mutex != NULL) && (*mutex != NULL),
|
|
|
|
"Uninitialized mutex in pthread_mutex_trylock_basic");
|
2000-11-09 05:08:26 +00:00
|
|
|
|
|
|
|
/* Reset the interrupted flag: */
|
2001-01-24 13:03:38 +00:00
|
|
|
curthread->interrupted = 0;
|
2000-11-09 05:08:26 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Enter a loop waiting to become the mutex owner. We need a
|
|
|
|
* loop in case the waiting thread is interrupted by a signal
|
|
|
|
* to execute a signal handler. It is not (currently) possible
|
|
|
|
* to remain in the waiting queue while running a handler.
|
|
|
|
* Instead, the thread is interrupted and backed out of the
|
|
|
|
* waiting queue prior to executing the signal handler.
|
|
|
|
*/
|
2000-11-20 13:12:44 +00:00
|
|
|
do {
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
/*
|
|
|
|
* Defer signals to protect the scheduling queues from
|
|
|
|
* access by the signal handler:
|
|
|
|
*/
|
|
|
|
_thread_kern_sig_defer();
|
|
|
|
|
|
|
|
/* Lock the mutex structure: */
|
|
|
|
_SPINLOCK(&(*mutex)->lock);
|
|
|
|
|
1999-05-23 10:55:33 +00:00
|
|
|
/*
|
|
|
|
* If the mutex was statically allocated, properly
|
|
|
|
* initialize the tail queue.
|
|
|
|
*/
|
|
|
|
if (((*mutex)->m_flags & MUTEX_FLAGS_INITED) == 0) {
|
|
|
|
TAILQ_INIT(&(*mutex)->m_queue);
|
|
|
|
(*mutex)->m_flags |= MUTEX_FLAGS_INITED;
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
_MUTEX_INIT_LINK(*mutex);
|
1999-05-23 10:55:33 +00:00
|
|
|
}
|
|
|
|
|
1996-08-20 08:22:01 +00:00
|
|
|
/* Process according to mutex type: */
|
1999-03-23 05:07:56 +00:00
|
|
|
switch ((*mutex)->m_protocol) {
|
|
|
|
/* Default POSIX mutex: */
|
|
|
|
case PTHREAD_PRIO_NONE:
|
|
|
|
if ((*mutex)->m_owner == NULL) {
|
|
|
|
/* Lock the mutex for this thread: */
|
2001-01-24 13:03:38 +00:00
|
|
|
(*mutex)->m_owner = curthread;
|
1996-08-20 08:22:01 +00:00
|
|
|
|
1999-03-23 05:07:56 +00:00
|
|
|
/* Add to the list of owned mutexes: */
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
_MUTEX_ASSERT_NOT_OWNED(*mutex);
|
2001-01-24 13:03:38 +00:00
|
|
|
TAILQ_INSERT_TAIL(&curthread->mutexq,
|
1999-03-23 05:07:56 +00:00
|
|
|
(*mutex), m_qe);
|
1996-08-20 08:22:01 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
} else if ((*mutex)->m_owner == curthread)
|
1999-03-23 05:07:56 +00:00
|
|
|
ret = mutex_self_lock(*mutex);
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* Join the queue of threads waiting to lock
|
|
|
|
* the mutex:
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
mutex_queue_enq(*mutex, curthread);
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Keep a pointer to the mutex this thread
|
|
|
|
* is waiting on:
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
curthread->data.mutex = *mutex;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Unlock the mutex structure and schedule the
|
|
|
|
* next thread:
|
|
|
|
*/
|
|
|
|
_thread_kern_sched_state_unlock(PS_MUTEX_WAIT,
|
|
|
|
&(*mutex)->lock, __FILE__, __LINE__);
|
|
|
|
|
|
|
|
/* Lock the mutex structure again: */
|
|
|
|
_SPINLOCK(&(*mutex)->lock);
|
1996-01-22 00:23:58 +00:00
|
|
|
}
|
1996-08-20 08:22:01 +00:00
|
|
|
break;
|
1996-01-22 00:23:58 +00:00
|
|
|
|
1999-03-23 05:07:56 +00:00
|
|
|
/* POSIX priority inheritence mutex: */
|
|
|
|
case PTHREAD_PRIO_INHERIT:
|
|
|
|
/* Check if this mutex is not locked: */
|
|
|
|
if ((*mutex)->m_owner == NULL) {
|
|
|
|
/* Lock the mutex for this thread: */
|
2001-01-24 13:03:38 +00:00
|
|
|
(*mutex)->m_owner = curthread;
|
1996-08-20 08:22:01 +00:00
|
|
|
|
1999-03-23 05:07:56 +00:00
|
|
|
/* Track number of priority mutexes owned: */
|
2001-01-24 13:03:38 +00:00
|
|
|
curthread->priority_mutex_count++;
|
1996-08-20 08:22:01 +00:00
|
|
|
|
1999-03-23 05:07:56 +00:00
|
|
|
/*
|
|
|
|
* The mutex takes on attributes of the
|
|
|
|
* running thread when there are no waiters.
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
(*mutex)->m_prio = curthread->active_priority;
|
1999-03-23 05:07:56 +00:00
|
|
|
(*mutex)->m_saved_prio =
|
2001-01-24 13:03:38 +00:00
|
|
|
curthread->inherited_priority;
|
|
|
|
curthread->inherited_priority =
|
1999-03-23 05:07:56 +00:00
|
|
|
(*mutex)->m_prio;
|
|
|
|
|
|
|
|
/* Add to the list of owned mutexes: */
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
_MUTEX_ASSERT_NOT_OWNED(*mutex);
|
2001-01-24 13:03:38 +00:00
|
|
|
TAILQ_INSERT_TAIL(&curthread->mutexq,
|
1999-03-23 05:07:56 +00:00
|
|
|
(*mutex), m_qe);
|
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
} else if ((*mutex)->m_owner == curthread)
|
1999-03-23 05:07:56 +00:00
|
|
|
ret = mutex_self_lock(*mutex);
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* Join the queue of threads waiting to lock
|
|
|
|
* the mutex:
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
mutex_queue_enq(*mutex, curthread);
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Keep a pointer to the mutex this thread
|
|
|
|
* is waiting on:
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
curthread->data.mutex = *mutex;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
if (curthread->active_priority >
|
1999-03-23 05:07:56 +00:00
|
|
|
(*mutex)->m_prio)
|
|
|
|
/* Adjust priorities: */
|
|
|
|
mutex_priority_adjust(*mutex);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unlock the mutex structure and schedule the
|
|
|
|
* next thread:
|
|
|
|
*/
|
|
|
|
_thread_kern_sched_state_unlock(PS_MUTEX_WAIT,
|
|
|
|
&(*mutex)->lock, __FILE__, __LINE__);
|
|
|
|
|
|
|
|
/* Lock the mutex structure again: */
|
|
|
|
_SPINLOCK(&(*mutex)->lock);
|
1996-01-22 00:23:58 +00:00
|
|
|
}
|
1999-03-23 05:07:56 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* POSIX priority protection mutex: */
|
|
|
|
case PTHREAD_PRIO_PROTECT:
|
|
|
|
/* Check for a priority ceiling violation: */
|
2001-01-24 13:03:38 +00:00
|
|
|
if (curthread->active_priority > (*mutex)->m_prio)
|
1999-03-23 05:07:56 +00:00
|
|
|
ret = EINVAL;
|
|
|
|
|
|
|
|
/* Check if this mutex is not locked: */
|
|
|
|
else if ((*mutex)->m_owner == NULL) {
|
|
|
|
/*
|
|
|
|
* Lock the mutex for the running
|
|
|
|
* thread:
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
(*mutex)->m_owner = curthread;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/* Track number of priority mutexes owned: */
|
2001-01-24 13:03:38 +00:00
|
|
|
curthread->priority_mutex_count++;
|
1996-01-22 00:23:58 +00:00
|
|
|
|
1999-03-23 05:07:56 +00:00
|
|
|
/*
|
|
|
|
* The running thread inherits the ceiling
|
|
|
|
* priority of the mutex and executes at that
|
|
|
|
* priority:
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
curthread->active_priority = (*mutex)->m_prio;
|
1999-03-23 05:07:56 +00:00
|
|
|
(*mutex)->m_saved_prio =
|
2001-01-24 13:03:38 +00:00
|
|
|
curthread->inherited_priority;
|
|
|
|
curthread->inherited_priority =
|
1999-03-23 05:07:56 +00:00
|
|
|
(*mutex)->m_prio;
|
|
|
|
|
|
|
|
/* Add to the list of owned mutexes: */
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
_MUTEX_ASSERT_NOT_OWNED(*mutex);
|
2001-01-24 13:03:38 +00:00
|
|
|
TAILQ_INSERT_TAIL(&curthread->mutexq,
|
1999-03-23 05:07:56 +00:00
|
|
|
(*mutex), m_qe);
|
2001-01-24 13:03:38 +00:00
|
|
|
} else if ((*mutex)->m_owner == curthread)
|
1999-03-23 05:07:56 +00:00
|
|
|
ret = mutex_self_lock(*mutex);
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* Join the queue of threads waiting to lock
|
|
|
|
* the mutex:
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
mutex_queue_enq(*mutex, curthread);
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Keep a pointer to the mutex this thread
|
|
|
|
* is waiting on:
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
curthread->data.mutex = *mutex;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/* Clear any previous error: */
|
2001-01-24 13:03:38 +00:00
|
|
|
curthread->error = 0;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Unlock the mutex structure and schedule the
|
|
|
|
* next thread:
|
|
|
|
*/
|
|
|
|
_thread_kern_sched_state_unlock(PS_MUTEX_WAIT,
|
|
|
|
&(*mutex)->lock, __FILE__, __LINE__);
|
|
|
|
|
|
|
|
/* Lock the mutex structure again: */
|
|
|
|
_SPINLOCK(&(*mutex)->lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The threads priority may have changed while
|
|
|
|
* waiting for the mutex causing a ceiling
|
|
|
|
* violation.
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
ret = curthread->error;
|
|
|
|
curthread->error = 0;
|
1999-03-23 05:07:56 +00:00
|
|
|
}
|
1996-08-20 08:22:01 +00:00
|
|
|
break;
|
1996-01-22 00:23:58 +00:00
|
|
|
|
|
|
|
/* Trap invalid mutex types: */
|
1996-08-20 08:22:01 +00:00
|
|
|
default:
|
|
|
|
/* Return an invalid argument error: */
|
1997-11-25 01:29:16 +00:00
|
|
|
ret = EINVAL;
|
1996-08-20 08:22:01 +00:00
|
|
|
break;
|
|
|
|
}
|
1996-01-22 00:23:58 +00:00
|
|
|
|
1999-11-28 05:38:13 +00:00
|
|
|
/*
|
|
|
|
* Check to see if this thread was interrupted and
|
|
|
|
* is still in the mutex queue of waiting threads:
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
if (curthread->interrupted != 0)
|
|
|
|
mutex_queue_remove(*mutex, curthread);
|
1999-11-28 05:38:13 +00:00
|
|
|
|
1998-04-29 09:59:34 +00:00
|
|
|
/* Unlock the mutex structure: */
|
1998-06-09 23:21:05 +00:00
|
|
|
_SPINUNLOCK(&(*mutex)->lock);
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/*
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
* Undefer and handle pending signals, yielding if
|
|
|
|
* necessary:
|
1999-03-23 05:07:56 +00:00
|
|
|
*/
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
_thread_kern_sig_undefer();
|
2001-01-24 13:03:38 +00:00
|
|
|
} while (((*mutex)->m_owner != curthread) && (ret == 0) &&
|
|
|
|
(curthread->interrupted == 0));
|
1996-01-22 00:23:58 +00:00
|
|
|
|
2001-01-24 13:03:38 +00:00
|
|
|
if (curthread->interrupted != 0 &&
|
|
|
|
curthread->continuation != NULL)
|
|
|
|
curthread->continuation((void *) curthread);
|
2000-11-09 05:08:26 +00:00
|
|
|
|
1996-01-22 00:23:58 +00:00
|
|
|
/* Return the completion status: */
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2001-01-24 13:03:38 +00:00
|
|
|
__pthread_mutex_lock(pthread_mutex_t *mutex)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (_thread_initial == NULL)
|
|
|
|
_thread_init();
|
|
|
|
|
|
|
|
if (mutex == NULL)
|
|
|
|
ret = EINVAL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the mutex is statically initialized, perform the dynamic
|
|
|
|
* initialization:
|
|
|
|
*/
|
|
|
|
else if ((*mutex != NULL) || ((ret = init_static(mutex)) == 0))
|
|
|
|
ret = mutex_lock_common(mutex);
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
_pthread_mutex_lock(pthread_mutex_t *mutex)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (_thread_initial == NULL)
|
|
|
|
_thread_init();
|
|
|
|
|
|
|
|
if (mutex == NULL)
|
|
|
|
ret = EINVAL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the mutex is statically initialized, perform the dynamic
|
|
|
|
* initialization marking it private (delete safe):
|
|
|
|
*/
|
|
|
|
else if ((*mutex != NULL) || ((ret = init_static_private(mutex)) == 0))
|
|
|
|
ret = mutex_lock_common(mutex);
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
_pthread_mutex_unlock(pthread_mutex_t * mutex)
|
1996-01-22 00:23:58 +00:00
|
|
|
{
|
1999-03-23 05:07:56 +00:00
|
|
|
return (mutex_unlock_common(mutex, /* add reference */ 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
_mutex_cv_unlock(pthread_mutex_t * mutex)
|
|
|
|
{
|
|
|
|
return (mutex_unlock_common(mutex, /* add reference */ 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
_mutex_cv_lock(pthread_mutex_t * mutex)
|
|
|
|
{
|
2000-10-13 22:12:32 +00:00
|
|
|
int ret;
|
2002-11-13 18:13:26 +00:00
|
|
|
if ((ret = _pthread_mutex_lock(mutex)) == 0)
|
1999-03-23 05:07:56 +00:00
|
|
|
(*mutex)->m_refcount--;
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
mutex_self_trylock(pthread_mutex_t mutex)
|
|
|
|
{
|
2000-10-13 22:12:32 +00:00
|
|
|
int ret = 0;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
switch (mutex->m_type) {
|
|
|
|
|
|
|
|
/* case PTHREAD_MUTEX_DEFAULT: */
|
|
|
|
case PTHREAD_MUTEX_ERRORCHECK:
|
|
|
|
case PTHREAD_MUTEX_NORMAL:
|
|
|
|
/*
|
|
|
|
* POSIX specifies that mutexes should return EDEADLK if a
|
|
|
|
* recursive lock is detected.
|
|
|
|
*/
|
|
|
|
ret = EBUSY;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PTHREAD_MUTEX_RECURSIVE:
|
|
|
|
/* Increment the lock count: */
|
|
|
|
mutex->m_data.m_count++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* Trap invalid mutex types; */
|
|
|
|
ret = EINVAL;
|
|
|
|
}
|
|
|
|
|
2002-11-13 18:13:26 +00:00
|
|
|
return (ret);
|
1999-03-23 05:07:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
mutex_self_lock(pthread_mutex_t mutex)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
switch (mutex->m_type) {
|
|
|
|
/* case PTHREAD_MUTEX_DEFAULT: */
|
|
|
|
case PTHREAD_MUTEX_ERRORCHECK:
|
|
|
|
/*
|
|
|
|
* POSIX specifies that mutexes should return EDEADLK if a
|
|
|
|
* recursive lock is detected.
|
|
|
|
*/
|
|
|
|
ret = EDEADLK;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PTHREAD_MUTEX_NORMAL:
|
|
|
|
/*
|
|
|
|
* What SS2 define as a 'normal' mutex. Intentionally
|
|
|
|
* deadlock on attempts to get a lock you already own.
|
|
|
|
*/
|
|
|
|
_thread_kern_sched_state_unlock(PS_DEADLOCK,
|
|
|
|
&mutex->lock, __FILE__, __LINE__);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PTHREAD_MUTEX_RECURSIVE:
|
|
|
|
/* Increment the lock count: */
|
|
|
|
mutex->m_data.m_count++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* Trap invalid mutex types; */
|
|
|
|
ret = EINVAL;
|
|
|
|
}
|
|
|
|
|
2002-11-13 18:13:26 +00:00
|
|
|
return (ret);
|
1999-03-23 05:07:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
mutex_unlock_common(pthread_mutex_t * mutex, int add_reference)
|
|
|
|
{
|
2001-01-24 13:03:38 +00:00
|
|
|
struct pthread *curthread = _get_curthread();
|
2000-10-13 22:12:32 +00:00
|
|
|
int ret = 0;
|
1996-01-22 00:23:58 +00:00
|
|
|
|
1996-08-20 08:22:01 +00:00
|
|
|
if (mutex == NULL || *mutex == NULL) {
|
1997-11-25 01:29:16 +00:00
|
|
|
ret = EINVAL;
|
1996-08-20 08:22:01 +00:00
|
|
|
} else {
|
1999-03-23 05:07:56 +00:00
|
|
|
/*
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
* Defer signals to protect the scheduling queues from
|
|
|
|
* access by the signal handler:
|
1999-03-23 05:07:56 +00:00
|
|
|
*/
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
_thread_kern_sig_defer();
|
1999-03-23 05:07:56 +00:00
|
|
|
|
1998-04-29 09:59:34 +00:00
|
|
|
/* Lock the mutex structure: */
|
1998-06-09 23:21:05 +00:00
|
|
|
_SPINLOCK(&(*mutex)->lock);
|
1996-01-22 00:23:58 +00:00
|
|
|
|
1996-08-20 08:22:01 +00:00
|
|
|
/* Process according to mutex type: */
|
1999-03-23 05:07:56 +00:00
|
|
|
switch ((*mutex)->m_protocol) {
|
|
|
|
/* Default POSIX mutex: */
|
|
|
|
case PTHREAD_PRIO_NONE:
|
|
|
|
/*
|
|
|
|
* Check if the running thread is not the owner of the
|
|
|
|
* mutex:
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
if ((*mutex)->m_owner != curthread) {
|
1999-03-23 05:07:56 +00:00
|
|
|
/*
|
|
|
|
* Return an invalid argument error for no
|
|
|
|
* owner and a permission error otherwise:
|
|
|
|
*/
|
|
|
|
ret = (*mutex)->m_owner == NULL ? EINVAL : EPERM;
|
1996-08-20 08:22:01 +00:00
|
|
|
}
|
1999-03-23 05:07:56 +00:00
|
|
|
else if (((*mutex)->m_type == PTHREAD_MUTEX_RECURSIVE) &&
|
2000-08-13 01:30:36 +00:00
|
|
|
((*mutex)->m_data.m_count > 0)) {
|
1999-03-23 05:07:56 +00:00
|
|
|
/* Decrement the count: */
|
|
|
|
(*mutex)->m_data.m_count--;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Clear the count in case this is recursive
|
|
|
|
* mutex.
|
|
|
|
*/
|
|
|
|
(*mutex)->m_data.m_count = 0;
|
|
|
|
|
|
|
|
/* Remove the mutex from the threads queue. */
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
_MUTEX_ASSERT_IS_OWNED(*mutex);
|
1999-03-23 05:07:56 +00:00
|
|
|
TAILQ_REMOVE(&(*mutex)->m_owner->mutexq,
|
|
|
|
(*mutex), m_qe);
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
_MUTEX_INIT_LINK(*mutex);
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the next thread from the queue of
|
|
|
|
* threads waiting on the mutex:
|
|
|
|
*/
|
|
|
|
if (((*mutex)->m_owner =
|
|
|
|
mutex_queue_deq(*mutex)) != NULL) {
|
2002-05-24 04:32:28 +00:00
|
|
|
/* Make the new owner runnable: */
|
|
|
|
PTHREAD_NEW_STATE((*mutex)->m_owner,
|
|
|
|
PS_RUNNING);
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Add the mutex to the threads list of
|
|
|
|
* owned mutexes:
|
|
|
|
*/
|
|
|
|
TAILQ_INSERT_TAIL(&(*mutex)->m_owner->mutexq,
|
|
|
|
(*mutex), m_qe);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The owner is no longer waiting for
|
|
|
|
* this mutex:
|
|
|
|
*/
|
|
|
|
(*mutex)->m_owner->data.mutex = NULL;
|
1999-03-23 05:07:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* POSIX priority inheritence mutex: */
|
|
|
|
case PTHREAD_PRIO_INHERIT:
|
1996-08-20 08:22:01 +00:00
|
|
|
/*
|
1999-03-23 05:07:56 +00:00
|
|
|
* Check if the running thread is not the owner of the
|
|
|
|
* mutex:
|
1996-08-20 08:22:01 +00:00
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
if ((*mutex)->m_owner != curthread) {
|
1999-03-23 05:07:56 +00:00
|
|
|
/*
|
|
|
|
* Return an invalid argument error for no
|
|
|
|
* owner and a permission error otherwise:
|
|
|
|
*/
|
|
|
|
ret = (*mutex)->m_owner == NULL ? EINVAL : EPERM;
|
|
|
|
}
|
|
|
|
else if (((*mutex)->m_type == PTHREAD_MUTEX_RECURSIVE) &&
|
2000-08-13 01:30:36 +00:00
|
|
|
((*mutex)->m_data.m_count > 0)) {
|
1999-03-23 05:07:56 +00:00
|
|
|
/* Decrement the count: */
|
|
|
|
(*mutex)->m_data.m_count--;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Clear the count in case this is recursive
|
|
|
|
* mutex.
|
|
|
|
*/
|
|
|
|
(*mutex)->m_data.m_count = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restore the threads inherited priority and
|
|
|
|
* recompute the active priority (being careful
|
|
|
|
* not to override changes in the threads base
|
|
|
|
* priority subsequent to locking the mutex).
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
curthread->inherited_priority =
|
1999-03-23 05:07:56 +00:00
|
|
|
(*mutex)->m_saved_prio;
|
2001-01-24 13:03:38 +00:00
|
|
|
curthread->active_priority =
|
|
|
|
MAX(curthread->inherited_priority,
|
|
|
|
curthread->base_priority);
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This thread now owns one less priority mutex.
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
curthread->priority_mutex_count--;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/* Remove the mutex from the threads queue. */
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
_MUTEX_ASSERT_IS_OWNED(*mutex);
|
1999-03-23 05:07:56 +00:00
|
|
|
TAILQ_REMOVE(&(*mutex)->m_owner->mutexq,
|
|
|
|
(*mutex), m_qe);
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
_MUTEX_INIT_LINK(*mutex);
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the next thread from the queue of threads
|
|
|
|
* waiting on the mutex:
|
|
|
|
*/
|
|
|
|
if (((*mutex)->m_owner =
|
|
|
|
mutex_queue_deq(*mutex)) == NULL)
|
|
|
|
/* This mutex has no priority. */
|
|
|
|
(*mutex)->m_prio = 0;
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* Track number of priority mutexes owned:
|
|
|
|
*/
|
|
|
|
(*mutex)->m_owner->priority_mutex_count++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add the mutex to the threads list
|
|
|
|
* of owned mutexes:
|
|
|
|
*/
|
|
|
|
TAILQ_INSERT_TAIL(&(*mutex)->m_owner->mutexq,
|
|
|
|
(*mutex), m_qe);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The owner is no longer waiting for
|
|
|
|
* this mutex:
|
|
|
|
*/
|
|
|
|
(*mutex)->m_owner->data.mutex = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the priority of the mutex. Since
|
|
|
|
* our waiting threads are in descending
|
|
|
|
* priority order, the priority of the
|
|
|
|
* mutex becomes the active priority of
|
|
|
|
* the thread we just dequeued.
|
|
|
|
*/
|
|
|
|
(*mutex)->m_prio =
|
|
|
|
(*mutex)->m_owner->active_priority;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Save the owning threads inherited
|
|
|
|
* priority:
|
|
|
|
*/
|
|
|
|
(*mutex)->m_saved_prio =
|
|
|
|
(*mutex)->m_owner->inherited_priority;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The owning threads inherited priority
|
|
|
|
* now becomes his active priority (the
|
|
|
|
* priority of the mutex).
|
|
|
|
*/
|
|
|
|
(*mutex)->m_owner->inherited_priority =
|
|
|
|
(*mutex)->m_prio;
|
|
|
|
|
|
|
|
/*
|
2002-05-24 04:32:28 +00:00
|
|
|
* Make the new owner runnable:
|
1999-03-23 05:07:56 +00:00
|
|
|
*/
|
2002-05-24 04:32:28 +00:00
|
|
|
PTHREAD_NEW_STATE((*mutex)->m_owner,
|
|
|
|
PS_RUNNING);
|
1999-03-23 05:07:56 +00:00
|
|
|
}
|
1996-08-20 08:22:01 +00:00
|
|
|
}
|
|
|
|
break;
|
1996-01-22 00:23:58 +00:00
|
|
|
|
1999-03-23 05:07:56 +00:00
|
|
|
/* POSIX priority ceiling mutex: */
|
|
|
|
case PTHREAD_PRIO_PROTECT:
|
|
|
|
/*
|
|
|
|
* Check if the running thread is not the owner of the
|
|
|
|
* mutex:
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
if ((*mutex)->m_owner != curthread) {
|
1999-03-23 05:07:56 +00:00
|
|
|
/*
|
|
|
|
* Return an invalid argument error for no
|
|
|
|
* owner and a permission error otherwise:
|
|
|
|
*/
|
|
|
|
ret = (*mutex)->m_owner == NULL ? EINVAL : EPERM;
|
1996-08-20 08:22:01 +00:00
|
|
|
}
|
1999-03-23 05:07:56 +00:00
|
|
|
else if (((*mutex)->m_type == PTHREAD_MUTEX_RECURSIVE) &&
|
2000-08-13 01:30:36 +00:00
|
|
|
((*mutex)->m_data.m_count > 0)) {
|
1996-08-20 08:22:01 +00:00
|
|
|
/* Decrement the count: */
|
|
|
|
(*mutex)->m_data.m_count--;
|
1998-11-28 23:52:58 +00:00
|
|
|
} else {
|
1999-03-23 05:07:56 +00:00
|
|
|
/*
|
|
|
|
* Clear the count in case this is recursive
|
|
|
|
* mutex.
|
|
|
|
*/
|
1998-11-28 23:52:58 +00:00
|
|
|
(*mutex)->m_data.m_count = 0;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
1998-11-28 23:52:58 +00:00
|
|
|
/*
|
1999-03-23 05:07:56 +00:00
|
|
|
* Restore the threads inherited priority and
|
|
|
|
* recompute the active priority (being careful
|
|
|
|
* not to override changes in the threads base
|
|
|
|
* priority subsequent to locking the mutex).
|
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
curthread->inherited_priority =
|
1999-03-23 05:07:56 +00:00
|
|
|
(*mutex)->m_saved_prio;
|
2001-01-24 13:03:38 +00:00
|
|
|
curthread->active_priority =
|
|
|
|
MAX(curthread->inherited_priority,
|
|
|
|
curthread->base_priority);
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This thread now owns one less priority mutex.
|
1998-11-28 23:52:58 +00:00
|
|
|
*/
|
2001-01-24 13:03:38 +00:00
|
|
|
curthread->priority_mutex_count--;
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/* Remove the mutex from the threads queue. */
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
_MUTEX_ASSERT_IS_OWNED(*mutex);
|
1999-03-23 05:07:56 +00:00
|
|
|
TAILQ_REMOVE(&(*mutex)->m_owner->mutexq,
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
(*mutex), m_qe);
|
|
|
|
_MUTEX_INIT_LINK(*mutex);
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Enter a loop to find a waiting thread whose
|
|
|
|
* active priority will not cause a ceiling
|
|
|
|
* violation:
|
|
|
|
*/
|
|
|
|
while ((((*mutex)->m_owner =
|
|
|
|
mutex_queue_deq(*mutex)) != NULL) &&
|
|
|
|
((*mutex)->m_owner->active_priority >
|
|
|
|
(*mutex)->m_prio)) {
|
|
|
|
/*
|
|
|
|
* Either the mutex ceiling priority
|
|
|
|
* been lowered and/or this threads
|
|
|
|
* priority has been raised subsequent
|
|
|
|
* to this thread being queued on the
|
|
|
|
* waiting list.
|
|
|
|
*/
|
|
|
|
(*mutex)->m_owner->error = EINVAL;
|
|
|
|
PTHREAD_NEW_STATE((*mutex)->m_owner,
|
|
|
|
PS_RUNNING);
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
/*
|
|
|
|
* The thread is no longer waiting for
|
|
|
|
* this mutex:
|
|
|
|
*/
|
|
|
|
(*mutex)->m_owner->data.mutex = NULL;
|
1999-03-23 05:07:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for a new owner: */
|
|
|
|
if ((*mutex)->m_owner != NULL) {
|
|
|
|
/*
|
|
|
|
* Track number of priority mutexes owned:
|
|
|
|
*/
|
|
|
|
(*mutex)->m_owner->priority_mutex_count++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add the mutex to the threads list
|
|
|
|
* of owned mutexes:
|
|
|
|
*/
|
|
|
|
TAILQ_INSERT_TAIL(&(*mutex)->m_owner->mutexq,
|
|
|
|
(*mutex), m_qe);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The owner is no longer waiting for
|
|
|
|
* this mutex:
|
|
|
|
*/
|
|
|
|
(*mutex)->m_owner->data.mutex = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Save the owning threads inherited
|
|
|
|
* priority:
|
|
|
|
*/
|
|
|
|
(*mutex)->m_saved_prio =
|
|
|
|
(*mutex)->m_owner->inherited_priority;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The owning thread inherits the
|
|
|
|
* ceiling priority of the mutex and
|
|
|
|
* executes at that priority:
|
|
|
|
*/
|
|
|
|
(*mutex)->m_owner->inherited_priority =
|
|
|
|
(*mutex)->m_prio;
|
|
|
|
(*mutex)->m_owner->active_priority =
|
|
|
|
(*mutex)->m_prio;
|
|
|
|
|
|
|
|
/*
|
2002-05-24 04:32:28 +00:00
|
|
|
* Make the new owner runnable:
|
1999-03-23 05:07:56 +00:00
|
|
|
*/
|
2002-05-24 04:32:28 +00:00
|
|
|
PTHREAD_NEW_STATE((*mutex)->m_owner,
|
|
|
|
PS_RUNNING);
|
1998-11-28 23:52:58 +00:00
|
|
|
}
|
1996-08-20 08:22:01 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Trap invalid mutex types: */
|
|
|
|
default:
|
1996-01-22 00:23:58 +00:00
|
|
|
/* Return an invalid argument error: */
|
1997-11-25 01:29:16 +00:00
|
|
|
ret = EINVAL;
|
1996-08-20 08:22:01 +00:00
|
|
|
break;
|
1996-01-22 00:23:58 +00:00
|
|
|
}
|
|
|
|
|
1999-03-23 05:07:56 +00:00
|
|
|
if ((ret == 0) && (add_reference != 0)) {
|
|
|
|
/* Increment the reference count: */
|
|
|
|
(*mutex)->m_refcount++;
|
|
|
|
}
|
|
|
|
|
1998-04-29 09:59:34 +00:00
|
|
|
/* Unlock the mutex structure: */
|
1998-06-09 23:21:05 +00:00
|
|
|
_SPINUNLOCK(&(*mutex)->lock);
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
/*
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
* Undefer and handle pending signals, yielding if
|
|
|
|
* necessary:
|
1999-03-23 05:07:56 +00:00
|
|
|
*/
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
_thread_kern_sig_undefer();
|
1996-01-22 00:23:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the completion status: */
|
|
|
|
return (ret);
|
|
|
|
}
|
1999-03-23 05:07:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
* This function is called when a change in base priority occurs for
|
|
|
|
* a thread that is holding or waiting for a priority protection or
|
|
|
|
* inheritence mutex. A change in a threads base priority can effect
|
|
|
|
* changes to active priorities of other threads and to the ordering
|
|
|
|
* of mutex locking by waiting threads.
|
1999-03-23 05:07:56 +00:00
|
|
|
*
|
|
|
|
* This must be called while thread scheduling is deferred.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
_mutex_notify_priochange(pthread_t pthread)
|
|
|
|
{
|
|
|
|
/* Adjust the priorites of any owned priority mutexes: */
|
|
|
|
if (pthread->priority_mutex_count > 0) {
|
|
|
|
/*
|
|
|
|
* Rescan the mutexes owned by this thread and correct
|
|
|
|
* their priorities to account for this threads change
|
|
|
|
* in priority. This has the side effect of changing
|
|
|
|
* the threads active priority.
|
|
|
|
*/
|
|
|
|
mutex_rescan_owned(pthread, /* rescan all owned */ NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this thread is waiting on a priority inheritence mutex,
|
|
|
|
* check for priority adjustments. A change in priority can
|
|
|
|
* also effect a ceiling violation(*) for a thread waiting on
|
|
|
|
* a priority protection mutex; we don't perform the check here
|
|
|
|
* as it is done in pthread_mutex_unlock.
|
|
|
|
*
|
|
|
|
* (*) It should be noted that a priority change to a thread
|
|
|
|
* _after_ taking and owning a priority ceiling mutex
|
|
|
|
* does not affect ownership of that mutex; the ceiling
|
|
|
|
* priority is only checked before mutex ownership occurs.
|
|
|
|
*/
|
|
|
|
if (pthread->state == PS_MUTEX_WAIT) {
|
|
|
|
/* Lock the mutex structure: */
|
|
|
|
_SPINLOCK(&pthread->data.mutex->lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check to make sure this thread is still in the same state
|
|
|
|
* (the spinlock above can yield the CPU to another thread):
|
|
|
|
*/
|
|
|
|
if (pthread->state == PS_MUTEX_WAIT) {
|
|
|
|
/*
|
|
|
|
* Remove and reinsert this thread into the list of
|
|
|
|
* waiting threads to preserve decreasing priority
|
|
|
|
* order.
|
|
|
|
*/
|
|
|
|
mutex_queue_remove(pthread->data.mutex, pthread);
|
|
|
|
mutex_queue_enq(pthread->data.mutex, pthread);
|
|
|
|
|
|
|
|
if (pthread->data.mutex->m_protocol ==
|
|
|
|
PTHREAD_PRIO_INHERIT) {
|
|
|
|
/* Adjust priorities: */
|
|
|
|
mutex_priority_adjust(pthread->data.mutex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unlock the mutex structure: */
|
|
|
|
_SPINUNLOCK(&pthread->data.mutex->lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called when a new thread is added to the mutex waiting queue or
|
|
|
|
* when a threads priority changes that is already in the mutex
|
|
|
|
* waiting queue.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
mutex_priority_adjust(pthread_mutex_t mutex)
|
|
|
|
{
|
|
|
|
pthread_t pthread_next, pthread = mutex->m_owner;
|
|
|
|
int temp_prio;
|
|
|
|
pthread_mutex_t m = mutex;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate the mutex priority as the maximum of the highest
|
|
|
|
* active priority of any waiting threads and the owning threads
|
|
|
|
* active priority(*).
|
|
|
|
*
|
|
|
|
* (*) Because the owning threads current active priority may
|
|
|
|
* reflect priority inherited from this mutex (and the mutex
|
|
|
|
* priority may have changed) we must recalculate the active
|
|
|
|
* priority based on the threads saved inherited priority
|
|
|
|
* and its base priority.
|
|
|
|
*/
|
|
|
|
pthread_next = TAILQ_FIRST(&m->m_queue); /* should never be NULL */
|
|
|
|
temp_prio = MAX(pthread_next->active_priority,
|
|
|
|
MAX(m->m_saved_prio, pthread->base_priority));
|
|
|
|
|
|
|
|
/* See if this mutex really needs adjusting: */
|
|
|
|
if (temp_prio == m->m_prio)
|
|
|
|
/* No need to propagate the priority: */
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Set new priority of the mutex: */
|
|
|
|
m->m_prio = temp_prio;
|
|
|
|
|
|
|
|
while (m != NULL) {
|
|
|
|
/*
|
|
|
|
* Save the threads priority before rescanning the
|
|
|
|
* owned mutexes:
|
|
|
|
*/
|
|
|
|
temp_prio = pthread->active_priority;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fix the priorities for all the mutexes this thread has
|
|
|
|
* locked since taking this mutex. This also has a
|
|
|
|
* potential side-effect of changing the threads priority.
|
|
|
|
*/
|
|
|
|
mutex_rescan_owned(pthread, m);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the thread is currently waiting on a mutex, check
|
|
|
|
* to see if the threads new priority has affected the
|
|
|
|
* priority of the mutex.
|
|
|
|
*/
|
|
|
|
if ((temp_prio != pthread->active_priority) &&
|
|
|
|
(pthread->state == PS_MUTEX_WAIT) &&
|
|
|
|
(pthread->data.mutex->m_protocol == PTHREAD_PRIO_INHERIT)) {
|
|
|
|
/* Grab the mutex this thread is waiting on: */
|
|
|
|
m = pthread->data.mutex;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The priority for this thread has changed. Remove
|
|
|
|
* and reinsert this thread into the list of waiting
|
|
|
|
* threads to preserve decreasing priority order.
|
|
|
|
*/
|
|
|
|
mutex_queue_remove(m, pthread);
|
|
|
|
mutex_queue_enq(m, pthread);
|
|
|
|
|
|
|
|
/* Grab the waiting thread with highest priority: */
|
|
|
|
pthread_next = TAILQ_FIRST(&m->m_queue);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate the mutex priority as the maximum of the
|
|
|
|
* highest active priority of any waiting threads and
|
|
|
|
* the owning threads active priority.
|
|
|
|
*/
|
|
|
|
temp_prio = MAX(pthread_next->active_priority,
|
|
|
|
MAX(m->m_saved_prio, m->m_owner->base_priority));
|
|
|
|
|
|
|
|
if (temp_prio != m->m_prio) {
|
|
|
|
/*
|
|
|
|
* The priority needs to be propagated to the
|
|
|
|
* mutex this thread is waiting on and up to
|
|
|
|
* the owner of that mutex.
|
|
|
|
*/
|
|
|
|
m->m_prio = temp_prio;
|
|
|
|
pthread = m->m_owner;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
/* We're done: */
|
|
|
|
m = NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
/* We're done: */
|
|
|
|
m = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2000-01-12 09:28:58 +00:00
|
|
|
mutex_rescan_owned(pthread_t pthread, pthread_mutex_t mutex)
|
1999-03-23 05:07:56 +00:00
|
|
|
{
|
|
|
|
int active_prio, inherited_prio;
|
|
|
|
pthread_mutex_t m;
|
|
|
|
pthread_t pthread_next;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start walking the mutexes the thread has taken since
|
|
|
|
* taking this mutex.
|
|
|
|
*/
|
|
|
|
if (mutex == NULL) {
|
|
|
|
/*
|
|
|
|
* A null mutex means start at the beginning of the owned
|
|
|
|
* mutex list.
|
|
|
|
*/
|
|
|
|
m = TAILQ_FIRST(&pthread->mutexq);
|
|
|
|
|
|
|
|
/* There is no inherited priority yet. */
|
|
|
|
inherited_prio = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* The caller wants to start after a specific mutex. It
|
|
|
|
* is assumed that this mutex is a priority inheritence
|
|
|
|
* mutex and that its priority has been correctly
|
|
|
|
* calculated.
|
|
|
|
*/
|
|
|
|
m = TAILQ_NEXT(mutex, m_qe);
|
|
|
|
|
|
|
|
/* Start inheriting priority from the specified mutex. */
|
|
|
|
inherited_prio = mutex->m_prio;
|
|
|
|
}
|
|
|
|
active_prio = MAX(inherited_prio, pthread->base_priority);
|
|
|
|
|
|
|
|
while (m != NULL) {
|
|
|
|
/*
|
|
|
|
* We only want to deal with priority inheritence
|
|
|
|
* mutexes. This might be optimized by only placing
|
|
|
|
* priority inheritence mutexes into the owned mutex
|
|
|
|
* list, but it may prove to be useful having all
|
|
|
|
* owned mutexes in this list. Consider a thread
|
|
|
|
* exiting while holding mutexes...
|
|
|
|
*/
|
|
|
|
if (m->m_protocol == PTHREAD_PRIO_INHERIT) {
|
|
|
|
/*
|
|
|
|
* Fix the owners saved (inherited) priority to
|
|
|
|
* reflect the priority of the previous mutex.
|
|
|
|
*/
|
|
|
|
m->m_saved_prio = inherited_prio;
|
|
|
|
|
|
|
|
if ((pthread_next = TAILQ_FIRST(&m->m_queue)) != NULL)
|
|
|
|
/* Recalculate the priority of the mutex: */
|
|
|
|
m->m_prio = MAX(active_prio,
|
|
|
|
pthread_next->active_priority);
|
|
|
|
else
|
|
|
|
m->m_prio = active_prio;
|
|
|
|
|
|
|
|
/* Recalculate new inherited and active priorities: */
|
|
|
|
inherited_prio = m->m_prio;
|
|
|
|
active_prio = MAX(m->m_prio, pthread->base_priority);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Advance to the next mutex owned by this thread: */
|
|
|
|
m = TAILQ_NEXT(m, m_qe);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fix the threads inherited priority and recalculate its
|
|
|
|
* active priority.
|
|
|
|
*/
|
|
|
|
pthread->inherited_priority = inherited_prio;
|
|
|
|
active_prio = MAX(inherited_prio, pthread->base_priority);
|
|
|
|
|
|
|
|
if (active_prio != pthread->active_priority) {
|
|
|
|
/*
|
|
|
|
* If this thread is in the priority queue, it must be
|
|
|
|
* removed and reinserted for its new priority.
|
|
|
|
*/
|
In the words of the author:
o The polling mechanism for I/O readiness was changed from
select() to poll(). In additon, a wrapped version of poll()
is now provided.
o The wrapped select routine now converts each fd_set to a
poll array so that the thread scheduler doesn't have to
perform a bitwise search for selected fds each time file
descriptors are polled for I/O readiness.
o The thread scheduler was modified to use a new queue (_workq)
for threads that need work. Threads waiting for I/O readiness
and spinblocks are added to the work queue in addition to the
waiting queue. This reduces the time spent forming/searching
the array of file descriptors being polled.
o The waiting queue (_waitingq) is now maintained in order of
thread wakeup time. This allows the thread scheduler to
find the nearest wakeup time by looking at the first thread
in the queue instead of searching the entire queue.
o Removed file descriptor locking for select/poll routines. An
application should not rely on the threads library for providing
this locking; if necessary, the application should use mutexes
to protect selecting/polling of file descriptors.
o Retrieve and use the kernel clock rate/resolution at startup
instead of hardcoding the clock resolution to 10 msec (tested
with kernel running at 1000 HZ).
o All queues have been changed to use queue.h macros. These
include the queues of all threads, dead threads, and threads
waiting for file descriptor locks.
o Added reinitialization of the GC mutex and condition variable
after a fork. Also prevented reallocation of the ready queue
after a fork.
o Prevented the wrapped close routine from closing the thread
kernel pipes.
o Initialized file descriptor table for stdio entries at thread
init.
o Provided additional flags to indicate to what queues threads
belong.
o Moved TAILQ initialization for statically allocated mutex and
condition variables to after the spinlock.
o Added dispatching of signals to pthread_kill. Removing the
dispatching of signals from thread activation broke sigsuspend
when pthread_kill was used to send a signal to a thread.
o Temporarily set the state of a thread to PS_SUSPENDED when it
is first created and placed in the list of threads so that it
will not be accidentally scheduled before becoming a member
of one of the scheduling queues.
o Change the signal handler to queue signals to the thread kernel
pipe if the scheduling queues are protected. When scheduling
queues are unprotected, signals are then dequeued and handled.
o Ensured that all installed signal handlers block the scheduling
signal and that the scheduling signal handler blocks all
other signals. This ensures that the signal handler is only
interruptible for and by non-scheduling signals. An atomic
lock is used to decide which instance of the signal handler
will handle pending signals.
o Removed _lock_thread_list and _unlock_thread_list as they are
no longer used to protect the thread list.
o Added missing RCS IDs to modified files.
o Added checks for appropriate queue membership and activity when
adding, removing, and searching the scheduling queues. These
checks add very little overhead and are enabled when compiled
with _PTHREADS_INVARIANTS defined. Suggested and implemented
by Tor Egge with some modification by me.
o Close a race condition in uthread_close. (Tor Egge)
o Protect the scheduling queues while modifying them in
pthread_cond_signal and _thread_fd_unlock. (Tor Egge)
o Ensure that when a thread gets a mutex, the mutex is on that
threads list of owned mutexes. (Tor Egge)
o Set the kernel-in-scheduler flag in _thread_kern_sched_state
and _thread_kern_sched_state_unlock to prevent a scheduling
signal from calling the scheduler again. (Tor Egge)
o Don't use TAILQ_FOREACH macro while searching the waiting
queue for threads in a sigwait state, because a change of
state destroys the TAILQ link. It is actually safe to do
so, though, because once a sigwaiting thread is found, the
loop ends and the function returns. (Tor Egge)
o When dispatching signals to threads, make the thread inherit
the signal deferral flag of the currently running thread.
(Tor Egge)
Submitted by: Daniel Eischen <eischen@vigrid.com> and
Tor Egge <Tor.Egge@fast.no>
1999-06-20 08:28:48 +00:00
|
|
|
if (pthread->flags & PTHREAD_FLAGS_IN_PRIOQ) {
|
1999-03-23 05:07:56 +00:00
|
|
|
/*
|
|
|
|
* Remove the thread from the priority queue
|
|
|
|
* before changing its priority:
|
|
|
|
*/
|
|
|
|
PTHREAD_PRIOQ_REMOVE(pthread);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* POSIX states that if the priority is being
|
|
|
|
* lowered, the thread must be inserted at the
|
|
|
|
* head of the queue for its priority if it owns
|
|
|
|
* any priority protection or inheritence mutexes.
|
|
|
|
*/
|
|
|
|
if ((active_prio < pthread->active_priority) &&
|
|
|
|
(pthread->priority_mutex_count > 0)) {
|
|
|
|
/* Set the new active priority. */
|
|
|
|
pthread->active_priority = active_prio;
|
|
|
|
|
|
|
|
PTHREAD_PRIOQ_INSERT_HEAD(pthread);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Set the new active priority. */
|
|
|
|
pthread->active_priority = active_prio;
|
|
|
|
|
|
|
|
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Set the new active priority. */
|
|
|
|
pthread->active_priority = active_prio;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-11-28 05:38:13 +00:00
|
|
|
void
|
|
|
|
_mutex_unlock_private(pthread_t pthread)
|
|
|
|
{
|
|
|
|
struct pthread_mutex *m, *m_next;
|
|
|
|
|
|
|
|
for (m = TAILQ_FIRST(&pthread->mutexq); m != NULL; m = m_next) {
|
|
|
|
m_next = TAILQ_NEXT(m, m_qe);
|
|
|
|
if ((m->m_flags & MUTEX_FLAGS_PRIVATE) != 0)
|
2002-11-13 18:13:26 +00:00
|
|
|
_pthread_mutex_unlock(&m);
|
1999-11-28 05:38:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-10-13 22:12:32 +00:00
|
|
|
void
|
|
|
|
_mutex_lock_backout(pthread_t pthread)
|
|
|
|
{
|
|
|
|
struct pthread_mutex *mutex;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Defer signals to protect the scheduling queues from
|
|
|
|
* access by the signal handler:
|
|
|
|
*/
|
|
|
|
_thread_kern_sig_defer();
|
2000-11-09 05:08:26 +00:00
|
|
|
if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0) {
|
2000-10-13 22:12:32 +00:00
|
|
|
mutex = pthread->data.mutex;
|
|
|
|
|
|
|
|
/* Lock the mutex structure: */
|
|
|
|
_SPINLOCK(&mutex->lock);
|
|
|
|
|
|
|
|
mutex_queue_remove(mutex, pthread);
|
|
|
|
|
|
|
|
/* This thread is no longer waiting for the mutex: */
|
2000-11-09 05:08:26 +00:00
|
|
|
pthread->data.mutex = NULL;
|
2000-10-13 22:12:32 +00:00
|
|
|
|
|
|
|
/* Unlock the mutex structure: */
|
|
|
|
_SPINUNLOCK(&mutex->lock);
|
|
|
|
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Undefer and handle pending signals, yielding if
|
|
|
|
* necessary:
|
|
|
|
*/
|
|
|
|
_thread_kern_sig_undefer();
|
|
|
|
}
|
|
|
|
|
1999-03-23 05:07:56 +00:00
|
|
|
/*
|
|
|
|
* Dequeue a waiting thread from the head of a mutex queue in descending
|
|
|
|
* priority order.
|
|
|
|
*/
|
|
|
|
static inline pthread_t
|
|
|
|
mutex_queue_deq(pthread_mutex_t mutex)
|
|
|
|
{
|
|
|
|
pthread_t pthread;
|
|
|
|
|
1999-11-28 05:38:13 +00:00
|
|
|
while ((pthread = TAILQ_FIRST(&mutex->m_queue)) != NULL) {
|
2000-10-13 22:12:32 +00:00
|
|
|
TAILQ_REMOVE(&mutex->m_queue, pthread, sqe);
|
1999-11-28 05:38:13 +00:00
|
|
|
pthread->flags &= ~PTHREAD_FLAGS_IN_MUTEXQ;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only exit the loop if the thread hasn't been
|
|
|
|
* cancelled.
|
|
|
|
*/
|
|
|
|
if (pthread->interrupted == 0)
|
|
|
|
break;
|
|
|
|
}
|
1999-03-23 05:07:56 +00:00
|
|
|
|
2002-11-13 18:13:26 +00:00
|
|
|
return (pthread);
|
1999-03-23 05:07:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove a waiting thread from a mutex queue in descending priority order.
|
|
|
|
*/
|
|
|
|
static inline void
|
|
|
|
mutex_queue_remove(pthread_mutex_t mutex, pthread_t pthread)
|
|
|
|
{
|
1999-11-28 05:38:13 +00:00
|
|
|
if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0) {
|
2000-10-13 22:12:32 +00:00
|
|
|
TAILQ_REMOVE(&mutex->m_queue, pthread, sqe);
|
1999-11-28 05:38:13 +00:00
|
|
|
pthread->flags &= ~PTHREAD_FLAGS_IN_MUTEXQ;
|
|
|
|
}
|
1999-03-23 05:07:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Enqueue a waiting thread to a queue in descending priority order.
|
|
|
|
*/
|
|
|
|
static inline void
|
|
|
|
mutex_queue_enq(pthread_mutex_t mutex, pthread_t pthread)
|
|
|
|
{
|
|
|
|
pthread_t tid = TAILQ_LAST(&mutex->m_queue, mutex_head);
|
|
|
|
|
2000-10-13 22:12:32 +00:00
|
|
|
PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread);
|
1999-03-23 05:07:56 +00:00
|
|
|
/*
|
|
|
|
* For the common case of all threads having equal priority,
|
|
|
|
* we perform a quick check against the priority of the thread
|
|
|
|
* at the tail of the queue.
|
|
|
|
*/
|
|
|
|
if ((tid == NULL) || (pthread->active_priority <= tid->active_priority))
|
2000-10-13 22:12:32 +00:00
|
|
|
TAILQ_INSERT_TAIL(&mutex->m_queue, pthread, sqe);
|
1999-03-23 05:07:56 +00:00
|
|
|
else {
|
|
|
|
tid = TAILQ_FIRST(&mutex->m_queue);
|
|
|
|
while (pthread->active_priority <= tid->active_priority)
|
2000-10-13 22:12:32 +00:00
|
|
|
tid = TAILQ_NEXT(tid, sqe);
|
|
|
|
TAILQ_INSERT_BEFORE(tid, pthread, sqe);
|
1999-03-23 05:07:56 +00:00
|
|
|
}
|
1999-11-28 05:38:13 +00:00
|
|
|
pthread->flags |= PTHREAD_FLAGS_IN_MUTEXQ;
|
1999-03-23 05:07:56 +00:00
|
|
|
}
|
|
|
|
|