Implement zero system call thread switching. Performance of

thread switches should be on par with that under scheduler
activations.

  o Timing is achieved through the use of a fixed interval
    timer (ITIMER_PROF) to count scheduling ticks instead
    of retrieving the time-of-day upon every thread switch
    and calculating elapsed real time.

  o Polling for I/O readiness is performed once for each
    scheduling tick instead of every thread switch.

  o The non-signal saving/restoring versions of setjmp/longjmp
    are used to save and restore thread contexts.  This may
    allow the removal of _THREAD_SAFE macros from setjmp()
    and longjmp() - needs more investigation.

Change signal handling so that signals are handled in the
context of the thread that is receiving the signal.  When
signals are dispatched to a thread, a special signal handling
frame is created on top of the target threads stack.  The
frame contains the threads saved state information and a new
context in which the thread can run.  The applications signal
handler is invoked through a wrapper routine that knows how
to restore the threads saved state and unwind to previous
frames.

Fix interruption of threads due to signals.  Some states
were being improperly interrupted while other states were
not being interrupted.  This should fix several PRs.

Signal handlers, which are invoked as a result of a process
signal (not by pthread_kill()), are now called with the
code (or siginfo_t if SA_SIGINFO was set in sa_flags) and
sigcontext_t as received from the process signal handler.

Modify the search for a thread to which a signal is delivered.
The search algorithm is now:

  o First thread found in sigwait() with signal in wait mask.
  o First thread found sigsuspend()'d on the signal.
  o Current thread if signal is unmasked.
  o First thread found with signal unmasked.

Collapse machine dependent support into macros defined in
pthread_private.h.  These should probably eventually be moved
into separate MD files.

Change the range of settable priorities to be compliant with
POSIX (0-31).  The threads library uses higher priorities
internally for real-time threads (not yet implemented) and
threads executing signal handlers.  Real-time threads and
threads running signal handlers add 64 and 32, respectively,
to a threads base priority.

Some other small changes and cleanups.

PR:		17757 18559 21943
Reviewed by:	jasone
This commit is contained in:
Daniel Eischen 2000-10-13 22:12:32 +00:00
parent a95b77ec78
commit fbeb36e4bf
77 changed files with 5351 additions and 3497 deletions

View file

@ -51,6 +51,7 @@
*/
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/time.h>
@ -59,14 +60,68 @@
#include <spinlock.h>
#include <pthread_np.h>
/*
* Define machine dependent macros to get and set the stack pointer
* from the supported contexts. Also define a macro to set the return
* address in a jmp_buf context.
*
* XXX - These need to be moved into architecture dependent support files.
*/
#if defined(__i386__)
#define GET_STACK_JB(jb) ((unsigned long)((jb)[0]._jb[2]))
#define GET_STACK_SJB(sjb) ((unsigned long)((sjb)[0]._sjb[2]))
#define GET_STACK_UC(ucp) ((unsigned long)((ucp)->uc_mcontext.mc_esp))
#define SET_STACK_JB(jb, stk) (jb)[0]._jb[2] = (int)(stk)
#define SET_STACK_SJB(sjb, stk) (sjb)[0]._sjb[2] = (int)(stk)
#define SET_STACK_UC(ucp, stk) (ucp)->uc_mcontext.mc_esp = (int)(stk)
#define FP_SAVE_UC(ucp) do { \
char *fdata; \
fdata = (char *) (ucp)->uc_mcontext.mc_fpregs; \
__asm__("fnsave %0": :"m"(*fdata)); \
} while (0)
#define FP_RESTORE_UC(ucp) do { \
char *fdata; \
fdata = (char *) (ucp)->uc_mcontext.mc_fpregs; \
__asm__("frstor %0": :"m"(*fdata)); \
} while (0)
#define SET_RETURN_ADDR_JB(jb, ra) (jb)[0]._jb[0] = (int)(ra)
#elif defined(__alpha__)
#include <machine/reg.h>
#define GET_STACK_JB(jb, stk) ((unsigned long)((jb)[0]._jb[R_SP + 4]))
#define GET_STACK_SJB(sjb, stk) ((unsigned long)((sjb)[0]._sjb[R_SP + 4]))
#define GET_STACK_UC(ucp, stk) ((ucp)->uc_mcontext.mc_regs[R_SP])
#define SET_STACK_JB(jb, stk) (jb)[0]._jb[R_SP + 4] = (long)(stk)
#define SET_STACK_SJB(sjb, stk) (sjb)[0]._sjb[R_SP + 4] = (long)(stk)
#define SET_STACK_UC(ucp, stk) (ucp)->uc_mcontext.mc_regs[R_SP] = (unsigned long)(stk)
#define FP_SAVE_UC(ucp)
#define FP_RESTORE_UC(ucp)
#define SET_RETURN_ADDR_JB(jb, ra) do { \
(jb)[0]._jb[2] = (long)(ra); \
(jb)[0]._jb[R_RA + 4] = 0; \
(jb)[0]._jb[R_T12 + 4] = (long)(ra); \
} while (0)
#else
#error "Don't recognize this architecture!"
#endif
/*
* Kernel fatal error handler macro.
*/
#define PANIC(string) _thread_exit(__FILE__,__LINE__,string)
/* Output debug messages like this: */
#define stdout_debug(_x) _thread_sys_write(1,_x,strlen(_x));
#define stderr_debug(_x) _thread_sys_write(2,_x,strlen(_x));
#define stdout_debug(args...) do { \
char buf[128]; \
snprintf(buf, sizeof(buf), ##args); \
_thread_sys_write(1, buf, strlen(buf)); \
} while (0)
#define stderr_debug(args...) do { \
char buf[128]; \
snprintf(buf, sizeof(buf), ##args); \
_thread_sys_write(2, buf, strlen(buf)); \
} while (0)
/*
@ -80,34 +135,13 @@
/*
* Waiting queue manipulation macros (using pqe link):
*/
#if defined(_PTHREADS_INVARIANTS)
#define PTHREAD_WAITQ_REMOVE(thrd) _waitq_remove(thrd)
#define PTHREAD_WAITQ_INSERT(thrd) _waitq_insert(thrd)
#if defined(_PTHREADS_INVARIANTS)
#define PTHREAD_WAITQ_CLEARACTIVE() _waitq_clearactive()
#define PTHREAD_WAITQ_SETACTIVE() _waitq_setactive()
#else
#define PTHREAD_WAITQ_REMOVE(thrd) do { \
TAILQ_REMOVE(&_waitingq,thrd,pqe); \
(thrd)->flags &= ~PTHREAD_FLAGS_IN_WAITQ; \
} while (0)
#define PTHREAD_WAITQ_INSERT(thrd) do { \
if ((thrd)->wakeup_time.tv_sec == -1) \
TAILQ_INSERT_TAIL(&_waitingq,thrd,pqe); \
else { \
pthread_t tid = TAILQ_FIRST(&_waitingq); \
while ((tid != NULL) && (tid->wakeup_time.tv_sec != -1) && \
((tid->wakeup_time.tv_sec < (thrd)->wakeup_time.tv_sec) || \
((tid->wakeup_time.tv_sec == (thrd)->wakeup_time.tv_sec) && \
(tid->wakeup_time.tv_nsec <= (thrd)->wakeup_time.tv_nsec)))) \
tid = TAILQ_NEXT(tid, pqe); \
if (tid == NULL) \
TAILQ_INSERT_TAIL(&_waitingq,thrd,pqe); \
else \
TAILQ_INSERT_BEFORE(tid,thrd,pqe); \
} \
(thrd)->flags |= PTHREAD_FLAGS_IN_WAITQ; \
} while (0)
#define PTHREAD_WAITQ_CLEARACTIVE()
#define PTHREAD_WAITQ_SETACTIVE()
#endif
@ -139,6 +173,14 @@
* called with preemption deferred (see thread_kern_sched_[un]defer).
*/
#if defined(_PTHREADS_INVARIANTS)
#include <assert.h>
#define PTHREAD_ASSERT(cond, msg) do { \
if (!(cond)) \
PANIC(msg); \
} while (0)
#define PTHREAD_ASSERT_NOT_IN_SYNCQ(thrd) \
PTHREAD_ASSERT((((thrd)->flags & PTHREAD_FLAGS_IN_SYNCQ) == 0), \
"Illegal call from signal handler");
#define PTHREAD_NEW_STATE(thrd, newstate) do { \
if (_thread_kern_new_state != 0) \
PANIC("Recursive PTHREAD_NEW_STATE"); \
@ -156,6 +198,8 @@
PTHREAD_SET_STATE(thrd, newstate); \
} while (0)
#else
#define PTHREAD_ASSERT(cond, msg)
#define PTHREAD_ASSERT_NOT_IN_SYNCQ(thrd)
#define PTHREAD_NEW_STATE(thrd, newstate) do { \
if ((thrd)->state != newstate) { \
if ((thrd)->state == PS_RUNNING) { \
@ -379,21 +423,52 @@ enum pthread_susp {
* almost entirely on this stack.
*/
#define PTHREAD_STACK_INITIAL 0x100000
/* Address immediately beyond the beginning of the initial thread stack. */
#define PTHREAD_DEFAULT_PRIORITY 64
#define PTHREAD_MAX_PRIORITY 126
#define PTHREAD_MIN_PRIORITY 0
#define _POSIX_THREAD_ATTR_STACKSIZE
/*
* Clock resolution in nanoseconds.
* Define the different priority ranges. All applications have thread
* priorities constrained within 0-31. The threads library raises the
* priority when delivering signals in order to ensure that signal
* delivery happens (from the POSIX spec) "as soon as possible".
* In the future, the threads library will also be able to map specific
* threads into real-time (cooperating) processes or kernel threads.
* The RT and SIGNAL priorities will be used internally and added to
* thread base priorities so that the scheduling queue can handle both
* normal and RT priority threads with and without signal handling.
*
* The approach taken is that, within each class, signal delivery
* always has priority over thread execution.
*/
#define CLOCK_RES_NSEC 10000000
#define PTHREAD_DEFAULT_PRIORITY 15
#define PTHREAD_MIN_PRIORITY 0
#define PTHREAD_MAX_PRIORITY 31 /* 0x1F */
#define PTHREAD_SIGNAL_PRIORITY 32 /* 0x20 */
#define PTHREAD_RT_PRIORITY 64 /* 0x40 */
#define PTHREAD_FIRST_PRIORITY PTHREAD_MIN_PRIORITY
#define PTHREAD_LAST_PRIORITY \
(PTHREAD_MAX_PRIORITY + PTHREAD_SIGNAL_PRIORITY + PTHREAD_RT_PRIORITY)
#define PTHREAD_BASE_PRIORITY(prio) ((prio) & PTHREAD_MAX_PRIORITY)
/*
* Clock resolution in microseconds.
*/
#define CLOCK_RES_USEC 10000
/*
* Time slice period in microseconds.
*/
#define TIMESLICE_USEC 100000
#define TIMESLICE_USEC 20000
/*
* Define a thread-safe macro to get the current time of day
* which is updated at regular intervals by the scheduling signal
* handler.
*/
#define GET_CURRENT_TOD(tv) \
do { \
tv.tv_sec = _sched_tod.tv_sec; \
tv.tv_usec = _sched_tod.tv_usec; \
} while (tv.tv_sec != _sched_tod.tv_sec)
struct pthread_key {
spinlock_t lock;
@ -487,8 +562,10 @@ union pthread_wait_data {
short branch; /* Line number, for debugging. */
char *fname; /* Source file name for debugging.*/
} fd;
struct pthread_poll_data * poll_data;
FILE *fp;
struct pthread_poll_data *poll_data;
spinlock_t *spinlock;
struct pthread *thread;
};
/*
@ -497,6 +574,83 @@ union pthread_wait_data {
*/
typedef void (*thread_continuation_t) (void *);
struct pthread_state_data {
int psd_interrupted;
sigset_t psd_sigmask;
enum pthread_state psd_state;
int psd_flags;
struct timespec psd_wakeup_time;
union pthread_wait_data psd_wait_data;
/* XXX - What about thread->timeout and/or thread->error? */
};
/*
* Normally thread contexts are stored as jmp_bufs via _setjmp()/_longjmp(),
* but they may also be sigjmp_buf and ucontext_t. When a thread is
* interrupted by a signal, it's context is saved as a ucontext_t. An
* application is also free to use [_]longjmp()/[_]siglongjmp() to jump
* between contexts within the same thread. Future support will also
* include setcontext()/getcontext().
*
* Define an enumerated type that can identify the 4 different context
* types.
*/
typedef enum {
CTX_JB_NOSIG, /* context is jmp_buf without saved sigset */
CTX_JB, /* context is jmp_buf (with saved sigset) */
CTX_SJB, /* context is sigjmp_buf (with saved sigset) */
CTX_UC /* context is ucontext_t (with saved sigset) */
} thread_context_t;
/*
* There are 2 basic contexts that a frame may contain at any
* one time:
*
* o ctx - The context that the thread should return to after normal
* completion of the signal handler.
* o sig_jb - The context just before the signal handler is invoked.
* Attempts at abnormal returns from user supplied signal handlers
* will return back to the signal context to perform any necessary
* cleanup.
*/
struct pthread_signal_frame {
/*
* This stores the threads state before the signal.
*/
struct pthread_state_data saved_state;
/* Beginning (bottom) of threads stack frame for this signal. */
unsigned long stackp;
/*
* Threads return context; ctxtype identifies the type of context.
* For signal frame 0, these point to the context storage area
* within the pthread structure. When handling signals (frame > 0),
* these point to a context storage area that is allocated off the
* threads stack.
*/
union {
jmp_buf jb;
sigjmp_buf sigjb;
ucontext_t uc;
} ctx;
thread_context_t ctxtype;
int longjmp_val;
/* Threads "jump out of signal handler" destination frame. */
int dst_frame;
/*
* Used to return back to the signal handling frame in case
* the application tries to change contexts from the handler.
*/
jmp_buf *sig_jb;
int signo; /* signal, arg 1 to sighandler */
int sig_has_args; /* use signal args if true */
};
/*
* Thread structure.
*/
@ -530,54 +684,19 @@ struct pthread {
void *stack;
struct pthread_attr attr;
#if (defined(__FreeBSD__) || defined(__NetBSD__)) && defined(__i386__)
/*
* Saved floating point registers on systems where they are not
* saved in the signal context.
*/
char saved_fp[108];
#endif
/*
* Saved signal context used in call to sigreturn by
* _thread_kern_sched if sig_saved is TRUE.
*/
ucontext_t saved_sigcontext;
/*
* Saved jump buffer used in call to longjmp by _thread_kern_sched
* if sig_saved is FALSE.
*/
jmp_buf saved_jmp_buf;
jmp_buf *sighandler_jmp_buf;
/*
* Saved jump buffers for use when doing nested [sig|_]longjmp()s, as
* when doing signal delivery.
*/
union {
jmp_buf jmp;
sigjmp_buf sigjmp;
} nested_jmp;
int longjmp_val;
#define JMPFLAGS_NONE 0x00
#define JMPFLAGS_LONGJMP 0x01
#define JMPFLAGS__LONGJMP 0x02
#define JMPFLAGS_SIGLONGJMP 0x04
#define JMPFLAGS_DEFERRED 0x08
int jmpflags;
/*
* TRUE if the last state saved was a signal context. FALSE if the
* last state saved was a jump buffer.
*/
int sig_saved;
/*
* Used for tracking delivery of nested signal handlers.
* Signal frame 0 is used for normal context (when no
* signal handlers are active for the thread). Frame
* 1 is used as the context for the first signal, and
* frames 2 .. NSIG-1 are used when additional signals
* arrive interrupting already active signal handlers.
*/
int signal_nest_level;
struct pthread_signal_frame *sigframes[NSIG];
struct pthread_signal_frame sigframe0;
struct pthread_signal_frame *curframe;
int sigframe_count;
int sigframe_done;
/*
* Cancelability flags - the lower 2 bits are used by cancel
@ -588,7 +707,7 @@ struct pthread {
#define PTHREAD_CANCEL_NEEDED 0x0010
int cancelflags;
enum pthread_susp suspended;
enum pthread_susp suspended;
thread_continuation_t continuation;
@ -597,16 +716,16 @@ struct pthread {
*/
sigset_t sigmask;
sigset_t sigpend;
int check_pending;
/* Thread state: */
enum pthread_state state;
enum pthread_state oldstate;
/* Time that this thread was last made active. */
struct timeval last_active;
/* Scheduling clock when this thread was last made active. */
long last_active;
/* Time that this thread was last made inactive. */
struct timeval last_inactive;
/* Scheduling clock when this thread was last made inactive. */
long last_inactive;
/*
* Number of microseconds accumulated by this thread when
@ -614,12 +733,6 @@ struct pthread {
*/
long slice_usec;
/*
* Incremental priority accumulated by thread while it is ready to
* run but is denied being run.
*/
int inc_prio;
/*
* Time to wake up thread. This is used for sleeping threads and
* for any operation which may time out (such as select).
@ -640,8 +753,7 @@ struct pthread {
/*
* The current thread can belong to only one scheduling queue at
* a time (ready or waiting queue). It can also belong to (only)
* one of:
* a time (ready or waiting queue). It can also belong to:
*
* o A queue of threads waiting for a mutex
* o A queue of threads waiting for a condition variable
@ -651,15 +763,21 @@ struct pthread {
* o A queue of threads needing work done by the kernel thread
* (waiting for a spinlock or file I/O)
*
* It is possible for a thread to belong to more than one of the
* above queues if it is handling a signal. A thread may only
* enter a mutex, condition variable, or join queue when it is
* not being called from a signal handler. If a thread is a
* member of one of these queues when a signal handler is invoked,
* it must remain in the queue. For this reason, the links for
* these queues must not be (re)used for other queues.
*
* Use pqe for the scheduling queue link (both ready and waiting),
* and qe for other links.
* sqe for synchronization (mutex, condition variable, and join)
* queue links, and qe for all other links.
*/
/* Priority queue entry for this thread: */
TAILQ_ENTRY(pthread) pqe;
/* Queue entry for this thread: */
TAILQ_ENTRY(pthread) qe;
TAILQ_ENTRY(pthread) pqe; /* priority queue link */
TAILQ_ENTRY(pthread) sqe; /* synchronization queue link */
TAILQ_ENTRY(pthread) qe; /* all other queues link */
/* Wait data. */
union pthread_wait_data data;
@ -694,14 +812,17 @@ struct pthread {
int flags;
#define PTHREAD_FLAGS_PRIVATE 0x0001
#define PTHREAD_EXITING 0x0002
#define PTHREAD_FLAGS_IN_CONDQ 0x0004 /* in condition queue using qe link*/
#define PTHREAD_FLAGS_IN_WORKQ 0x0008 /* in work queue using qe link */
#define PTHREAD_FLAGS_IN_WAITQ 0x0010 /* in waiting queue using pqe link */
#define PTHREAD_FLAGS_IN_PRIOQ 0x0020 /* in priority queue using pqe link */
#define PTHREAD_FLAGS_IN_MUTEXQ 0x0040 /* in mutex queue using qe link */
#define PTHREAD_FLAGS_IN_FILEQ 0x0080 /* in file lock queue using qe link */
#define PTHREAD_FLAGS_IN_FDQ 0x0100 /* in fd lock queue using qe link */
#define PTHREAD_FLAGS_TRACE 0x0200 /* for debugging purposes */
#define PTHREAD_FLAGS_IN_WAITQ 0x0004 /* in waiting queue using pqe link */
#define PTHREAD_FLAGS_IN_PRIOQ 0x0008 /* in priority queue using pqe link */
#define PTHREAD_FLAGS_IN_WORKQ 0x0010 /* in work queue using qe link */
#define PTHREAD_FLAGS_IN_FILEQ 0x0020 /* in file lock queue using qe link */
#define PTHREAD_FLAGS_IN_FDQ 0x0040 /* in fd lock queue using qe link */
#define PTHREAD_FLAGS_IN_CONDQ 0x0080 /* in condition queue using sqe link*/
#define PTHREAD_FLAGS_IN_MUTEXQ 0x0100 /* in mutex queue using sqe link */
#define PTHREAD_FLAGS_IN_JOINQ 0x0200 /* in join queue using sqe link */
#define PTHREAD_FLAGS_TRACE 0x0400 /* for debugging purposes */
#define PTHREAD_FLAGS_IN_SYNCQ \
(PTHREAD_FLAGS_IN_CONDQ | PTHREAD_FLAGS_IN_MUTEXQ | PTHREAD_FLAGS_IN_JOINQ)
/*
* Base priority is the user setable and retrievable priority
@ -820,14 +941,31 @@ SCLASS int _thread_kern_in_sched
;
#endif
/* Last time that an incremental priority update was performed: */
SCLASS struct timeval kern_inc_prio_time
SCLASS int _sig_in_handler
#ifdef GLOBAL_PTHREAD_PRIVATE
= 0;
#else
;
#endif
/* Time of day at last scheduling timer signal: */
SCLASS struct timeval volatile _sched_tod
#ifdef GLOBAL_PTHREAD_PRIVATE
= { 0, 0 };
#else
;
#endif
/*
* Current scheduling timer ticks; used as resource usage.
*/
SCLASS unsigned int volatile _sched_ticks
#ifdef GLOBAL_PTHREAD_PRIVATE
= 0;
#else
;
#endif
/* Dead threads: */
SCLASS TAILQ_HEAD(, pthread) _dead_list
#ifdef GLOBAL_PTHREAD_PRIVATE
@ -905,9 +1043,9 @@ SCLASS int _thread_dtablesize /* Descriptor table size. */
;
#endif
SCLASS int _clock_res_nsec /* Clock resolution in nsec. */
SCLASS int _clock_res_usec /* Clock resolution in usec. */
#ifdef GLOBAL_PTHREAD_PRIVATE
= CLOCK_RES_NSEC;
= CLOCK_RES_USEC;
#else
;
#endif
@ -937,9 +1075,10 @@ SCLASS struct sigaction _thread_sigact[NSIG];
SCLASS int _thread_dfl_count[NSIG];
/*
* Pending signals for this process.
* Pending signals and mask for this process:
*/
SCLASS sigset_t _process_sigpending;
SCLASS sigset_t _process_sigmask;
/*
* Scheduling queues:
@ -959,6 +1098,21 @@ SCLASS volatile int _spinblock_count
#endif
;
/* Used to maintain pending and active signals: */
struct sigstatus {
int pending; /* Is this a pending signal? */
int blocked; /*
* A handler is currently active for
* this signal; ignore subsequent
* signals until the handler is done.
*/
int signo; /* arg 1 to signal handler */
siginfo_t siginfo; /* arg 2 to signal handler */
ucontext_t uc; /* arg 3 to signal handler */
};
SCLASS struct sigstatus _thread_sigq[NSIG];
/* Indicates that the signal queue needs to be checked. */
SCLASS volatile int _sigq_check_reqd
#ifdef GLOBAL_PTHREAD_PRIVATE
@ -998,6 +1152,18 @@ SCLASS void * _next_stack
#endif
;
/*
* Declare the kernel scheduler jump buffer and stack:
*/
SCLASS jmp_buf _thread_kern_sched_jb;
SCLASS void * _thread_kern_sched_stack
#ifdef GLOBAL_PTHREAD_PRIVATE
= NULL
#endif
;
/* Used for _PTHREADS_INVARIANTS checking. */
SCLASS int _thread_kern_new_state
#ifdef GLOBAL_PTHREAD_PRIVATE
@ -1025,15 +1191,19 @@ __BEGIN_DECLS
char *__ttyname_basic(int);
char *__ttyname_r_basic(int, char *, size_t);
char *ttyname_r(int, char *, size_t);
void _cond_wait_backout(pthread_t);
void _fd_lock_backout(pthread_t);
int _find_dead_thread(pthread_t);
int _find_thread(pthread_t);
void _flockfile_backout(pthread_t);
void _funlock_owned(pthread_t);
void _join_backout(pthread_t);
int _thread_create(pthread_t *,const pthread_attr_t *,void *(*start_routine)(void *),void *,pthread_t);
int _thread_fd_lock(int, int, struct timespec *);
int _thread_fd_lock_debug(int, int, struct timespec *,char *fname,int lineno);
void _dispatch_signals(void);
int _mutex_cv_lock(pthread_mutex_t *);
int _mutex_cv_unlock(pthread_mutex_t *);
void _mutex_lock_backout(pthread_t);
void _mutex_notify_priochange(pthread_t);
int _mutex_reinit(pthread_mutex_t *);
void _mutex_unlock_private(pthread_t);
@ -1044,14 +1214,15 @@ void _pq_remove(struct pq_queue *pq, struct pthread *);
void _pq_insert_head(struct pq_queue *pq, struct pthread *);
void _pq_insert_tail(struct pq_queue *pq, struct pthread *);
struct pthread *_pq_first(struct pq_queue *pq);
#if defined(_PTHREADS_INVARIANTS)
void _waitq_insert(pthread_t pthread);
void _waitq_remove(pthread_t pthread);
#if defined(_PTHREADS_INVARIANTS)
void _waitq_setactive(void);
void _waitq_clearactive(void);
#endif
void _thread_exit(char *, int, char *);
void _thread_exit_cleanup(void);
void _thread_exit_finish(void);
void _thread_fd_unlock(int, int);
void _thread_fd_unlock_debug(int, int, char *, int);
void _thread_fd_unlock_owned(pthread_t);
@ -1060,20 +1231,23 @@ void _thread_cleanupspecific(void);
void _thread_dump_info(void);
void _thread_init(void);
void _thread_kern_sched(ucontext_t *);
void _thread_kern_sched_state(enum pthread_state,char *fname,int lineno);
void _thread_kern_scheduler(void);
void _thread_kern_sched_frame(int frame);
void _thread_kern_sched_sig(void);
void _thread_kern_sched_state(enum pthread_state, char *fname, int lineno);
void _thread_kern_sched_state_unlock(enum pthread_state state,
spinlock_t *lock, char *fname, int lineno);
void _thread_kern_set_timeout(const struct timespec *);
void _thread_kern_sig_defer(void);
void _thread_kern_sig_undefer(void);
void _thread_sig_handler(int, int, ucontext_t *);
pthread_t _thread_sig_handle(int, ucontext_t *);
void _thread_sig_init(void);
void _thread_sig_handler(int, siginfo_t *, ucontext_t *);
void _thread_sig_check_pending(pthread_t pthread);
void _thread_sig_handle_pending(void);
void _thread_sig_send(pthread_t pthread, int sig);
void _thread_sig_deliver(pthread_t pthread, int sig);
void _thread_sig_wrapper(void);
int _thread_sigframe_find(pthread_t pthread, void *stackp);
void _thread_start(void);
void _thread_start_sig_handler(void);
void _thread_seterrno(pthread_t,int);
void _thread_seterrno(pthread_t, int);
int _thread_fd_table_init(int fd);
pthread_addr_t _thread_gc(pthread_addr_t);
void _thread_enter_cancellation_point(void);

View file

@ -45,6 +45,10 @@ pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param
ret = EINVAL;
else if (param == NULL) {
ret = ENOTSUP;
} else if ((param->sched_priority < PTHREAD_MIN_PRIORITY) ||
(param->sched_priority > PTHREAD_MAX_PRIORITY)) {
/* Return an unsupported value error. */
ret = ENOTSUP;
} else
(*attr)->prio = param->sched_priority;

View file

@ -170,10 +170,7 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
* perform the dynamic initialization:
*/
else if (*cond != NULL ||
(rval = pthread_cond_init(cond,NULL)) == 0) {
_thread_enter_cancellation_point();
(rval = pthread_cond_init(cond, NULL)) == 0) {
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
@ -286,8 +283,6 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
if (_thread_run->continuation != NULL)
_thread_run->continuation((void *) _thread_run);
}
_thread_leave_cancellation_point();
}
_thread_leave_cancellation_point();
@ -313,8 +308,6 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
* initialization.
*/
else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
_thread_enter_cancellation_point();
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
@ -446,8 +439,6 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
if (_thread_run->continuation != NULL)
_thread_run->continuation((void *) _thread_run);
}
_thread_leave_cancellation_point();
}
_thread_leave_cancellation_point();
@ -589,6 +580,48 @@ pthread_cond_broadcast(pthread_cond_t * cond)
return (rval);
}
void
_cond_wait_backout(pthread_t pthread)
{
pthread_cond_t cond;
cond = pthread->data.cond;
if (cond != NULL) {
/*
* Defer signals to protect the scheduling queues
* from access by the signal handler:
*/
_thread_kern_sig_defer();
/* Lock the condition variable structure: */
_SPINLOCK(&cond->lock);
/* Process according to condition variable type: */
switch (cond->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
cond_queue_remove(cond, pthread);
/* Check for no more waiters: */
if (TAILQ_FIRST(&cond->c_queue) == NULL)
cond->c_mutex = NULL;
break;
default:
break;
}
/* Unlock the condition variable structure: */
_SPINUNLOCK(&cond->lock);
/*
* Undefer and handle pending signals, yielding if
* necessary:
*/
_thread_kern_sig_undefer();
}
}
/*
* Dequeue a waiting thread from the head of a condition queue in
* descending priority order.
@ -599,7 +632,7 @@ cond_queue_deq(pthread_cond_t cond)
pthread_t pthread;
while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
TAILQ_REMOVE(&cond->c_queue, pthread, qe);
TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
if ((pthread->timeout == 0) && (pthread->interrupted == 0))
/*
@ -628,7 +661,7 @@ cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
* it isn't in the queue.
*/
if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
TAILQ_REMOVE(&cond->c_queue, pthread, qe);
TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
}
}
@ -642,19 +675,22 @@ cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
{
pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread);
/*
* 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))
TAILQ_INSERT_TAIL(&cond->c_queue, pthread, qe);
TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe);
else {
tid = TAILQ_FIRST(&cond->c_queue);
while (pthread->active_priority <= tid->active_priority)
tid = TAILQ_NEXT(tid, qe);
TAILQ_INSERT_BEFORE(tid, pthread, qe);
tid = TAILQ_NEXT(tid, sqe);
TAILQ_INSERT_BEFORE(tid, pthread, sqe);
}
pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
pthread->data.cond = cond;
}
#endif

View file

@ -49,17 +49,24 @@
static u_int64_t next_uniqueid = 1;
#define OFF(f) offsetof(struct pthread, f)
#define SIGFRAME_OFF(f) offsetof(struct pthread_signal_frame, f)
int _thread_next_offset = OFF(tle.tqe_next);
int _thread_uniqueid_offset = OFF(uniqueid);
int _thread_state_offset = OFF(state);
int _thread_name_offset = OFF(name);
int _thread_sig_saved_offset = OFF(sig_saved);
int _thread_saved_sigcontext_offset = OFF(saved_sigcontext);
int _thread_saved_jmp_buf_offset = OFF(saved_jmp_buf);
int _thread_curframe_offset = OFF(curframe);
int _thread_sigframe_ctx_offset = SIGFRAME_OFF(ctx);
int _thread_sigframe_ctxtype_offset = SIGFRAME_OFF(ctxtype);
#undef OFF
#undef SIGFRAME_OFF
int _thread_PS_RUNNING_value = PS_RUNNING;
int _thread_PS_DEAD_value = PS_DEAD;
int _thread_CTX_JB_NOSIG_value = CTX_JB_NOSIG;
int _thread_CTX_JB_value = CTX_JB;
int _thread_CTX_SJB_value = CTX_SJB;
int _thread_CTX_UC_value = CTX_UC;
int _thread_sigframe_size_value = sizeof(struct pthread_signal_frame);
int
pthread_create(pthread_t * thread, const pthread_attr_t * attr,
@ -162,7 +169,6 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Initialise the thread structure: */
memset(new_thread, 0, sizeof(struct pthread));
new_thread->slice_usec = -1;
new_thread->sig_saved = 0;
new_thread->stack = stack;
new_thread->start_routine = start_routine;
new_thread->arg = arg;
@ -179,62 +185,32 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Initialise the thread for signals: */
new_thread->sigmask = _thread_run->sigmask;
/* Initialize the first signal frame: */
new_thread->sigframes[0] = &new_thread->sigframe0;
new_thread->curframe = &new_thread->sigframe0;
/* Initialise the jump buffer: */
setjmp(new_thread->saved_jmp_buf);
_setjmp(new_thread->curframe->ctx.jb);
/*
* Set up new stack frame so that it looks like it
* returned from a longjmp() to the beginning of
* _thread_start().
*/
#if defined(__FreeBSD__)
#if defined(__alpha__)
new_thread->saved_jmp_buf[0]._jb[2] =
(long)_thread_start;
new_thread->saved_jmp_buf[0]._jb[4 + R_RA] =
0;
new_thread->saved_jmp_buf[0]._jb[4 + R_T12] =
(long)_thread_start;
#else
new_thread->saved_jmp_buf[0]._jb[0] =
(long)_thread_start;
#endif
#elif defined(__NetBSD__)
#if defined(__alpha__)
new_thread->saved_jmp_buf[2] = (long)_thread_start;
new_thread->saved_jmp_buf[4 + R_RA] = 0;
new_thread->saved_jmp_buf[4 + R_T12] =
(long)_thread_start;
#else
new_thread->saved_jmp_buf[0] = (long)_thread_start;
#endif
#else
#error "Don't recognize this operating system!"
#endif
SET_RETURN_ADDR_JB(new_thread->curframe->ctx.jb,
_thread_start);
/* The stack starts high and builds down: */
#if defined(__FreeBSD__)
#if defined(__alpha__)
new_thread->saved_jmp_buf[0]._jb[4 + R_SP] =
(long)new_thread->stack + pattr->stacksize_attr
- sizeof(double);
#else
new_thread->saved_jmp_buf[0]._jb[2] =
(int)(new_thread->stack + pattr->stacksize_attr -
sizeof(double));
#endif
#elif defined(__NetBSD__)
#if defined(__alpha__)
new_thread->saved_jmp_buf[4 + R_SP] =
(long)new_thread->stack + pattr->stacksize_attr -
sizeof(double);
#else
new_thread->saved_jmp_buf[2] = (long)new_thread->stack
+ pattr->stacksize_attr - sizeof(double);
#endif
#else
#error "Don't recognize this operating system!"
#endif
SET_STACK_JB(new_thread->curframe->ctx.jb,
(long)new_thread->stack + pattr->stacksize_attr
- sizeof(double));
/* Initialize the rest of the frame: */
new_thread->curframe->ctxtype = CTX_JB_NOSIG;
/* Set the base of the stack: */
new_thread->curframe->stackp =
GET_STACK_JB(new_thread->curframe->ctx.jb);
new_thread->sigframe_count = 0;
/* Copy the thread attributes: */
memcpy(&new_thread->attr, pattr, sizeof(struct pthread_attr));
@ -245,20 +221,22 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
*/
if (new_thread->attr.flags & PTHREAD_INHERIT_SCHED) {
/* Copy the scheduling attributes: */
new_thread->base_priority
= _thread_run->base_priority;
new_thread->attr.prio
= _thread_run->base_priority;
new_thread->attr.sched_policy
= _thread_run->attr.sched_policy;
new_thread->base_priority =
_thread_run->base_priority &
~PTHREAD_SIGNAL_PRIORITY;
new_thread->attr.prio =
_thread_run->base_priority &
~PTHREAD_SIGNAL_PRIORITY;
new_thread->attr.sched_policy =
_thread_run->attr.sched_policy;
} else {
/*
* Use just the thread priority, leaving the
* other scheduling attributes as their
* default values:
*/
new_thread->base_priority
= new_thread->attr.prio;
new_thread->base_priority =
new_thread->attr.prio;
}
new_thread->active_priority = new_thread->base_priority;
new_thread->inherited_priority = 0;
@ -275,7 +253,6 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
new_thread->flags = 0;
new_thread->poll_data.nfds = 0;
new_thread->poll_data.fds = NULL;
new_thread->jmpflags = 0;
new_thread->continuation = NULL;
/*
@ -317,7 +294,6 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Schedule the new user thread: */
_thread_kern_sched(NULL);
/*
* Start a garbage collector thread
* if necessary.
@ -325,6 +301,7 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
if (f_gc && pthread_create(&gc_thread,NULL,
_thread_gc,NULL) != 0)
PANIC("Can't create gc thread");
}
}

View file

@ -61,9 +61,10 @@ pthread_detach(pthread_t pthread)
/* Enter a loop to bring all threads off the join queue: */
while ((next_thread = TAILQ_FIRST(&pthread->join_queue)) != NULL) {
/* Remove the thread from the queue: */
TAILQ_REMOVE(&pthread->join_queue, next_thread, qe);
TAILQ_REMOVE(&pthread->join_queue, next_thread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
/* Make the thread run: */
/* Make the thread runnable: */
PTHREAD_NEW_STATE(next_thread,PS_RUNNING);
}

View file

@ -93,9 +93,6 @@ _execve(const char *name, char *const * argv, char *const * envp)
act.sa_mask = _thread_sigact[i - 1].sa_mask;
act.sa_flags = _thread_sigact[i - 1].sa_flags;
/* Ensure the scheduling signal is masked: */
sigaddset(&act.sa_mask, _SCHED_SIGNAL);
/* Change the signal action for the process: */
_thread_sys_sigaction(i, &act, &oact);
}

View file

@ -41,6 +41,9 @@
#include <pthread.h>
#include "pthread_private.h"
#define FLAGS_IN_SCHEDQ \
(PTHREAD_FLAGS_IN_PRIOQ|PTHREAD_FLAGS_IN_WAITQ|PTHREAD_FLAGS_IN_WORKQ)
void __exit(int status)
{
int flags;
@ -138,7 +141,7 @@ _thread_exit_cleanup(void)
void
pthread_exit(void *status)
{
pthread_t pthread;
int frame;
/* Check if this thread is already in the process of exiting: */
if ((_thread_run->flags & PTHREAD_EXITING) != 0) {
@ -172,25 +175,24 @@ pthread_exit(void *status)
_thread_run->poll_data.fds = NULL;
}
/*
* Defer signals to protect the scheduling queues from access
* by the signal handler:
*/
_thread_kern_sig_defer();
/* Check if there are any threads joined to this one: */
while ((pthread = TAILQ_FIRST(&(_thread_run->join_queue))) != NULL) {
/* Remove the thread from the queue: */
TAILQ_REMOVE(&_thread_run->join_queue, pthread, qe);
/* Wake the joined thread and let it detach this thread: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
if ((frame = _thread_run->sigframe_count) == 0)
_thread_exit_finish();
else {
/*
* Jump back and unwind the signal frames to gracefully
* cleanup.
*/
___longjmp(*_thread_run->sigframes[frame]->sig_jb, 1);
}
/*
* Undefer and handle pending signals, yielding if necessary:
*/
_thread_kern_sig_undefer();
/* This point should not be reached. */
PANIC("Dead thread has resumed");
}
void
_thread_exit_finish(void)
{
pthread_t pthread;
/*
* Lock the garbage collector mutex to ensure that the garbage
@ -202,20 +204,6 @@ pthread_exit(void *status)
/* Add this thread to the list of dead threads. */
TAILQ_INSERT_HEAD(&_dead_list, _thread_run, dle);
/*
* Defer signals to protect the scheduling queues from access
* by the signal handler:
*/
_thread_kern_sig_defer();
/* Remove this thread from the thread list: */
TAILQ_REMOVE(&_thread_list, _thread_run, tle);
/*
* Undefer and handle pending signals, yielding if necessary:
*/
_thread_kern_sig_undefer();
/*
* Signal the garbage collector thread that there is something
* to clean up.
@ -224,19 +212,33 @@ pthread_exit(void *status)
PANIC("Cannot signal gc cond");
/*
* Mark the thread as dead so it will not return if it
* gets context switched out when the mutex is unlocked.
* Avoid a race condition where a scheduling signal can occur
* causing the garbage collector thread to run. If this happens,
* the current thread can be cleaned out from under us.
*/
PTHREAD_SET_STATE(_thread_run, PS_DEAD);
_thread_kern_sig_defer();
/* Unlock the garbage collector mutex: */
if (pthread_mutex_unlock(&_gc_mutex) != 0)
PANIC("Cannot lock gc mutex");
/* This this thread will never be re-scheduled. */
_thread_kern_sched(NULL);
/* Check if there are any threads joined to this one: */
while ((pthread = TAILQ_FIRST(&(_thread_run->join_queue))) != NULL) {
/* Remove the thread from the queue: */
TAILQ_REMOVE(&_thread_run->join_queue, pthread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
/* This point should not be reached. */
PANIC("Dead thread has resumed");
/*
* Wake the joined thread and let it
* detach this thread:
*/
PTHREAD_NEW_STATE(pthread, PS_RUNNING);
}
/* Remove this thread from the thread list: */
TAILQ_REMOVE(&_thread_list, _thread_run, tle);
/* This thread will never be re-scheduled. */
_thread_kern_sched_state(PS_DEAD, __FILE__, __LINE__);
}
#endif

View file

@ -295,9 +295,6 @@ _thread_fd_unlock(int fd, int lock_type)
*/
_thread_kern_sig_undefer();
}
/* Nothing to return. */
return;
}
int
@ -326,7 +323,8 @@ _thread_fd_lock(int fd, int lock_type, struct timespec * timeout)
* Wait for the file descriptor to be locked
* for read for the current thread:
*/
if (_thread_fd_table[fd]->r_owner != _thread_run) {
while ((_thread_fd_table[fd]->r_owner != _thread_run) &&
(_thread_run->interrupted == 0)) {
/*
* Check if the file descriptor is locked by
* another thread:
@ -404,7 +402,8 @@ _thread_fd_lock(int fd, int lock_type, struct timespec * timeout)
* Wait for the file descriptor to be locked
* for write for the current thread:
*/
if (_thread_fd_table[fd]->w_owner != _thread_run) {
while ((_thread_fd_table[fd]->w_owner != _thread_run) &&
(_thread_run->interrupted == 0)) {
/*
* Check if the file descriptor is locked by
* another thread:
@ -608,9 +607,6 @@ _thread_fd_unlock_debug(int fd, int lock_type, char *fname, int lineno)
*/
_thread_kern_sig_undefer();
}
/* Nothing to return. */
return;
}
int
@ -640,7 +636,8 @@ _thread_fd_lock_debug(int fd, int lock_type, struct timespec * timeout,
* Wait for the file descriptor to be locked
* for read for the current thread:
*/
if (_thread_fd_table[fd]->r_owner != _thread_run) {
while ((_thread_fd_table[fd]->r_owner != _thread_run) &&
(_thread_run->interrupted == 0)) {
/*
* Check if the file descriptor is locked by
* another thread:
@ -727,7 +724,8 @@ _thread_fd_lock_debug(int fd, int lock_type, struct timespec * timeout,
* Wait for the file descriptor to be locked
* for write for the current thread:
*/
if (_thread_fd_table[fd]->w_owner != _thread_run) {
while ((_thread_fd_table[fd]->w_owner != _thread_run) &&
(_thread_run->interrupted == 0)) {
/*
* Check if the file descriptor is locked by
* another thread:
@ -902,6 +900,58 @@ _thread_fd_unlock_owned(pthread_t pthread)
}
}
void
_fd_lock_backout(pthread_t pthread)
{
int fd;
/*
* Defer signals to protect the scheduling queues
* from access by the signal handler:
*/
_thread_kern_sig_defer();
switch (pthread->state) {
case PS_FDLR_WAIT:
fd = pthread->data.fd.fd;
/*
* Lock the file descriptor table entry to prevent
* other threads for clashing with the current
* thread's accesses:
*/
_SPINLOCK(&_thread_fd_table[fd]->lock);
/* Remove the thread from the waiting queue: */
FDQ_REMOVE(&_thread_fd_table[fd]->r_queue, pthread);
break;
case PS_FDLW_WAIT:
fd = pthread->data.fd.fd;
/*
* Lock the file descriptor table entry to prevent
* other threads from clashing with the current
* thread's accesses:
*/
_SPINLOCK(&_thread_fd_table[fd]->lock);
/* Remove the thread from the waiting queue: */
FDQ_REMOVE(&_thread_fd_table[fd]->w_queue, pthread);
break;
default:
break;
}
/*
* Undefer and handle pending signals, yielding if
* necessary.
*/
_thread_kern_sig_undefer();
}
static inline pthread_t
fd_next_reader(int fd)
{

View file

@ -245,6 +245,8 @@ _flockfile_debug(FILE * fp, char *fname, int lineno)
/* Unlock the hash table: */
_SPINUNLOCK(&hash_lock);
_thread_run->data.fp = fp;
/* Wait on the FILE lock: */
_thread_kern_sched_state(PS_FILE_WAIT, fname, lineno);
@ -260,14 +262,12 @@ _flockfile_debug(FILE * fp, char *fname, int lineno)
_thread_run->continuation((void *)_thread_run);
}
}
return;
}
void
_flockfile(FILE * fp)
{
_flockfile_debug(fp, __FILE__, __LINE__);
return;
}
int
@ -398,7 +398,6 @@ _funlockfile(FILE * fp)
*/
_thread_kern_sig_undefer();
}
return;
}
void
@ -469,4 +468,39 @@ _funlock_owned(pthread_t pthread)
_thread_kern_sig_undefer();
}
void
_flockfile_backout(pthread_t pthread)
{
int idx = file_idx(pthread->data.fp);
struct file_lock *p;
/*
* Defer signals to protect the scheduling queues from
* access by the signal handler:
*/
_thread_kern_sig_defer();
/*
* Get a pointer to the lock for the file and check that
* the running thread is the one with the lock:
*/
if (((pthread->flags & PTHREAD_FLAGS_IN_FILEQ) != 0) &&
((p = find_lock(idx, pthread->data.fp)) != NULL)) {
/* Lock the hash table: */
_SPINLOCK(&hash_lock);
/* Remove the thread from the queue: */
TAILQ_REMOVE(&p->l_head, pthread, qe);
pthread->flags &= ~PTHREAD_FLAGS_IN_FILEQ;
/* Unlock the hash table: */
_SPINUNLOCK(&hash_lock);
}
/*
* Undefer and handle pending signals, yielding if necessary:
*/
_thread_kern_sig_undefer();
}
#endif

View file

@ -183,9 +183,6 @@ _fork(void)
/* Don't queue signals yet: */
_queue_signals = 0;
/* Initialize signal handling: */
_thread_sig_init();
/* Initialize the scheduling switch hook routine: */
_sched_switch_hook = NULL;

View file

@ -57,8 +57,8 @@ _thread_gc(pthread_addr_t arg)
void *p_stack;
/* Block all signals */
sigfillset (&mask);
sigprocmask (SIG_BLOCK, &mask, NULL);
sigfillset(&mask);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
/* Mark this thread as a library thread (not a user thread). */
_thread_run->flags |= PTHREAD_FLAGS_PRIVATE;

View file

@ -49,7 +49,8 @@ pthread_getschedparam(pthread_t pthread, int *policy,
/* Find the thread in the list of active threads: */
else if ((ret = _find_thread(pthread)) == 0) {
/* Return the threads base priority and scheduling policy: */
param->sched_priority = pthread->base_priority;
param->sched_priority =
PTHREAD_BASE_PRIORITY(pthread->base_priority);
*policy = pthread->attr.sched_policy;
}

View file

@ -32,6 +32,7 @@
* $FreeBSD$
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
@ -296,7 +297,6 @@ _thread_dump_info(void)
/* Close the dump file: */
_thread_sys_close(fd);
}
return;
}
/* Set the thread name for debug: */

View file

@ -90,9 +90,9 @@ _thread_init(void)
int i;
size_t len;
int mib[2];
struct timeval tv;
struct clockinfo clockinfo;
struct sigaction act;
struct itimerval itimer;
/* Check if this function has already been called: */
if (_thread_initial)
@ -160,7 +160,7 @@ _thread_init(void)
PANIC("Cannot get kernel write pipe flags");
}
/* Allocate and initialize the ready queue: */
else if (_pq_alloc(&_readyq, PTHREAD_MIN_PRIORITY, PTHREAD_MAX_PRIORITY) != 0) {
else if (_pq_alloc(&_readyq, PTHREAD_MIN_PRIORITY, PTHREAD_LAST_PRIORITY) != 0) {
/* Abort this application: */
PANIC("Cannot allocate priority ready queue.");
}
@ -171,7 +171,11 @@ _thread_init(void)
* abort:
*/
PANIC("Cannot allocate memory for initial thread");
} else {
}
/* Allocate memory for the scheduler stack: */
else if ((_thread_kern_sched_stack = malloc(PAGE_SIZE * 10)) == NULL)
PANIC("Failed to allocate stack for scheduler");
else {
/* Zero the global kernel thread structure: */
memset(&_thread_kern_thread, 0, sizeof(struct pthread));
_thread_kern_thread.flags = PTHREAD_FLAGS_PRIVATE;
@ -211,6 +215,12 @@ _thread_init(void)
_thread_initial->attr.stackaddr_attr = _thread_initial->stack;
_thread_initial->attr.stacksize_attr = PTHREAD_STACK_INITIAL;
/* Setup the context for the scheduler: */
_setjmp(_thread_kern_sched_jb);
SET_STACK_JB(_thread_kern_sched_jb,
_thread_kern_sched_stack + PAGE_SIZE*10 - sizeof(double));
SET_RETURN_ADDR_JB(_thread_kern_sched_jb, _thread_kern_scheduler);
/*
* Write a magic value to the thread structure
* to help identify valid ones:
@ -236,10 +246,19 @@ _thread_init(void)
TAILQ_INIT(&(_thread_initial->mutexq));
_thread_initial->priority_mutex_count = 0;
/* Initialize last active time to now: */
gettimeofday(&tv, NULL);
_thread_initial->last_active.tv_sec = tv.tv_sec;
_thread_initial->last_active.tv_usec = tv.tv_usec;
/* Initialize the global scheduling time: */
_sched_ticks = 0;
gettimeofday((struct timeval *) &_sched_tod, NULL);
/* Initialize last active: */
_thread_initial->last_active = (long) _sched_ticks;
/* Initialize the initial signal frame: */
_thread_initial->sigframes[0] = &_thread_initial->sigframe0;
_thread_initial->curframe = &_thread_initial->sigframe0;
_thread_initial->curframe->ctxtype = CTX_JB_NOSIG;
/* Set the base of the stack: */
_thread_initial->curframe->stackp = (unsigned long) USRSTACK;
/* Initialise the rest of the fields: */
_thread_initial->poll_data.nfds = 0;
@ -257,10 +276,13 @@ _thread_init(void)
/* Initialise the global signal action structure: */
sigfillset(&act.sa_mask);
act.sa_handler = (void (*) ()) _thread_sig_handler;
act.sa_flags = 0;
act.sa_flags = SA_SIGINFO;
/* Initialize signal handling: */
_thread_sig_init();
/* Clear pending signals for the process: */
sigemptyset(&_process_sigpending);
/* Clear the signal queue: */
memset(_thread_sigq, 0, sizeof(_thread_sigq));
/* Enter a loop to get the existing signal status: */
for (i = 1; i < NSIG; i++) {
@ -295,13 +317,19 @@ _thread_init(void)
*/
PANIC("Cannot initialise signal handler");
}
_thread_sigact[_SCHED_SIGNAL - 1].sa_flags = SA_SIGINFO;
_thread_sigact[SIGINFO - 1].sa_flags = SA_SIGINFO;
_thread_sigact[SIGCHLD - 1].sa_flags = SA_SIGINFO;
/* Get the process signal mask: */
_thread_sys_sigprocmask(SIG_SETMASK, NULL, &_process_sigmask);
/* Get the kernel clockrate: */
mib[0] = CTL_KERN;
mib[1] = KERN_CLOCKRATE;
len = sizeof (struct clockinfo);
if (sysctl(mib, 2, &clockinfo, &len, NULL, 0) == 0)
_clock_res_nsec = clockinfo.tick * 1000;
_clock_res_usec = clockinfo.tick;
/* Get the table size: */
if ((_thread_dtablesize = getdtablesize()) < 0) {
@ -346,6 +374,14 @@ _thread_init(void)
PANIC("Cannot initialize stdio file "
"descriptor table entry");
}
/* Install the scheduling timer: */
itimer.it_interval.tv_sec = 0;
itimer.it_interval.tv_usec = _clock_res_usec;
itimer.it_value = itimer.it_interval;
if (setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL) != 0)
PANIC("Cannot set interval timer");
}
}
@ -362,10 +398,6 @@ _thread_init(void)
if (pthread_mutex_init(&_gc_mutex,NULL) != 0 ||
pthread_cond_init(&_gc_cond,NULL) != 0)
PANIC("Failed to initialise garbage collector mutex or condvar");
gettimeofday(&kern_inc_prio_time, NULL);
return;
}
/*

View file

@ -40,39 +40,24 @@
#include <pthread.h>
#include "pthread_private.h"
/*
* Offset into the jmp_buf. This is highly machine-dependent, but is a
* necessary evil in order to compare stack pointers and make decisions based on
* where a *longjmp() is jumping to.
*/
#if defined(__i386__)
#define JMP_BUF_SP_OFFSET 2
#elif defined(__alpha)
#define JMP_BUF_SP_OFFSET (4 + R_SP)
#else
#error "Don't recognize this architecture!"
#endif
void
siglongjmp(sigjmp_buf env, int savemask)
{
void *jmp_stackp;
void *stack_begin, *stack_end;
int frame, dst_frame;
if (_thread_run->signal_nest_level == 0)
if ((frame = _thread_run->sigframe_count) == 0)
__siglongjmp(env, savemask);
/* Get the stack pointer from the jump buffer. */
jmp_stackp = (void *)env->_sjb[JMP_BUF_SP_OFFSET];
jmp_stackp = (void *) GET_STACK_SJB(env);
/* Get the bounds of the current threads stack. */
if (_thread_run->stack != NULL) {
stack_begin = _thread_run->stack;
stack_end = stack_begin + _thread_run->attr.stacksize_attr;
} else {
stack_end = (void *)USRSTACK;
stack_begin = stack_end - PTHREAD_STACK_INITIAL;
}
PTHREAD_ASSERT(_thread_run->stack != NULL,
"Thread stack pointer is null");
stack_begin = _thread_run->stack;
stack_end = stack_begin + _thread_run->attr.stacksize_attr;
/*
* Make sure we aren't jumping to a different stack. Make sure
@ -84,19 +69,28 @@ siglongjmp(sigjmp_buf env, int savemask)
PANIC("siglongjmp()ing between thread contexts is undefined by "
"POSIX 1003.1");
memcpy(_thread_run->nested_jmp.sigjmp, env,
sizeof(_thread_run->nested_jmp.sigjmp));
if ((dst_frame = _thread_sigframe_find(_thread_run, jmp_stackp)) < 0)
/*
* The stack pointer was verified above, so this
* shouldn't happen. Let's be anal anyways.
*/
PANIC("Error locating signal frame");
else if (dst_frame == frame) {
/*
* The stack pointer is somewhere within the current
* frame. Jump to the users context.
*/
__siglongjmp(env, savemask);
}
/*
* Only save oldstate once so that dispatching multiple signals will not
* lose the thread's original state.
* Copy the users context to the return context of the
* destination frame.
*/
if (_thread_run->jmpflags == JMPFLAGS_NONE)
_thread_run->oldstate = _thread_run->state;
PTHREAD_SET_STATE(_thread_run, PS_RUNNING);
_thread_run->jmpflags = JMPFLAGS_SIGLONGJMP;
_thread_run->longjmp_val = savemask;
___longjmp(*_thread_run->sighandler_jmp_buf, 1);
memcpy(&_thread_run->sigframes[dst_frame]->ctx.sigjb, env, sizeof(*env));
_thread_run->sigframes[dst_frame]->ctxtype = CTX_SJB;
_thread_run->sigframes[dst_frame]->longjmp_val = savemask;
_thread_run->curframe->dst_frame = dst_frame;
___longjmp(*_thread_run->curframe->sig_jb, 1);
}
void
@ -104,21 +98,19 @@ longjmp(jmp_buf env, int val)
{
void *jmp_stackp;
void *stack_begin, *stack_end;
int frame, dst_frame;
if (_thread_run->signal_nest_level == 0)
if ((frame = _thread_run->sigframe_count) == 0)
__longjmp(env, val);
/* Get the stack pointer from the jump buffer. */
jmp_stackp = (void *)env->_jb[JMP_BUF_SP_OFFSET];
jmp_stackp = (void *) GET_STACK_JB(env);
/* Get the bounds of the current threads stack. */
if (_thread_run->stack != NULL) {
stack_begin = _thread_run->stack;
stack_end = stack_begin + _thread_run->attr.stacksize_attr;
} else {
stack_end = (void *)USRSTACK;
stack_begin = stack_end - PTHREAD_STACK_INITIAL;
}
PTHREAD_ASSERT(_thread_run->stack != NULL,
"Thread stack pointer is null");
stack_begin = _thread_run->stack;
stack_end = stack_begin + _thread_run->attr.stacksize_attr;
/*
* Make sure we aren't jumping to a different stack. Make sure
@ -130,19 +122,29 @@ longjmp(jmp_buf env, int val)
PANIC("longjmp()ing between thread contexts is undefined by "
"POSIX 1003.1");
memcpy(_thread_run->nested_jmp.jmp, env,
sizeof(_thread_run->nested_jmp.jmp));
if ((dst_frame = _thread_sigframe_find(_thread_run, jmp_stackp)) < 0)
/*
* The stack pointer was verified above, so this
* shouldn't happen. Let's be anal anyways.
*/
PANIC("Error locating signal frame");
else if (dst_frame == frame) {
/*
* The stack pointer is somewhere within the current
* frame. Jump to the users context.
*/
__longjmp(env, val);
}
/*
* Only save oldstate once so that dispatching multiple signals will not
* lose the thread's original state.
* Copy the users context to the return context of the
* destination frame.
*/
if (_thread_run->jmpflags == JMPFLAGS_NONE)
_thread_run->oldstate = _thread_run->state;
PTHREAD_SET_STATE(_thread_run, PS_RUNNING);
_thread_run->jmpflags = JMPFLAGS_LONGJMP;
_thread_run->longjmp_val = val;
___longjmp(*_thread_run->sighandler_jmp_buf, 1);
memcpy(&_thread_run->sigframes[dst_frame]->ctx.jb, env, sizeof(*env));
_thread_run->sigframes[dst_frame]->ctxtype = CTX_JB;
_thread_run->sigframes[dst_frame]->longjmp_val = val;
_thread_run->curframe->dst_frame = dst_frame;
___longjmp(*_thread_run->curframe->sig_jb, 1);
}
void
@ -150,21 +152,19 @@ _longjmp(jmp_buf env, int val)
{
void *jmp_stackp;
void *stack_begin, *stack_end;
int frame, dst_frame;
if (_thread_run->signal_nest_level == 0)
if ((frame = _thread_run->sigframe_count) == 0)
___longjmp(env, val);
/* Get the stack pointer from the jump buffer. */
jmp_stackp = (void *)env->_jb[JMP_BUF_SP_OFFSET];
jmp_stackp = (void *) GET_STACK_JB(env);
/* Get the bounds of the current threads stack. */
if (_thread_run->stack != NULL) {
stack_begin = _thread_run->stack;
stack_end = stack_begin + _thread_run->attr.stacksize_attr;
} else {
stack_end = (void *)USRSTACK;
stack_begin = stack_end - PTHREAD_STACK_INITIAL;
}
PTHREAD_ASSERT(_thread_run->stack != NULL,
"Thread stack pointer is null");
stack_begin = _thread_run->stack;
stack_end = stack_begin + _thread_run->attr.stacksize_attr;
/*
* Make sure we aren't jumping to a different stack. Make sure
@ -176,18 +176,27 @@ _longjmp(jmp_buf env, int val)
PANIC("_longjmp()ing between thread contexts is undefined by "
"POSIX 1003.1");
memcpy(_thread_run->nested_jmp.jmp, env,
sizeof(_thread_run->nested_jmp.jmp));
if ((dst_frame = _thread_sigframe_find(_thread_run, jmp_stackp)) < 0)
/*
* The stack pointer was verified above, so this
* shouldn't happen. Let's be anal anyways.
*/
PANIC("Error locating signal frame");
else if (dst_frame == frame) {
/*
* The stack pointer is somewhere within the current
* frame. Jump to the users context.
*/
___longjmp(env, val);
}
/*
* Only save oldstate once so that dispatching multiple signals will not
* lose the thread's original state.
* Copy the users context to the return context of the
* destination frame.
*/
if (_thread_run->jmpflags == JMPFLAGS_NONE)
_thread_run->oldstate = _thread_run->state;
PTHREAD_SET_STATE(_thread_run, PS_RUNNING);
_thread_run->jmpflags = JMPFLAGS__LONGJMP;
_thread_run->longjmp_val = val;
___longjmp(*_thread_run->sighandler_jmp_buf, 1);
memcpy(&_thread_run->sigframes[dst_frame]->ctx.jb, env, sizeof(*env));
_thread_run->sigframes[dst_frame]->ctxtype = CTX_JB_NOSIG;
_thread_run->sigframes[dst_frame]->longjmp_val = val;
_thread_run->curframe->dst_frame = dst_frame;
___longjmp(*_thread_run->curframe->sig_jb, 1);
}
#endif

View file

@ -40,7 +40,6 @@ int
pthread_join(pthread_t pthread, void **thread_return)
{
int ret = 0;
pthread_t pthread1 = NULL;
_thread_enter_cancellation_point();
@ -62,11 +61,7 @@ pthread_join(pthread_t pthread, void **thread_return)
* Find the thread in the list of active threads or in the
* list of dead threads:
*/
if (_find_thread(pthread) == 0 ||
_find_dead_thread(pthread) == 0)
pthread1 = pthread;
if (pthread1 == NULL)
if ((_find_thread(pthread) != 0) && (_find_dead_thread(pthread) != 0))
/* Return an error: */
ret = ESRCH;
@ -77,6 +72,8 @@ pthread_join(pthread_t pthread, void **thread_return)
/* Check if the thread is not dead: */
else if (pthread->state != PS_DEAD) {
PTHREAD_ASSERT_NOT_IN_SYNCQ(_thread_run);
/* Clear the interrupted flag: */
_thread_run->interrupted = 0;
@ -87,13 +84,18 @@ pthread_join(pthread_t pthread, void **thread_return)
_thread_kern_sig_defer();
/* Add the running thread to the join queue: */
TAILQ_INSERT_TAIL(&(pthread->join_queue), _thread_run, qe);
TAILQ_INSERT_TAIL(&(pthread->join_queue), _thread_run, sqe);
_thread_run->flags |= PTHREAD_FLAGS_IN_JOINQ;
_thread_run->data.thread = pthread;
/* Schedule the next thread: */
_thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
if (_thread_run->interrupted != 0)
TAILQ_REMOVE(&(pthread->join_queue), _thread_run, qe);
if (_thread_run->interrupted != 0) {
TAILQ_REMOVE(&(pthread->join_queue), _thread_run, sqe);
_thread_run->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
}
_thread_run->data.thread = NULL;
_thread_kern_sig_undefer();
@ -122,4 +124,15 @@ pthread_join(pthread_t pthread, void **thread_return)
/* Return the completion status: */
return (ret);
}
void
_join_backout(pthread_t pthread)
{
_thread_kern_sig_defer();
if (pthread->state == PS_JOIN) {
TAILQ_REMOVE(&pthread->data.thread->join_queue, pthread, sqe);
_thread_run->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
}
_thread_kern_sig_undefer();
}
#endif

View file

@ -52,9 +52,16 @@
#include <pthread.h>
#include "pthread_private.h"
/* #define DEBUG_THREAD_KERN */
#ifdef DEBUG_THREAD_KERN
#define DBG_MSG stdout_debug
#else
#define DBG_MSG(x...)
#endif
/* Static function prototype definitions: */
static void
_thread_kern_poll(int wait_reqd);
thread_kern_poll(int wait_reqd);
static void
dequeue_signals(void);
@ -62,18 +69,39 @@ dequeue_signals(void);
static inline void
thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in);
void
_thread_kern_sched(ucontext_t * scp)
{
#ifndef __alpha__
char *fdata;
#endif
pthread_t pthread, pthread_h = NULL;
struct itimerval itimer;
struct timespec ts, ts1;
struct timeval tv, tv1;
int set_timer = 0;
/* Static variables: */
static int last_tick = 0;
/*
* This is called when a signal handler finishes and wants to
* return to a previous frame.
*/
void
_thread_kern_sched_frame(int frame)
{
/*
* Flag the pthread kernel as executing scheduler code
* to avoid a signal from interrupting this execution and
* corrupting the (soon-to-be) current frame.
*/
_thread_kern_in_sched = 1;
/* Return to the specified frame: */
_thread_run->curframe = _thread_run->sigframes[frame];
_thread_run->sigframe_count = frame;
if (_thread_run->sigframe_count == 0)
/* Restore the threads priority: */
_thread_run->active_priority &= ~PTHREAD_SIGNAL_PRIORITY;
/* Switch to the thread scheduler: */
___longjmp(_thread_kern_sched_jb, 1);
}
void
_thread_kern_sched(ucontext_t *scp)
{
/*
* Flag the pthread kernel as executing scheduler code
* to avoid a scheduler signal from interrupting this
@ -84,67 +112,94 @@ _thread_kern_sched(ucontext_t * scp)
/* Check if this function was called from the signal handler: */
if (scp != NULL) {
/*
* Copy the signal context to the current thread's jump
* buffer:
* The signal handler should have saved the state of
* the current thread. Restore the process signal
* mask.
*/
memcpy(&_thread_run->saved_sigcontext, scp, sizeof(_thread_run->saved_sigcontext));
#ifndef __alpha__
/* Point to the floating point data in the running thread: */
fdata = _thread_run->saved_fp;
/* Save the floating point data: */
__asm__("fnsave %0": :"m"(*fdata));
#endif
/* Flag the signal context as the last state saved: */
_thread_run->sig_saved = 1;
}
/* Save the state of the current thread: */
else if (setjmp(_thread_run->saved_jmp_buf) != 0) {
if (_thread_sys_sigprocmask(SIG_SETMASK,
&_process_sigmask, NULL) != 0)
PANIC("Unable to restore process mask after signal");
/*
* This point is reached when a longjmp() is called to
* restore the state of a thread.
*
* This is the normal way out of the scheduler.
* We're running on the signal stack; just call the
* kernel scheduler directly.
*/
_thread_kern_in_sched = 0;
if (((_thread_run->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0) &&
((_thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)) {
/*
* Cancellations override signals.
DBG_MSG("Entering scheduler due to signal\n");
_thread_kern_scheduler();
} else {
/* Save the state of the current thread: */
if (_setjmp(_thread_run->curframe->ctx.jb) == 0) {
/* Flag the jump buffer was the last state saved: */
_thread_run->curframe->ctxtype = CTX_JB_NOSIG;
_thread_run->curframe->longjmp_val = 1;
} else {
DBG_MSG("Returned from ___longjmp, thread %p\n",
_thread_run);
/*
* This point is reached when a longjmp() is called
* to restore the state of a thread.
*
* Stick a cancellation point at the start of
* each async-cancellable thread's resumption.
*
* We allow threads woken at cancel points to do their
* own checks.
* This is the normal way out of the scheduler.
*/
pthread_testcancel();
_thread_kern_in_sched = 0;
if (_thread_run->sig_defer_count == 0) {
if (((_thread_run->cancelflags &
PTHREAD_AT_CANCEL_POINT) == 0) &&
((_thread_run->cancelflags &
PTHREAD_CANCEL_ASYNCHRONOUS) != 0))
/*
* Cancellations override signals.
*
* Stick a cancellation point at the
* start of each async-cancellable
* thread's resumption.
*
* We allow threads woken at cancel
* points to do their own checks.
*/
pthread_testcancel();
}
if (_sched_switch_hook != NULL) {
/* Run the installed switch hook: */
thread_run_switch_hook(_last_user_thread,
_thread_run);
}
return;
}
/* Switch to the thread scheduler: */
___longjmp(_thread_kern_sched_jb, 1);
}
}
/*
* Check for undispatched signals due to calls to
* pthread_kill().
*/
if (SIGNOTEMPTY(_thread_run->sigpend))
_dispatch_signals();
void
_thread_kern_sched_sig(void)
{
_thread_run->check_pending = 1;
_thread_kern_sched(NULL);
}
if (_sched_switch_hook != NULL) {
/* Run the installed switch hook: */
thread_run_switch_hook(_last_user_thread, _thread_run);
}
return;
} else
/* Flag the jump buffer was the last state saved: */
_thread_run->sig_saved = 0;
void
_thread_kern_scheduler(void)
{
struct pthread_signal_frame *psf;
struct timespec ts;
struct timeval tv;
pthread_t pthread, pthread_h;
unsigned int current_tick;
int add_to_prioq;
/* If the currently running thread is a user thread, save it: */
if ((_thread_run->flags & PTHREAD_FLAGS_PRIVATE) == 0)
_last_user_thread = _thread_run;
/* Are there pending signals for this thread? */
if (_thread_run->check_pending != 0) {
_thread_run->check_pending = 0;
_thread_sig_check_pending(_thread_run);
}
/*
* Enter a scheduling loop that finds the next thread that is
* ready to run. This loop completes when there are no more threads
@ -154,29 +209,37 @@ __asm__("fnsave %0": :"m"(*fdata));
*/
while (!(TAILQ_EMPTY(&_thread_list))) {
/* Get the current time of day: */
gettimeofday(&tv, NULL);
GET_CURRENT_TOD(tv);
TIMEVAL_TO_TIMESPEC(&tv, &ts);
current_tick = _sched_ticks;
/*
* Protect the scheduling queues from access by the signal
* handler.
*/
_queue_signals = 1;
add_to_prioq = 0;
if (_thread_run != &_thread_kern_thread) {
/*
* This thread no longer needs to yield the CPU.
*/
_thread_run->yield_on_sig_undefer = 0;
/*
* Save the current time as the time that the thread
* became inactive:
*/
_thread_run->last_inactive.tv_sec = tv.tv_sec;
_thread_run->last_inactive.tv_usec = tv.tv_usec;
if (_thread_run->state != PS_RUNNING) {
/*
* Save the current time as the time that the
* thread became inactive:
*/
_thread_run->last_inactive = (long)current_tick;
if (_thread_run->last_inactive <
_thread_run->last_active) {
/* Account for a rollover: */
_thread_run->last_inactive =+
UINT_MAX + 1;
}
}
/*
* Place the currently running thread into the
* appropriate queue(s).
@ -198,22 +261,7 @@ __asm__("fnsave %0": :"m"(*fdata));
* are polled (to preserve round-robin
* scheduling).
*/
if ((_thread_run->slice_usec != -1) &&
(_thread_run->attr.sched_policy != SCHED_FIFO)) {
/*
* Accumulate the number of microseconds that
* this thread has run for:
*/
_thread_run->slice_usec +=
(_thread_run->last_inactive.tv_sec -
_thread_run->last_active.tv_sec) * 1000000 +
_thread_run->last_inactive.tv_usec -
_thread_run->last_active.tv_usec;
/* Check for time quantum exceeded: */
if (_thread_run->slice_usec > TIMESLICE_USEC)
_thread_run->slice_usec = -1;
}
add_to_prioq = 1;
break;
/*
@ -260,7 +308,7 @@ __asm__("fnsave %0": :"m"(*fdata));
/* Increment spinblock count: */
_spinblock_count++;
/* fall through */
/* FALLTHROUGH */
case PS_FDR_WAIT:
case PS_FDW_WAIT:
case PS_POLL_WAIT:
@ -277,17 +325,26 @@ __asm__("fnsave %0": :"m"(*fdata));
}
}
/* Unprotect the scheduling queues: */
_queue_signals = 0;
/*
* Poll file descriptors to update the state of threads
* waiting on file I/O where data may be available:
* Poll file descriptors only if a new scheduling signal
* has occurred or if we have no more runnable threads.
*/
_thread_kern_poll(0);
if (((current_tick = _sched_ticks) != last_tick) ||
((_thread_run->state != PS_RUNNING) &&
(PTHREAD_PRIOQ_FIRST() == NULL))) {
/* Unprotect the scheduling queues: */
_queue_signals = 0;
/* Protect the scheduling queues: */
_queue_signals = 1;
/*
* Poll file descriptors to update the state of threads
* waiting on file I/O where data may be available:
*/
thread_kern_poll(0);
/* Protect the scheduling queues: */
_queue_signals = 1;
}
last_tick = current_tick;
/*
* Wake up threads that have timedout. This has to be
@ -329,12 +386,37 @@ __asm__("fnsave %0": :"m"(*fdata));
PTHREAD_WAITQ_CLEARACTIVE();
/*
* Check if there is a current runnable thread that isn't
* already in the ready queue:
* Check to see if the current thread needs to be added
* to the priority queue:
*/
if ((_thread_run != &_thread_kern_thread) &&
(_thread_run->state == PS_RUNNING) &&
((_thread_run->flags & PTHREAD_FLAGS_IN_PRIOQ) == 0)) {
if (add_to_prioq != 0) {
/*
* Save the current time as the time that the
* thread became inactive:
*/
current_tick = _sched_ticks;
_thread_run->last_inactive = (long)current_tick;
if (_thread_run->last_inactive <
_thread_run->last_active) {
/* Account for a rollover: */
_thread_run->last_inactive =+ UINT_MAX + 1;
}
if ((_thread_run->slice_usec != -1) &&
(_thread_run->attr.sched_policy != SCHED_FIFO)) {
/*
* Accumulate the number of microseconds for
* which the current thread has run:
*/
_thread_run->slice_usec +=
(_thread_run->last_inactive -
_thread_run->last_active) *
(long)_clock_res_usec;
/* Check for time quantum exceeded: */
if (_thread_run->slice_usec > TIMESLICE_USEC)
_thread_run->slice_usec = -1;
}
if (_thread_run->slice_usec == -1) {
/*
* The thread exceeded its time
@ -366,6 +448,8 @@ __asm__("fnsave %0": :"m"(*fdata));
* thread structure:
*/
_thread_run = &_thread_kern_thread;
DBG_MSG("No runnable threads, using kernel thread %p\n",
_thread_run);
/* Unprotect the scheduling queues: */
_queue_signals = 0;
@ -374,21 +458,28 @@ __asm__("fnsave %0": :"m"(*fdata));
* There are no threads ready to run, so wait until
* something happens that changes this condition:
*/
_thread_kern_poll(1);
} else {
thread_kern_poll(1);
/*
* This process' usage will likely be very small
* while waiting in a poll. Since the scheduling
* clock is based on the profiling timer, it is
* unlikely that the profiling timer will fire
* and update the time of day. To account for this,
* get the time of day after polling with a timeout.
*/
gettimeofday((struct timeval *) &_sched_tod, NULL);
/* Check once more for a runnable thread: */
_queue_signals = 1;
pthread_h = PTHREAD_PRIOQ_FIRST();
_queue_signals = 0;
}
if (pthread_h != NULL) {
/* Remove the thread from the ready queue: */
PTHREAD_PRIOQ_REMOVE(pthread_h);
/* Get first thread on the waiting list: */
pthread = TAILQ_FIRST(&_waitingq);
/* Check to see if there is more than one thread: */
if (pthread_h != TAILQ_FIRST(&_thread_list) ||
TAILQ_NEXT(pthread_h, tle) != NULL)
set_timer = 1;
else
set_timer = 0;
/* Unprotect the scheduling queues: */
_queue_signals = 0;
@ -411,32 +502,19 @@ __asm__("fnsave %0": :"m"(*fdata));
*/
if (((pthread = PTHREAD_PRIOQ_FIRST()) != NULL) &&
(pthread->active_priority > pthread_h->active_priority)) {
/* Remove the thread from the ready queue: */
PTHREAD_PRIOQ_REMOVE(pthread);
/*
* Insert the lower priority thread
* at the head of its priority list:
*/
PTHREAD_PRIOQ_INSERT_HEAD(pthread_h);
/* Remove the thread from the ready queue: */
PTHREAD_PRIOQ_REMOVE(pthread);
/* There's a new thread in town: */
pthread_h = pthread;
}
/* Get first thread on the waiting list: */
pthread = TAILQ_FIRST(&_waitingq);
/*
* Check to see if there is more than one
* thread:
*/
if (pthread_h != TAILQ_FIRST(&_thread_list) ||
TAILQ_NEXT(pthread_h, tle) != NULL)
set_timer = 1;
else
set_timer = 0;
/* Unprotect the scheduling queues: */
_queue_signals = 0;
}
@ -448,78 +526,8 @@ __asm__("fnsave %0": :"m"(*fdata));
* Save the current time as the time that the thread
* became active:
*/
_thread_run->last_active.tv_sec = tv.tv_sec;
_thread_run->last_active.tv_usec = tv.tv_usec;
/*
* Define the maximum time before a scheduling signal
* is required:
*/
itimer.it_value.tv_sec = 0;
itimer.it_value.tv_usec = TIMESLICE_USEC;
/*
* The interval timer is not reloaded when it
* times out. The interval time needs to be
* calculated every time.
*/
itimer.it_interval.tv_sec = 0;
itimer.it_interval.tv_usec = 0;
/* Get first thread on the waiting list: */
if ((pthread != NULL) &&
(pthread->wakeup_time.tv_sec != -1)) {
/*
* Calculate the time until this thread
* is ready, allowing for the clock
* resolution:
*/
ts1.tv_sec = pthread->wakeup_time.tv_sec
- ts.tv_sec;
ts1.tv_nsec = pthread->wakeup_time.tv_nsec
- ts.tv_nsec + _clock_res_nsec;
/*
* Check for underflow of the nanosecond field:
*/
while (ts1.tv_nsec < 0) {
/*
* Allow for the underflow of the
* nanosecond field:
*/
ts1.tv_sec--;
ts1.tv_nsec += 1000000000;
}
/*
* Check for overflow of the nanosecond field:
*/
while (ts1.tv_nsec >= 1000000000) {
/*
* Allow for the overflow of the
* nanosecond field:
*/
ts1.tv_sec++;
ts1.tv_nsec -= 1000000000;
}
/*
* Convert the timespec structure to a
* timeval structure:
*/
TIMESPEC_TO_TIMEVAL(&tv1, &ts1);
/*
* Check if the thread will be ready
* sooner than the earliest ones found
* so far:
*/
if (timercmp(&tv1, &itimer.it_value, <)) {
/*
* Update the time value:
*/
itimer.it_value.tv_sec = tv1.tv_sec;
itimer.it_value.tv_usec = tv1.tv_usec;
}
}
current_tick = _sched_ticks;
_thread_run->last_active = (long) current_tick;
/*
* Check if this thread is running for the first time
@ -531,88 +539,51 @@ __asm__("fnsave %0": :"m"(*fdata));
_thread_run->slice_usec = 0;
}
/* Check if there is more than one thread: */
if (set_timer != 0) {
/*
* Start the interval timer for the
* calculated time interval:
*/
if (setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL) != 0) {
/*
* Cannot initialise the timer, so
* abort this process:
*/
PANIC("Cannot set scheduling timer");
}
/*
* If we had a context switch, run any
* installed switch hooks.
*/
if ((_sched_switch_hook != NULL) &&
(_last_user_thread != _thread_run)) {
thread_run_switch_hook(_last_user_thread,
_thread_run);
}
/*
* Continue the thread at its current frame:
*/
psf = _thread_run->curframe;
switch(psf->ctxtype) {
case CTX_JB_NOSIG:
___longjmp(psf->ctx.jb, psf->longjmp_val);
break;
case CTX_JB:
__longjmp(psf->ctx.jb, psf->longjmp_val);
break;
case CTX_SJB:
__siglongjmp(psf->ctx.sigjb, psf->longjmp_val);
break;
case CTX_UC:
/* XXX - Restore FP regsisters? */
FP_RESTORE_UC(&psf->ctx.uc);
/*
* Check if this thread is being continued from a
* longjmp() out of a signal handler:
*/
if ((_thread_run->jmpflags & JMPFLAGS_LONGJMP) != 0) {
_thread_run->jmpflags = 0;
__longjmp(_thread_run->nested_jmp.jmp,
_thread_run->longjmp_val);
}
/*
* Check if this thread is being continued from a
* _longjmp() out of a signal handler:
*/
else if ((_thread_run->jmpflags & JMPFLAGS__LONGJMP) !=
0) {
_thread_run->jmpflags = 0;
___longjmp(_thread_run->nested_jmp.jmp,
_thread_run->longjmp_val);
}
/*
* Check if this thread is being continued from a
* siglongjmp() out of a signal handler:
*/
else if ((_thread_run->jmpflags & JMPFLAGS_SIGLONGJMP)
!= 0) {
_thread_run->jmpflags = 0;
__siglongjmp(
_thread_run->nested_jmp.sigjmp,
_thread_run->longjmp_val);
}
/* Check if a signal context was saved: */
else if (_thread_run->sig_saved == 1) {
#ifndef __alpha__
/*
* Point to the floating point data in the
* running thread:
*/
fdata = _thread_run->saved_fp;
/* Restore the floating point state: */
__asm__("frstor %0": :"m"(*fdata));
#endif
/*
* Do a sigreturn to restart the thread that
* was interrupted by a signal:
*/
_thread_kern_in_sched = 0;
#if NOT_YET
_setcontext(&psf->ctx.uc);
#else
/*
* If we had a context switch, run any
* installed switch hooks.
* Ensure the process signal mask is set
* correctly:
*/
if ((_sched_switch_hook != NULL) &&
(_last_user_thread != _thread_run)) {
thread_run_switch_hook(_last_user_thread,
_thread_run);
}
_thread_sys_sigreturn(&_thread_run->saved_sigcontext);
} else {
/*
* Do a longjmp to restart the thread that
* was context switched out (by a longjmp to
* a different thread):
*/
__longjmp(_thread_run->saved_jmp_buf, 1);
psf->ctx.uc.uc_sigmask = _process_sigmask;
_thread_sys_sigreturn(&psf->ctx.uc);
#endif
break;
}
/* This point should not be reached. */
PANIC("Thread has returned from sigreturn or longjmp");
}
@ -645,7 +616,6 @@ _thread_kern_sched_state(enum pthread_state state, char *fname, int lineno)
/* Schedule the next thread that is ready: */
_thread_kern_sched(NULL);
return;
}
void
@ -675,11 +645,10 @@ _thread_kern_sched_state_unlock(enum pthread_state state,
/* Schedule the next thread that is ready: */
_thread_kern_sched(NULL);
return;
}
static void
_thread_kern_poll(int wait_reqd)
thread_kern_poll(int wait_reqd)
{
int count = 0;
int i, found;
@ -696,7 +665,7 @@ _thread_kern_poll(int wait_reqd)
}
else {
/* Get the current time of day: */
gettimeofday(&tv, NULL);
GET_CURRENT_TOD(tv);
TIMEVAL_TO_TIMESPEC(&tv, &ts);
_queue_signals = 1;
@ -713,11 +682,11 @@ _thread_kern_poll(int wait_reqd)
else {
/*
* Calculate the time left for the next thread to
* timeout allowing for the clock resolution:
* timeout:
*/
timeout_ms = ((pthread->wakeup_time.tv_sec - ts.tv_sec) *
1000) + ((pthread->wakeup_time.tv_nsec - ts.tv_nsec +
_clock_res_nsec) / 1000000);
1000) + ((pthread->wakeup_time.tv_nsec - ts.tv_nsec) /
1000000);
/*
* Don't allow negative timeouts:
*/
@ -1002,9 +971,6 @@ _thread_kern_poll(int wait_reqd)
/* Unprotect the scheduling queues: */
_queue_signals = 0;
}
/* Nothing to return. */
return;
}
void
@ -1032,7 +998,7 @@ _thread_kern_set_timeout(const struct timespec * timeout)
_thread_run->wakeup_time.tv_nsec = 0;
} else {
/* Get the current time: */
gettimeofday(&tv, NULL);
GET_CURRENT_TOD(tv);
TIMEVAL_TO_TIMESPEC(&tv, &current_time);
/* Calculate the time for the current thread to wake up: */
@ -1046,7 +1012,6 @@ _thread_kern_set_timeout(const struct timespec * timeout)
_thread_run->wakeup_time.tv_nsec -= 1000000000;
}
}
return;
}
void
@ -1059,9 +1024,6 @@ _thread_kern_sig_defer(void)
void
_thread_kern_sig_undefer(void)
{
pthread_t pthread;
int need_resched = 0;
/*
* Perform checks to yield only if we are about to undefer
* signals.
@ -1077,33 +1039,25 @@ _thread_kern_sig_undefer(void)
/*
* Check if there are queued signals:
*/
while (_sigq_check_reqd != 0) {
/* Defer scheduling while we process queued signals: */
_thread_run->sig_defer_count = 1;
if (_sigq_check_reqd != 0)
_thread_kern_sched(NULL);
/* Clear the flag before checking the signal queue: */
_sigq_check_reqd = 0;
/*
* Check for asynchronous cancellation before delivering any
* pending signals:
*/
if (((_thread_run->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0) &&
((_thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0))
pthread_testcancel();
/* Dequeue and handle signals: */
dequeue_signals();
/*
* Avoiding an unnecessary check to reschedule, check
* to see if signal handling caused a higher priority
* thread to become ready.
*/
if ((need_resched == 0) &&
(((pthread = PTHREAD_PRIOQ_FIRST()) != NULL) &&
(pthread->active_priority > _thread_run->active_priority))) {
need_resched = 1;
}
/* Reenable signals: */
_thread_run->sig_defer_count = 0;
}
/* Yield the CPU if necessary: */
if (need_resched || _thread_run->yield_on_sig_undefer != 0) {
/*
* If there are pending signals or this thread has
* to yield the CPU, call the kernel scheduler:
*
* XXX - Come back and revisit the pending signal problem
*/
if ((_thread_run->yield_on_sig_undefer != 0) ||
SIGNOTEMPTY(_thread_run->sigpend)) {
_thread_run->yield_on_sig_undefer = 0;
_thread_kern_sched(NULL);
}
@ -1114,35 +1068,13 @@ static void
dequeue_signals(void)
{
char bufr[128];
int i, num;
pthread_t pthread;
int num;
/*
* Enter a loop to read and handle queued signals from the
* pthread kernel pipe:
* Enter a loop to clear the pthread kernel pipe:
*/
while (((num = _thread_sys_read(_thread_kern_pipe[0], bufr,
sizeof(bufr))) > 0) || (num == -1 && errno == EINTR)) {
/*
* The buffer read contains one byte per signal and
* each byte is the signal number.
*/
for (i = 0; i < num; i++) {
if ((int) bufr[i] == _SCHED_SIGNAL) {
/*
* Scheduling signals shouldn't ever be
* queued; just ignore it for now.
*/
}
else {
/* Handle this signal: */
pthread = _thread_sig_handle((int) bufr[i],
NULL);
if (pthread != NULL)
_thread_sig_deliver(pthread,
(int) bufr[i]);
}
}
}
if ((num < 0) && (errno != EAGAIN)) {
/*
@ -1151,6 +1083,8 @@ dequeue_signals(void)
*/
PANIC("Unable to read from thread kernel pipe");
}
/* Handle any pending signals: */
_thread_sig_handle_pending();
}
static inline void

View file

@ -79,7 +79,7 @@ static spinlock_t static_init_lock = _SPINLOCK_INITIALIZER;
int
_mutex_reinit(pthread_mutex_t * mutex)
{
int ret = 0;
int ret = 0;
if (mutex == NULL)
ret = EINVAL;
@ -113,7 +113,7 @@ pthread_mutex_init(pthread_mutex_t * mutex,
int protocol;
int ceiling;
pthread_mutex_t pmutex;
int ret = 0;
int ret = 0;
if (mutex == NULL)
ret = EINVAL;
@ -203,7 +203,7 @@ pthread_mutex_init(pthread_mutex_t * mutex,
int
pthread_mutex_destroy(pthread_mutex_t * mutex)
{
int ret = 0;
int ret = 0;
if (mutex == NULL || *mutex == NULL)
ret = EINVAL;
@ -245,7 +245,7 @@ pthread_mutex_destroy(pthread_mutex_t * mutex)
static int
init_static(pthread_mutex_t *mutex)
{
int ret;
int ret;
_SPINLOCK(&static_init_lock);
@ -262,7 +262,7 @@ init_static(pthread_mutex_t *mutex)
int
pthread_mutex_trylock(pthread_mutex_t * mutex)
{
int ret = 0;
int ret = 0;
if (mutex == NULL)
ret = EINVAL;
@ -400,7 +400,7 @@ pthread_mutex_trylock(pthread_mutex_t * mutex)
int
pthread_mutex_lock(pthread_mutex_t * mutex)
{
int ret = 0;
int ret = 0;
if (mutex == NULL)
ret = EINVAL;
@ -610,9 +610,8 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
* Check to see if this thread was interrupted and
* is still in the mutex queue of waiting threads:
*/
if (_thread_run->interrupted != 0) {
if (_thread_run->interrupted != 0)
mutex_queue_remove(*mutex, _thread_run);
}
/* Unlock the mutex structure: */
_SPINUNLOCK(&(*mutex)->lock);
@ -647,7 +646,7 @@ _mutex_cv_unlock(pthread_mutex_t * mutex)
int
_mutex_cv_lock(pthread_mutex_t * mutex)
{
int ret;
int ret;
if ((ret = pthread_mutex_lock(mutex)) == 0)
(*mutex)->m_refcount--;
return (ret);
@ -656,7 +655,7 @@ _mutex_cv_lock(pthread_mutex_t * mutex)
static inline int
mutex_self_trylock(pthread_mutex_t mutex)
{
int ret = 0;
int ret = 0;
switch (mutex->m_type) {
@ -723,7 +722,7 @@ mutex_self_lock(pthread_mutex_t mutex)
static inline int
mutex_unlock_common(pthread_mutex_t * mutex, int add_reference)
{
int ret = 0;
int ret = 0;
if (mutex == NULL || *mutex == NULL) {
ret = EINVAL;
@ -1369,6 +1368,38 @@ _mutex_unlock_private(pthread_t pthread)
}
}
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();
if (pthread->state == PS_MUTEX_WAIT) {
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: */
mutex->m_owner->data.mutex = NULL;
/* Unlock the mutex structure: */
_SPINUNLOCK(&mutex->lock);
}
/*
* Undefer and handle pending signals, yielding if
* necessary:
*/
_thread_kern_sig_undefer();
}
/*
* Dequeue a waiting thread from the head of a mutex queue in descending
* priority order.
@ -1379,7 +1410,7 @@ mutex_queue_deq(pthread_mutex_t mutex)
pthread_t pthread;
while ((pthread = TAILQ_FIRST(&mutex->m_queue)) != NULL) {
TAILQ_REMOVE(&mutex->m_queue, pthread, qe);
TAILQ_REMOVE(&mutex->m_queue, pthread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_MUTEXQ;
/*
@ -1400,7 +1431,7 @@ static inline void
mutex_queue_remove(pthread_mutex_t mutex, pthread_t pthread)
{
if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0) {
TAILQ_REMOVE(&mutex->m_queue, pthread, qe);
TAILQ_REMOVE(&mutex->m_queue, pthread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_MUTEXQ;
}
}
@ -1413,18 +1444,19 @@ mutex_queue_enq(pthread_mutex_t mutex, pthread_t pthread)
{
pthread_t tid = TAILQ_LAST(&mutex->m_queue, mutex_head);
PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread);
/*
* 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))
TAILQ_INSERT_TAIL(&mutex->m_queue, pthread, qe);
TAILQ_INSERT_TAIL(&mutex->m_queue, pthread, sqe);
else {
tid = TAILQ_FIRST(&mutex->m_queue);
while (pthread->active_priority <= tid->active_priority)
tid = TAILQ_NEXT(tid, qe);
TAILQ_INSERT_BEFORE(tid, pthread, qe);
tid = TAILQ_NEXT(tid, sqe);
TAILQ_INSERT_BEFORE(tid, pthread, sqe);
}
pthread->flags |= PTHREAD_FLAGS_IN_MUTEXQ;
}

View file

@ -66,9 +66,13 @@ static int _pq_active = 0;
PANIC(msg); \
} while (0)
#define _PQ_ASSERT_NOT_QUEUED(thrd, msg) do { \
if ((thrd)->flags & _PQ_IN_SCHEDQ) \
if (((thrd)->flags & _PQ_IN_SCHEDQ) != 0) \
PANIC(msg); \
} while (0)
#define _PQ_ASSERT_PROTECTED(msg) \
PTHREAD_ASSERT((_thread_kern_in_sched != 0) || \
(_thread_run->sig_defer_count > 0) || \
(_sig_in_handler != 0), msg);
#else
@ -79,11 +83,10 @@ static int _pq_active = 0;
#define _PQ_ASSERT_IN_WAITQ(thrd, msg)
#define _PQ_ASSERT_IN_PRIOQ(thrd, msg)
#define _PQ_ASSERT_NOT_QUEUED(thrd, msg)
#define _PQ_CHECK_PRIO()
#define _PQ_ASSERT_PROTECTED(msg)
#endif
int
_pq_alloc(pq_queue_t *pq, int minprio, int maxprio)
{
@ -101,9 +104,7 @@ _pq_alloc(pq_queue_t *pq, int minprio, int maxprio)
else {
/* Remember the queue size: */
pq->pq_size = prioslots;
ret = _pq_init(pq);
}
return (ret);
}
@ -142,6 +143,7 @@ _pq_remove(pq_queue_t *pq, pthread_t pthread)
_PQ_ASSERT_INACTIVE("_pq_remove: pq_active");
_PQ_SET_ACTIVE();
_PQ_ASSERT_IN_PRIOQ(pthread, "_pq_remove: Not in priority queue");
_PQ_ASSERT_PROTECTED("_pq_remove: prioq not protected!");
/*
* Remove this thread from priority list. Note that if
@ -172,6 +174,7 @@ _pq_insert_head(pq_queue_t *pq, pthread_t pthread)
_PQ_SET_ACTIVE();
_PQ_ASSERT_NOT_QUEUED(pthread,
"_pq_insert_head: Already in priority queue");
_PQ_ASSERT_PROTECTED("_pq_insert_head: prioq not protected!");
TAILQ_INSERT_HEAD(&pq->pq_lists[prio].pl_head, pthread, pqe);
if (pq->pq_lists[prio].pl_queued == 0)
@ -197,6 +200,7 @@ _pq_insert_tail(pq_queue_t *pq, pthread_t pthread)
_PQ_SET_ACTIVE();
_PQ_ASSERT_NOT_QUEUED(pthread,
"_pq_insert_tail: Already in priority queue");
_PQ_ASSERT_PROTECTED("_pq_insert_tail: prioq not protected!");
TAILQ_INSERT_TAIL(&pq->pq_lists[prio].pl_head, pthread, pqe);
if (pq->pq_lists[prio].pl_queued == 0)
@ -221,6 +225,7 @@ _pq_first(pq_queue_t *pq)
*/
_PQ_ASSERT_INACTIVE("_pq_first: pq_active");
_PQ_SET_ACTIVE();
_PQ_ASSERT_PROTECTED("_pq_first: prioq not protected!");
while (((pql = TAILQ_FIRST(&pq->pq_queue)) != NULL) &&
(pthread == NULL)) {
@ -250,6 +255,7 @@ pq_insert_prio_list(pq_queue_t *pq, int prio)
* Make some assertions when debugging is enabled:
*/
_PQ_ASSERT_ACTIVE("pq_insert_prio_list: pq_active");
_PQ_ASSERT_PROTECTED("_pq_insert_prio_list: prioq not protected!");
/*
* The priority queue is in descending priority order. Start at
@ -270,11 +276,10 @@ pq_insert_prio_list(pq_queue_t *pq, int prio)
pq->pq_lists[prio].pl_queued = 1;
}
#if defined(_PTHREADS_INVARIANTS)
void
_waitq_insert(pthread_t pthread)
{
pthread_t tid;
pthread_t tid;
/*
* Make some assertions when debugging is enabled:
@ -332,4 +337,3 @@ _waitq_clearactive(void)
_PQ_CLEAR_ACTIVE();
}
#endif
#endif

View file

@ -29,6 +29,7 @@
* $FreeBSD$
*/
#include <stdlib.h>
#include <errno.h>
#ifdef _THREAD_SAFE
#include <semaphore.h>

View file

@ -48,7 +48,7 @@ sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr,
/* Write the headers if any. */
if ((hdtr != NULL) && (hdtr->headers != NULL)) {
if (wvret = writev(s, hdtr->headers, hdtr->hdr_cnt) == -1) {
if ((wvret = writev(s, hdtr->headers, hdtr->hdr_cnt)) == -1) {
ret = -1;
goto ERROR;
} else
@ -135,7 +135,7 @@ sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr,
if (ret == 0) {
/* Write the trailers, if any. */
if ((hdtr != NULL) && (hdtr->trailers != NULL)) {
if (wvret = writev(s, hdtr->trailers, hdtr->trl_cnt)
if ((wvret = writev(s, hdtr->trailers, hdtr->trl_cnt))
== -1)
ret = -1;
else

View file

@ -59,7 +59,8 @@ pthread_setschedparam(pthread_t pthread, int policy,
*/
_thread_kern_sig_defer();
if (param->sched_priority != pthread->base_priority) {
if (param->sched_priority !=
PTHREAD_BASE_PRIORITY(pthread->base_priority)) {
/*
* Remove the thread from its current priority
* queue before any adjustments are made to its
@ -72,6 +73,8 @@ pthread_setschedparam(pthread_t pthread, int policy,
}
/* Set the thread base priority: */
pthread->base_priority &=
(PTHREAD_SIGNAL_PRIORITY | PTHREAD_RT_PRIORITY);
pthread->base_priority = param->sched_priority;
/* Recalculate the active priority: */

File diff suppressed because it is too large Load diff

View file

@ -74,12 +74,13 @@ _sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
*/
if (act != NULL && sig != _SCHED_SIGNAL && sig != SIGCHLD &&
sig != SIGINFO) {
/* Initialise the global signal action structure: */
gact.sa_mask = act->sa_mask;
gact.sa_flags = 0;
/* Ensure the scheduling signal is masked: */
sigaddset(&gact.sa_mask, _SCHED_SIGNAL);
/*
* Ensure the signal handler cannot be interrupted
* by other signals. Always request the POSIX signal
* handler arguments.
*/
sigfillset(&gact.sa_mask);
gact.sa_flags = SA_SIGINFO;
/*
* Check if the signal handler is being set to

View file

@ -43,7 +43,8 @@
int
pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
{
int ret = 0;
sigset_t sigset;
int ret = 0;
/* Check if the existing signal process mask is to be returned: */
if (oset != NULL) {
@ -81,10 +82,18 @@ pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
}
/*
* Dispatch signals to the running thread that are pending
* and now unblocked:
* Check if there are pending signals for the running
* thread or process that aren't blocked:
*/
_dispatch_signals();
sigset = _thread_run->sigpend;
SIGSETOR(sigset, _process_sigpending);
SIGSETNAND(sigset, _thread_run->sigmask);
if (SIGNOTEMPTY(sigset))
/*
* Call the kernel scheduler which will safely
* install a signal frame for the running thread:
*/
_thread_kern_sched_sig();
}
/* Return the completion status: */

View file

@ -45,7 +45,7 @@ _thread_sys_signal(int s, sig_t a)
/* Initialise the signal action structure: */
sigemptyset(&sa.sa_mask);
sa.sa_handler = a;
sa.sa_flags = 0;
sa.sa_flags = SA_SIGINFO;
/* Perform the sigaction syscall: */
if (_thread_sys_sigaction(s, &sa, &osa) < 0) {

View file

@ -41,53 +41,9 @@
#include "pthread_private.h"
int
_sigprocmask(int how, const sigset_t * set, sigset_t * oset)
_sigprocmask(int how, const sigset_t *set, sigset_t *oset)
{
int ret = 0;
/* Check if the existing signal process mask is to be returned: */
if (oset != NULL) {
/* Return the current mask: */
*oset = _thread_run->sigmask;
}
/* Check if a new signal set was provided by the caller: */
if (set != NULL) {
/* Process according to what to do: */
switch (how) {
/* Block signals: */
case SIG_BLOCK:
/* Add signals to the existing mask: */
SIGSETOR(_thread_run->sigmask, *set);
break;
/* Unblock signals: */
case SIG_UNBLOCK:
/* Clear signals from the existing mask: */
SIGSETNAND(_thread_run->sigmask, *set);
break;
/* Set the signal process mask: */
case SIG_SETMASK:
/* Set the new mask: */
_thread_run->sigmask = *set;
break;
/* Trap invalid actions: */
default:
/* Return an invalid argument: */
errno = EINVAL;
ret = -1;
break;
}
/*
* Dispatch signals to the running thread that are pending
* and now unblocked:
*/
_dispatch_signals();
}
/* Return the completion status: */
return (ret);
return (pthread_sigmask(how, set, oset));
}
__strong_reference(_sigprocmask, sigprocmask);

View file

@ -40,7 +40,7 @@
#include "pthread_private.h"
int
sigwait(const sigset_t * set, int *sig)
sigwait(const sigset_t *set, int *sig)
{
int ret = 0;
int i;
@ -52,11 +52,9 @@ sigwait(const sigset_t * set, int *sig)
* Specify the thread kernel signal handler.
*/
act.sa_handler = (void (*) ()) _thread_sig_handler;
act.sa_flags = SA_RESTART;
act.sa_mask = *set;
/* Ensure the scheduling signal is masked: */
sigaddset(&act.sa_mask, _SCHED_SIGNAL);
act.sa_flags = SA_RESTART | SA_SIGINFO;
/* Ensure the signal handler cannot be interrupted by other signals: */
sigfillset(&act.sa_mask);
/*
* Initialize the set of signals that will be waited on:

View file

@ -127,7 +127,7 @@ _write(int fd, const void *buf, size_t nbytes)
/* Return the number of bytes written: */
ret = num;
}
_FD_UNLOCK(fd, FD_RDWR);
_FD_UNLOCK(fd, FD_WRITE);
}
return (ret);
}

View file

@ -57,8 +57,5 @@ pthread_yield(void)
/* Schedule the next thread: */
_thread_kern_sched(NULL);
/* Nothing to return. */
return;
}
#endif

View file

@ -45,6 +45,10 @@ pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param
ret = EINVAL;
else if (param == NULL) {
ret = ENOTSUP;
} else if ((param->sched_priority < PTHREAD_MIN_PRIORITY) ||
(param->sched_priority > PTHREAD_MAX_PRIORITY)) {
/* Return an unsupported value error. */
ret = ENOTSUP;
} else
(*attr)->prio = param->sched_priority;

View file

@ -170,10 +170,7 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
* perform the dynamic initialization:
*/
else if (*cond != NULL ||
(rval = pthread_cond_init(cond,NULL)) == 0) {
_thread_enter_cancellation_point();
(rval = pthread_cond_init(cond, NULL)) == 0) {
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
@ -286,8 +283,6 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
if (_thread_run->continuation != NULL)
_thread_run->continuation((void *) _thread_run);
}
_thread_leave_cancellation_point();
}
_thread_leave_cancellation_point();
@ -313,8 +308,6 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
* initialization.
*/
else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
_thread_enter_cancellation_point();
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
@ -446,8 +439,6 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
if (_thread_run->continuation != NULL)
_thread_run->continuation((void *) _thread_run);
}
_thread_leave_cancellation_point();
}
_thread_leave_cancellation_point();
@ -589,6 +580,48 @@ pthread_cond_broadcast(pthread_cond_t * cond)
return (rval);
}
void
_cond_wait_backout(pthread_t pthread)
{
pthread_cond_t cond;
cond = pthread->data.cond;
if (cond != NULL) {
/*
* Defer signals to protect the scheduling queues
* from access by the signal handler:
*/
_thread_kern_sig_defer();
/* Lock the condition variable structure: */
_SPINLOCK(&cond->lock);
/* Process according to condition variable type: */
switch (cond->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
cond_queue_remove(cond, pthread);
/* Check for no more waiters: */
if (TAILQ_FIRST(&cond->c_queue) == NULL)
cond->c_mutex = NULL;
break;
default:
break;
}
/* Unlock the condition variable structure: */
_SPINUNLOCK(&cond->lock);
/*
* Undefer and handle pending signals, yielding if
* necessary:
*/
_thread_kern_sig_undefer();
}
}
/*
* Dequeue a waiting thread from the head of a condition queue in
* descending priority order.
@ -599,7 +632,7 @@ cond_queue_deq(pthread_cond_t cond)
pthread_t pthread;
while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
TAILQ_REMOVE(&cond->c_queue, pthread, qe);
TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
if ((pthread->timeout == 0) && (pthread->interrupted == 0))
/*
@ -628,7 +661,7 @@ cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
* it isn't in the queue.
*/
if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
TAILQ_REMOVE(&cond->c_queue, pthread, qe);
TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
}
}
@ -642,19 +675,22 @@ cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
{
pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread);
/*
* 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))
TAILQ_INSERT_TAIL(&cond->c_queue, pthread, qe);
TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe);
else {
tid = TAILQ_FIRST(&cond->c_queue);
while (pthread->active_priority <= tid->active_priority)
tid = TAILQ_NEXT(tid, qe);
TAILQ_INSERT_BEFORE(tid, pthread, qe);
tid = TAILQ_NEXT(tid, sqe);
TAILQ_INSERT_BEFORE(tid, pthread, sqe);
}
pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
pthread->data.cond = cond;
}
#endif

View file

@ -49,17 +49,24 @@
static u_int64_t next_uniqueid = 1;
#define OFF(f) offsetof(struct pthread, f)
#define SIGFRAME_OFF(f) offsetof(struct pthread_signal_frame, f)
int _thread_next_offset = OFF(tle.tqe_next);
int _thread_uniqueid_offset = OFF(uniqueid);
int _thread_state_offset = OFF(state);
int _thread_name_offset = OFF(name);
int _thread_sig_saved_offset = OFF(sig_saved);
int _thread_saved_sigcontext_offset = OFF(saved_sigcontext);
int _thread_saved_jmp_buf_offset = OFF(saved_jmp_buf);
int _thread_curframe_offset = OFF(curframe);
int _thread_sigframe_ctx_offset = SIGFRAME_OFF(ctx);
int _thread_sigframe_ctxtype_offset = SIGFRAME_OFF(ctxtype);
#undef OFF
#undef SIGFRAME_OFF
int _thread_PS_RUNNING_value = PS_RUNNING;
int _thread_PS_DEAD_value = PS_DEAD;
int _thread_CTX_JB_NOSIG_value = CTX_JB_NOSIG;
int _thread_CTX_JB_value = CTX_JB;
int _thread_CTX_SJB_value = CTX_SJB;
int _thread_CTX_UC_value = CTX_UC;
int _thread_sigframe_size_value = sizeof(struct pthread_signal_frame);
int
pthread_create(pthread_t * thread, const pthread_attr_t * attr,
@ -162,7 +169,6 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Initialise the thread structure: */
memset(new_thread, 0, sizeof(struct pthread));
new_thread->slice_usec = -1;
new_thread->sig_saved = 0;
new_thread->stack = stack;
new_thread->start_routine = start_routine;
new_thread->arg = arg;
@ -179,62 +185,32 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Initialise the thread for signals: */
new_thread->sigmask = _thread_run->sigmask;
/* Initialize the first signal frame: */
new_thread->sigframes[0] = &new_thread->sigframe0;
new_thread->curframe = &new_thread->sigframe0;
/* Initialise the jump buffer: */
setjmp(new_thread->saved_jmp_buf);
_setjmp(new_thread->curframe->ctx.jb);
/*
* Set up new stack frame so that it looks like it
* returned from a longjmp() to the beginning of
* _thread_start().
*/
#if defined(__FreeBSD__)
#if defined(__alpha__)
new_thread->saved_jmp_buf[0]._jb[2] =
(long)_thread_start;
new_thread->saved_jmp_buf[0]._jb[4 + R_RA] =
0;
new_thread->saved_jmp_buf[0]._jb[4 + R_T12] =
(long)_thread_start;
#else
new_thread->saved_jmp_buf[0]._jb[0] =
(long)_thread_start;
#endif
#elif defined(__NetBSD__)
#if defined(__alpha__)
new_thread->saved_jmp_buf[2] = (long)_thread_start;
new_thread->saved_jmp_buf[4 + R_RA] = 0;
new_thread->saved_jmp_buf[4 + R_T12] =
(long)_thread_start;
#else
new_thread->saved_jmp_buf[0] = (long)_thread_start;
#endif
#else
#error "Don't recognize this operating system!"
#endif
SET_RETURN_ADDR_JB(new_thread->curframe->ctx.jb,
_thread_start);
/* The stack starts high and builds down: */
#if defined(__FreeBSD__)
#if defined(__alpha__)
new_thread->saved_jmp_buf[0]._jb[4 + R_SP] =
(long)new_thread->stack + pattr->stacksize_attr
- sizeof(double);
#else
new_thread->saved_jmp_buf[0]._jb[2] =
(int)(new_thread->stack + pattr->stacksize_attr -
sizeof(double));
#endif
#elif defined(__NetBSD__)
#if defined(__alpha__)
new_thread->saved_jmp_buf[4 + R_SP] =
(long)new_thread->stack + pattr->stacksize_attr -
sizeof(double);
#else
new_thread->saved_jmp_buf[2] = (long)new_thread->stack
+ pattr->stacksize_attr - sizeof(double);
#endif
#else
#error "Don't recognize this operating system!"
#endif
SET_STACK_JB(new_thread->curframe->ctx.jb,
(long)new_thread->stack + pattr->stacksize_attr
- sizeof(double));
/* Initialize the rest of the frame: */
new_thread->curframe->ctxtype = CTX_JB_NOSIG;
/* Set the base of the stack: */
new_thread->curframe->stackp =
GET_STACK_JB(new_thread->curframe->ctx.jb);
new_thread->sigframe_count = 0;
/* Copy the thread attributes: */
memcpy(&new_thread->attr, pattr, sizeof(struct pthread_attr));
@ -245,20 +221,22 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
*/
if (new_thread->attr.flags & PTHREAD_INHERIT_SCHED) {
/* Copy the scheduling attributes: */
new_thread->base_priority
= _thread_run->base_priority;
new_thread->attr.prio
= _thread_run->base_priority;
new_thread->attr.sched_policy
= _thread_run->attr.sched_policy;
new_thread->base_priority =
_thread_run->base_priority &
~PTHREAD_SIGNAL_PRIORITY;
new_thread->attr.prio =
_thread_run->base_priority &
~PTHREAD_SIGNAL_PRIORITY;
new_thread->attr.sched_policy =
_thread_run->attr.sched_policy;
} else {
/*
* Use just the thread priority, leaving the
* other scheduling attributes as their
* default values:
*/
new_thread->base_priority
= new_thread->attr.prio;
new_thread->base_priority =
new_thread->attr.prio;
}
new_thread->active_priority = new_thread->base_priority;
new_thread->inherited_priority = 0;
@ -275,7 +253,6 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
new_thread->flags = 0;
new_thread->poll_data.nfds = 0;
new_thread->poll_data.fds = NULL;
new_thread->jmpflags = 0;
new_thread->continuation = NULL;
/*
@ -317,7 +294,6 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Schedule the new user thread: */
_thread_kern_sched(NULL);
/*
* Start a garbage collector thread
* if necessary.
@ -325,6 +301,7 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
if (f_gc && pthread_create(&gc_thread,NULL,
_thread_gc,NULL) != 0)
PANIC("Can't create gc thread");
}
}

View file

@ -61,9 +61,10 @@ pthread_detach(pthread_t pthread)
/* Enter a loop to bring all threads off the join queue: */
while ((next_thread = TAILQ_FIRST(&pthread->join_queue)) != NULL) {
/* Remove the thread from the queue: */
TAILQ_REMOVE(&pthread->join_queue, next_thread, qe);
TAILQ_REMOVE(&pthread->join_queue, next_thread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
/* Make the thread run: */
/* Make the thread runnable: */
PTHREAD_NEW_STATE(next_thread,PS_RUNNING);
}

View file

@ -41,6 +41,9 @@
#include <pthread.h>
#include "pthread_private.h"
#define FLAGS_IN_SCHEDQ \
(PTHREAD_FLAGS_IN_PRIOQ|PTHREAD_FLAGS_IN_WAITQ|PTHREAD_FLAGS_IN_WORKQ)
void __exit(int status)
{
int flags;
@ -138,7 +141,7 @@ _thread_exit_cleanup(void)
void
pthread_exit(void *status)
{
pthread_t pthread;
int frame;
/* Check if this thread is already in the process of exiting: */
if ((_thread_run->flags & PTHREAD_EXITING) != 0) {
@ -172,25 +175,24 @@ pthread_exit(void *status)
_thread_run->poll_data.fds = NULL;
}
/*
* Defer signals to protect the scheduling queues from access
* by the signal handler:
*/
_thread_kern_sig_defer();
/* Check if there are any threads joined to this one: */
while ((pthread = TAILQ_FIRST(&(_thread_run->join_queue))) != NULL) {
/* Remove the thread from the queue: */
TAILQ_REMOVE(&_thread_run->join_queue, pthread, qe);
/* Wake the joined thread and let it detach this thread: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
if ((frame = _thread_run->sigframe_count) == 0)
_thread_exit_finish();
else {
/*
* Jump back and unwind the signal frames to gracefully
* cleanup.
*/
___longjmp(*_thread_run->sigframes[frame]->sig_jb, 1);
}
/*
* Undefer and handle pending signals, yielding if necessary:
*/
_thread_kern_sig_undefer();
/* This point should not be reached. */
PANIC("Dead thread has resumed");
}
void
_thread_exit_finish(void)
{
pthread_t pthread;
/*
* Lock the garbage collector mutex to ensure that the garbage
@ -202,20 +204,6 @@ pthread_exit(void *status)
/* Add this thread to the list of dead threads. */
TAILQ_INSERT_HEAD(&_dead_list, _thread_run, dle);
/*
* Defer signals to protect the scheduling queues from access
* by the signal handler:
*/
_thread_kern_sig_defer();
/* Remove this thread from the thread list: */
TAILQ_REMOVE(&_thread_list, _thread_run, tle);
/*
* Undefer and handle pending signals, yielding if necessary:
*/
_thread_kern_sig_undefer();
/*
* Signal the garbage collector thread that there is something
* to clean up.
@ -224,19 +212,33 @@ pthread_exit(void *status)
PANIC("Cannot signal gc cond");
/*
* Mark the thread as dead so it will not return if it
* gets context switched out when the mutex is unlocked.
* Avoid a race condition where a scheduling signal can occur
* causing the garbage collector thread to run. If this happens,
* the current thread can be cleaned out from under us.
*/
PTHREAD_SET_STATE(_thread_run, PS_DEAD);
_thread_kern_sig_defer();
/* Unlock the garbage collector mutex: */
if (pthread_mutex_unlock(&_gc_mutex) != 0)
PANIC("Cannot lock gc mutex");
/* This this thread will never be re-scheduled. */
_thread_kern_sched(NULL);
/* Check if there are any threads joined to this one: */
while ((pthread = TAILQ_FIRST(&(_thread_run->join_queue))) != NULL) {
/* Remove the thread from the queue: */
TAILQ_REMOVE(&_thread_run->join_queue, pthread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
/* This point should not be reached. */
PANIC("Dead thread has resumed");
/*
* Wake the joined thread and let it
* detach this thread:
*/
PTHREAD_NEW_STATE(pthread, PS_RUNNING);
}
/* Remove this thread from the thread list: */
TAILQ_REMOVE(&_thread_list, _thread_run, tle);
/* This thread will never be re-scheduled. */
_thread_kern_sched_state(PS_DEAD, __FILE__, __LINE__);
}
#endif

View file

@ -183,9 +183,6 @@ _fork(void)
/* Don't queue signals yet: */
_queue_signals = 0;
/* Initialize signal handling: */
_thread_sig_init();
/* Initialize the scheduling switch hook routine: */
_sched_switch_hook = NULL;

View file

@ -49,7 +49,8 @@ pthread_getschedparam(pthread_t pthread, int *policy,
/* Find the thread in the list of active threads: */
else if ((ret = _find_thread(pthread)) == 0) {
/* Return the threads base priority and scheduling policy: */
param->sched_priority = pthread->base_priority;
param->sched_priority =
PTHREAD_BASE_PRIORITY(pthread->base_priority);
*policy = pthread->attr.sched_policy;
}

View file

@ -32,6 +32,7 @@
* $FreeBSD$
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
@ -296,7 +297,6 @@ _thread_dump_info(void)
/* Close the dump file: */
_thread_sys_close(fd);
}
return;
}
/* Set the thread name for debug: */

View file

@ -90,9 +90,9 @@ _thread_init(void)
int i;
size_t len;
int mib[2];
struct timeval tv;
struct clockinfo clockinfo;
struct sigaction act;
struct itimerval itimer;
/* Check if this function has already been called: */
if (_thread_initial)
@ -160,7 +160,7 @@ _thread_init(void)
PANIC("Cannot get kernel write pipe flags");
}
/* Allocate and initialize the ready queue: */
else if (_pq_alloc(&_readyq, PTHREAD_MIN_PRIORITY, PTHREAD_MAX_PRIORITY) != 0) {
else if (_pq_alloc(&_readyq, PTHREAD_MIN_PRIORITY, PTHREAD_LAST_PRIORITY) != 0) {
/* Abort this application: */
PANIC("Cannot allocate priority ready queue.");
}
@ -171,7 +171,11 @@ _thread_init(void)
* abort:
*/
PANIC("Cannot allocate memory for initial thread");
} else {
}
/* Allocate memory for the scheduler stack: */
else if ((_thread_kern_sched_stack = malloc(PAGE_SIZE * 10)) == NULL)
PANIC("Failed to allocate stack for scheduler");
else {
/* Zero the global kernel thread structure: */
memset(&_thread_kern_thread, 0, sizeof(struct pthread));
_thread_kern_thread.flags = PTHREAD_FLAGS_PRIVATE;
@ -211,6 +215,12 @@ _thread_init(void)
_thread_initial->attr.stackaddr_attr = _thread_initial->stack;
_thread_initial->attr.stacksize_attr = PTHREAD_STACK_INITIAL;
/* Setup the context for the scheduler: */
_setjmp(_thread_kern_sched_jb);
SET_STACK_JB(_thread_kern_sched_jb,
_thread_kern_sched_stack + PAGE_SIZE*10 - sizeof(double));
SET_RETURN_ADDR_JB(_thread_kern_sched_jb, _thread_kern_scheduler);
/*
* Write a magic value to the thread structure
* to help identify valid ones:
@ -236,10 +246,19 @@ _thread_init(void)
TAILQ_INIT(&(_thread_initial->mutexq));
_thread_initial->priority_mutex_count = 0;
/* Initialize last active time to now: */
gettimeofday(&tv, NULL);
_thread_initial->last_active.tv_sec = tv.tv_sec;
_thread_initial->last_active.tv_usec = tv.tv_usec;
/* Initialize the global scheduling time: */
_sched_ticks = 0;
gettimeofday((struct timeval *) &_sched_tod, NULL);
/* Initialize last active: */
_thread_initial->last_active = (long) _sched_ticks;
/* Initialize the initial signal frame: */
_thread_initial->sigframes[0] = &_thread_initial->sigframe0;
_thread_initial->curframe = &_thread_initial->sigframe0;
_thread_initial->curframe->ctxtype = CTX_JB_NOSIG;
/* Set the base of the stack: */
_thread_initial->curframe->stackp = (unsigned long) USRSTACK;
/* Initialise the rest of the fields: */
_thread_initial->poll_data.nfds = 0;
@ -257,10 +276,13 @@ _thread_init(void)
/* Initialise the global signal action structure: */
sigfillset(&act.sa_mask);
act.sa_handler = (void (*) ()) _thread_sig_handler;
act.sa_flags = 0;
act.sa_flags = SA_SIGINFO;
/* Initialize signal handling: */
_thread_sig_init();
/* Clear pending signals for the process: */
sigemptyset(&_process_sigpending);
/* Clear the signal queue: */
memset(_thread_sigq, 0, sizeof(_thread_sigq));
/* Enter a loop to get the existing signal status: */
for (i = 1; i < NSIG; i++) {
@ -295,13 +317,19 @@ _thread_init(void)
*/
PANIC("Cannot initialise signal handler");
}
_thread_sigact[_SCHED_SIGNAL - 1].sa_flags = SA_SIGINFO;
_thread_sigact[SIGINFO - 1].sa_flags = SA_SIGINFO;
_thread_sigact[SIGCHLD - 1].sa_flags = SA_SIGINFO;
/* Get the process signal mask: */
_thread_sys_sigprocmask(SIG_SETMASK, NULL, &_process_sigmask);
/* Get the kernel clockrate: */
mib[0] = CTL_KERN;
mib[1] = KERN_CLOCKRATE;
len = sizeof (struct clockinfo);
if (sysctl(mib, 2, &clockinfo, &len, NULL, 0) == 0)
_clock_res_nsec = clockinfo.tick * 1000;
_clock_res_usec = clockinfo.tick;
/* Get the table size: */
if ((_thread_dtablesize = getdtablesize()) < 0) {
@ -346,6 +374,14 @@ _thread_init(void)
PANIC("Cannot initialize stdio file "
"descriptor table entry");
}
/* Install the scheduling timer: */
itimer.it_interval.tv_sec = 0;
itimer.it_interval.tv_usec = _clock_res_usec;
itimer.it_value = itimer.it_interval;
if (setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL) != 0)
PANIC("Cannot set interval timer");
}
}
@ -362,10 +398,6 @@ _thread_init(void)
if (pthread_mutex_init(&_gc_mutex,NULL) != 0 ||
pthread_cond_init(&_gc_cond,NULL) != 0)
PANIC("Failed to initialise garbage collector mutex or condvar");
gettimeofday(&kern_inc_prio_time, NULL);
return;
}
/*

View file

@ -40,7 +40,6 @@ int
pthread_join(pthread_t pthread, void **thread_return)
{
int ret = 0;
pthread_t pthread1 = NULL;
_thread_enter_cancellation_point();
@ -62,11 +61,7 @@ pthread_join(pthread_t pthread, void **thread_return)
* Find the thread in the list of active threads or in the
* list of dead threads:
*/
if (_find_thread(pthread) == 0 ||
_find_dead_thread(pthread) == 0)
pthread1 = pthread;
if (pthread1 == NULL)
if ((_find_thread(pthread) != 0) && (_find_dead_thread(pthread) != 0))
/* Return an error: */
ret = ESRCH;
@ -77,6 +72,8 @@ pthread_join(pthread_t pthread, void **thread_return)
/* Check if the thread is not dead: */
else if (pthread->state != PS_DEAD) {
PTHREAD_ASSERT_NOT_IN_SYNCQ(_thread_run);
/* Clear the interrupted flag: */
_thread_run->interrupted = 0;
@ -87,13 +84,18 @@ pthread_join(pthread_t pthread, void **thread_return)
_thread_kern_sig_defer();
/* Add the running thread to the join queue: */
TAILQ_INSERT_TAIL(&(pthread->join_queue), _thread_run, qe);
TAILQ_INSERT_TAIL(&(pthread->join_queue), _thread_run, sqe);
_thread_run->flags |= PTHREAD_FLAGS_IN_JOINQ;
_thread_run->data.thread = pthread;
/* Schedule the next thread: */
_thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
if (_thread_run->interrupted != 0)
TAILQ_REMOVE(&(pthread->join_queue), _thread_run, qe);
if (_thread_run->interrupted != 0) {
TAILQ_REMOVE(&(pthread->join_queue), _thread_run, sqe);
_thread_run->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
}
_thread_run->data.thread = NULL;
_thread_kern_sig_undefer();
@ -122,4 +124,15 @@ pthread_join(pthread_t pthread, void **thread_return)
/* Return the completion status: */
return (ret);
}
void
_join_backout(pthread_t pthread)
{
_thread_kern_sig_defer();
if (pthread->state == PS_JOIN) {
TAILQ_REMOVE(&pthread->data.thread->join_queue, pthread, sqe);
_thread_run->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
}
_thread_kern_sig_undefer();
}
#endif

View file

@ -52,9 +52,16 @@
#include <pthread.h>
#include "pthread_private.h"
/* #define DEBUG_THREAD_KERN */
#ifdef DEBUG_THREAD_KERN
#define DBG_MSG stdout_debug
#else
#define DBG_MSG(x...)
#endif
/* Static function prototype definitions: */
static void
_thread_kern_poll(int wait_reqd);
thread_kern_poll(int wait_reqd);
static void
dequeue_signals(void);
@ -62,18 +69,39 @@ dequeue_signals(void);
static inline void
thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in);
void
_thread_kern_sched(ucontext_t * scp)
{
#ifndef __alpha__
char *fdata;
#endif
pthread_t pthread, pthread_h = NULL;
struct itimerval itimer;
struct timespec ts, ts1;
struct timeval tv, tv1;
int set_timer = 0;
/* Static variables: */
static int last_tick = 0;
/*
* This is called when a signal handler finishes and wants to
* return to a previous frame.
*/
void
_thread_kern_sched_frame(int frame)
{
/*
* Flag the pthread kernel as executing scheduler code
* to avoid a signal from interrupting this execution and
* corrupting the (soon-to-be) current frame.
*/
_thread_kern_in_sched = 1;
/* Return to the specified frame: */
_thread_run->curframe = _thread_run->sigframes[frame];
_thread_run->sigframe_count = frame;
if (_thread_run->sigframe_count == 0)
/* Restore the threads priority: */
_thread_run->active_priority &= ~PTHREAD_SIGNAL_PRIORITY;
/* Switch to the thread scheduler: */
___longjmp(_thread_kern_sched_jb, 1);
}
void
_thread_kern_sched(ucontext_t *scp)
{
/*
* Flag the pthread kernel as executing scheduler code
* to avoid a scheduler signal from interrupting this
@ -84,67 +112,94 @@ _thread_kern_sched(ucontext_t * scp)
/* Check if this function was called from the signal handler: */
if (scp != NULL) {
/*
* Copy the signal context to the current thread's jump
* buffer:
* The signal handler should have saved the state of
* the current thread. Restore the process signal
* mask.
*/
memcpy(&_thread_run->saved_sigcontext, scp, sizeof(_thread_run->saved_sigcontext));
#ifndef __alpha__
/* Point to the floating point data in the running thread: */
fdata = _thread_run->saved_fp;
/* Save the floating point data: */
__asm__("fnsave %0": :"m"(*fdata));
#endif
/* Flag the signal context as the last state saved: */
_thread_run->sig_saved = 1;
}
/* Save the state of the current thread: */
else if (setjmp(_thread_run->saved_jmp_buf) != 0) {
if (_thread_sys_sigprocmask(SIG_SETMASK,
&_process_sigmask, NULL) != 0)
PANIC("Unable to restore process mask after signal");
/*
* This point is reached when a longjmp() is called to
* restore the state of a thread.
*
* This is the normal way out of the scheduler.
* We're running on the signal stack; just call the
* kernel scheduler directly.
*/
_thread_kern_in_sched = 0;
if (((_thread_run->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0) &&
((_thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)) {
/*
* Cancellations override signals.
DBG_MSG("Entering scheduler due to signal\n");
_thread_kern_scheduler();
} else {
/* Save the state of the current thread: */
if (_setjmp(_thread_run->curframe->ctx.jb) == 0) {
/* Flag the jump buffer was the last state saved: */
_thread_run->curframe->ctxtype = CTX_JB_NOSIG;
_thread_run->curframe->longjmp_val = 1;
} else {
DBG_MSG("Returned from ___longjmp, thread %p\n",
_thread_run);
/*
* This point is reached when a longjmp() is called
* to restore the state of a thread.
*
* Stick a cancellation point at the start of
* each async-cancellable thread's resumption.
*
* We allow threads woken at cancel points to do their
* own checks.
* This is the normal way out of the scheduler.
*/
pthread_testcancel();
_thread_kern_in_sched = 0;
if (_thread_run->sig_defer_count == 0) {
if (((_thread_run->cancelflags &
PTHREAD_AT_CANCEL_POINT) == 0) &&
((_thread_run->cancelflags &
PTHREAD_CANCEL_ASYNCHRONOUS) != 0))
/*
* Cancellations override signals.
*
* Stick a cancellation point at the
* start of each async-cancellable
* thread's resumption.
*
* We allow threads woken at cancel
* points to do their own checks.
*/
pthread_testcancel();
}
if (_sched_switch_hook != NULL) {
/* Run the installed switch hook: */
thread_run_switch_hook(_last_user_thread,
_thread_run);
}
return;
}
/* Switch to the thread scheduler: */
___longjmp(_thread_kern_sched_jb, 1);
}
}
/*
* Check for undispatched signals due to calls to
* pthread_kill().
*/
if (SIGNOTEMPTY(_thread_run->sigpend))
_dispatch_signals();
void
_thread_kern_sched_sig(void)
{
_thread_run->check_pending = 1;
_thread_kern_sched(NULL);
}
if (_sched_switch_hook != NULL) {
/* Run the installed switch hook: */
thread_run_switch_hook(_last_user_thread, _thread_run);
}
return;
} else
/* Flag the jump buffer was the last state saved: */
_thread_run->sig_saved = 0;
void
_thread_kern_scheduler(void)
{
struct pthread_signal_frame *psf;
struct timespec ts;
struct timeval tv;
pthread_t pthread, pthread_h;
unsigned int current_tick;
int add_to_prioq;
/* If the currently running thread is a user thread, save it: */
if ((_thread_run->flags & PTHREAD_FLAGS_PRIVATE) == 0)
_last_user_thread = _thread_run;
/* Are there pending signals for this thread? */
if (_thread_run->check_pending != 0) {
_thread_run->check_pending = 0;
_thread_sig_check_pending(_thread_run);
}
/*
* Enter a scheduling loop that finds the next thread that is
* ready to run. This loop completes when there are no more threads
@ -154,29 +209,37 @@ __asm__("fnsave %0": :"m"(*fdata));
*/
while (!(TAILQ_EMPTY(&_thread_list))) {
/* Get the current time of day: */
gettimeofday(&tv, NULL);
GET_CURRENT_TOD(tv);
TIMEVAL_TO_TIMESPEC(&tv, &ts);
current_tick = _sched_ticks;
/*
* Protect the scheduling queues from access by the signal
* handler.
*/
_queue_signals = 1;
add_to_prioq = 0;
if (_thread_run != &_thread_kern_thread) {
/*
* This thread no longer needs to yield the CPU.
*/
_thread_run->yield_on_sig_undefer = 0;
/*
* Save the current time as the time that the thread
* became inactive:
*/
_thread_run->last_inactive.tv_sec = tv.tv_sec;
_thread_run->last_inactive.tv_usec = tv.tv_usec;
if (_thread_run->state != PS_RUNNING) {
/*
* Save the current time as the time that the
* thread became inactive:
*/
_thread_run->last_inactive = (long)current_tick;
if (_thread_run->last_inactive <
_thread_run->last_active) {
/* Account for a rollover: */
_thread_run->last_inactive =+
UINT_MAX + 1;
}
}
/*
* Place the currently running thread into the
* appropriate queue(s).
@ -198,22 +261,7 @@ __asm__("fnsave %0": :"m"(*fdata));
* are polled (to preserve round-robin
* scheduling).
*/
if ((_thread_run->slice_usec != -1) &&
(_thread_run->attr.sched_policy != SCHED_FIFO)) {
/*
* Accumulate the number of microseconds that
* this thread has run for:
*/
_thread_run->slice_usec +=
(_thread_run->last_inactive.tv_sec -
_thread_run->last_active.tv_sec) * 1000000 +
_thread_run->last_inactive.tv_usec -
_thread_run->last_active.tv_usec;
/* Check for time quantum exceeded: */
if (_thread_run->slice_usec > TIMESLICE_USEC)
_thread_run->slice_usec = -1;
}
add_to_prioq = 1;
break;
/*
@ -260,7 +308,7 @@ __asm__("fnsave %0": :"m"(*fdata));
/* Increment spinblock count: */
_spinblock_count++;
/* fall through */
/* FALLTHROUGH */
case PS_FDR_WAIT:
case PS_FDW_WAIT:
case PS_POLL_WAIT:
@ -277,17 +325,26 @@ __asm__("fnsave %0": :"m"(*fdata));
}
}
/* Unprotect the scheduling queues: */
_queue_signals = 0;
/*
* Poll file descriptors to update the state of threads
* waiting on file I/O where data may be available:
* Poll file descriptors only if a new scheduling signal
* has occurred or if we have no more runnable threads.
*/
_thread_kern_poll(0);
if (((current_tick = _sched_ticks) != last_tick) ||
((_thread_run->state != PS_RUNNING) &&
(PTHREAD_PRIOQ_FIRST() == NULL))) {
/* Unprotect the scheduling queues: */
_queue_signals = 0;
/* Protect the scheduling queues: */
_queue_signals = 1;
/*
* Poll file descriptors to update the state of threads
* waiting on file I/O where data may be available:
*/
thread_kern_poll(0);
/* Protect the scheduling queues: */
_queue_signals = 1;
}
last_tick = current_tick;
/*
* Wake up threads that have timedout. This has to be
@ -329,12 +386,37 @@ __asm__("fnsave %0": :"m"(*fdata));
PTHREAD_WAITQ_CLEARACTIVE();
/*
* Check if there is a current runnable thread that isn't
* already in the ready queue:
* Check to see if the current thread needs to be added
* to the priority queue:
*/
if ((_thread_run != &_thread_kern_thread) &&
(_thread_run->state == PS_RUNNING) &&
((_thread_run->flags & PTHREAD_FLAGS_IN_PRIOQ) == 0)) {
if (add_to_prioq != 0) {
/*
* Save the current time as the time that the
* thread became inactive:
*/
current_tick = _sched_ticks;
_thread_run->last_inactive = (long)current_tick;
if (_thread_run->last_inactive <
_thread_run->last_active) {
/* Account for a rollover: */
_thread_run->last_inactive =+ UINT_MAX + 1;
}
if ((_thread_run->slice_usec != -1) &&
(_thread_run->attr.sched_policy != SCHED_FIFO)) {
/*
* Accumulate the number of microseconds for
* which the current thread has run:
*/
_thread_run->slice_usec +=
(_thread_run->last_inactive -
_thread_run->last_active) *
(long)_clock_res_usec;
/* Check for time quantum exceeded: */
if (_thread_run->slice_usec > TIMESLICE_USEC)
_thread_run->slice_usec = -1;
}
if (_thread_run->slice_usec == -1) {
/*
* The thread exceeded its time
@ -366,6 +448,8 @@ __asm__("fnsave %0": :"m"(*fdata));
* thread structure:
*/
_thread_run = &_thread_kern_thread;
DBG_MSG("No runnable threads, using kernel thread %p\n",
_thread_run);
/* Unprotect the scheduling queues: */
_queue_signals = 0;
@ -374,21 +458,28 @@ __asm__("fnsave %0": :"m"(*fdata));
* There are no threads ready to run, so wait until
* something happens that changes this condition:
*/
_thread_kern_poll(1);
} else {
thread_kern_poll(1);
/*
* This process' usage will likely be very small
* while waiting in a poll. Since the scheduling
* clock is based on the profiling timer, it is
* unlikely that the profiling timer will fire
* and update the time of day. To account for this,
* get the time of day after polling with a timeout.
*/
gettimeofday((struct timeval *) &_sched_tod, NULL);
/* Check once more for a runnable thread: */
_queue_signals = 1;
pthread_h = PTHREAD_PRIOQ_FIRST();
_queue_signals = 0;
}
if (pthread_h != NULL) {
/* Remove the thread from the ready queue: */
PTHREAD_PRIOQ_REMOVE(pthread_h);
/* Get first thread on the waiting list: */
pthread = TAILQ_FIRST(&_waitingq);
/* Check to see if there is more than one thread: */
if (pthread_h != TAILQ_FIRST(&_thread_list) ||
TAILQ_NEXT(pthread_h, tle) != NULL)
set_timer = 1;
else
set_timer = 0;
/* Unprotect the scheduling queues: */
_queue_signals = 0;
@ -411,32 +502,19 @@ __asm__("fnsave %0": :"m"(*fdata));
*/
if (((pthread = PTHREAD_PRIOQ_FIRST()) != NULL) &&
(pthread->active_priority > pthread_h->active_priority)) {
/* Remove the thread from the ready queue: */
PTHREAD_PRIOQ_REMOVE(pthread);
/*
* Insert the lower priority thread
* at the head of its priority list:
*/
PTHREAD_PRIOQ_INSERT_HEAD(pthread_h);
/* Remove the thread from the ready queue: */
PTHREAD_PRIOQ_REMOVE(pthread);
/* There's a new thread in town: */
pthread_h = pthread;
}
/* Get first thread on the waiting list: */
pthread = TAILQ_FIRST(&_waitingq);
/*
* Check to see if there is more than one
* thread:
*/
if (pthread_h != TAILQ_FIRST(&_thread_list) ||
TAILQ_NEXT(pthread_h, tle) != NULL)
set_timer = 1;
else
set_timer = 0;
/* Unprotect the scheduling queues: */
_queue_signals = 0;
}
@ -448,78 +526,8 @@ __asm__("fnsave %0": :"m"(*fdata));
* Save the current time as the time that the thread
* became active:
*/
_thread_run->last_active.tv_sec = tv.tv_sec;
_thread_run->last_active.tv_usec = tv.tv_usec;
/*
* Define the maximum time before a scheduling signal
* is required:
*/
itimer.it_value.tv_sec = 0;
itimer.it_value.tv_usec = TIMESLICE_USEC;
/*
* The interval timer is not reloaded when it
* times out. The interval time needs to be
* calculated every time.
*/
itimer.it_interval.tv_sec = 0;
itimer.it_interval.tv_usec = 0;
/* Get first thread on the waiting list: */
if ((pthread != NULL) &&
(pthread->wakeup_time.tv_sec != -1)) {
/*
* Calculate the time until this thread
* is ready, allowing for the clock
* resolution:
*/
ts1.tv_sec = pthread->wakeup_time.tv_sec
- ts.tv_sec;
ts1.tv_nsec = pthread->wakeup_time.tv_nsec
- ts.tv_nsec + _clock_res_nsec;
/*
* Check for underflow of the nanosecond field:
*/
while (ts1.tv_nsec < 0) {
/*
* Allow for the underflow of the
* nanosecond field:
*/
ts1.tv_sec--;
ts1.tv_nsec += 1000000000;
}
/*
* Check for overflow of the nanosecond field:
*/
while (ts1.tv_nsec >= 1000000000) {
/*
* Allow for the overflow of the
* nanosecond field:
*/
ts1.tv_sec++;
ts1.tv_nsec -= 1000000000;
}
/*
* Convert the timespec structure to a
* timeval structure:
*/
TIMESPEC_TO_TIMEVAL(&tv1, &ts1);
/*
* Check if the thread will be ready
* sooner than the earliest ones found
* so far:
*/
if (timercmp(&tv1, &itimer.it_value, <)) {
/*
* Update the time value:
*/
itimer.it_value.tv_sec = tv1.tv_sec;
itimer.it_value.tv_usec = tv1.tv_usec;
}
}
current_tick = _sched_ticks;
_thread_run->last_active = (long) current_tick;
/*
* Check if this thread is running for the first time
@ -531,88 +539,51 @@ __asm__("fnsave %0": :"m"(*fdata));
_thread_run->slice_usec = 0;
}
/* Check if there is more than one thread: */
if (set_timer != 0) {
/*
* Start the interval timer for the
* calculated time interval:
*/
if (setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL) != 0) {
/*
* Cannot initialise the timer, so
* abort this process:
*/
PANIC("Cannot set scheduling timer");
}
/*
* If we had a context switch, run any
* installed switch hooks.
*/
if ((_sched_switch_hook != NULL) &&
(_last_user_thread != _thread_run)) {
thread_run_switch_hook(_last_user_thread,
_thread_run);
}
/*
* Continue the thread at its current frame:
*/
psf = _thread_run->curframe;
switch(psf->ctxtype) {
case CTX_JB_NOSIG:
___longjmp(psf->ctx.jb, psf->longjmp_val);
break;
case CTX_JB:
__longjmp(psf->ctx.jb, psf->longjmp_val);
break;
case CTX_SJB:
__siglongjmp(psf->ctx.sigjb, psf->longjmp_val);
break;
case CTX_UC:
/* XXX - Restore FP regsisters? */
FP_RESTORE_UC(&psf->ctx.uc);
/*
* Check if this thread is being continued from a
* longjmp() out of a signal handler:
*/
if ((_thread_run->jmpflags & JMPFLAGS_LONGJMP) != 0) {
_thread_run->jmpflags = 0;
__longjmp(_thread_run->nested_jmp.jmp,
_thread_run->longjmp_val);
}
/*
* Check if this thread is being continued from a
* _longjmp() out of a signal handler:
*/
else if ((_thread_run->jmpflags & JMPFLAGS__LONGJMP) !=
0) {
_thread_run->jmpflags = 0;
___longjmp(_thread_run->nested_jmp.jmp,
_thread_run->longjmp_val);
}
/*
* Check if this thread is being continued from a
* siglongjmp() out of a signal handler:
*/
else if ((_thread_run->jmpflags & JMPFLAGS_SIGLONGJMP)
!= 0) {
_thread_run->jmpflags = 0;
__siglongjmp(
_thread_run->nested_jmp.sigjmp,
_thread_run->longjmp_val);
}
/* Check if a signal context was saved: */
else if (_thread_run->sig_saved == 1) {
#ifndef __alpha__
/*
* Point to the floating point data in the
* running thread:
*/
fdata = _thread_run->saved_fp;
/* Restore the floating point state: */
__asm__("frstor %0": :"m"(*fdata));
#endif
/*
* Do a sigreturn to restart the thread that
* was interrupted by a signal:
*/
_thread_kern_in_sched = 0;
#if NOT_YET
_setcontext(&psf->ctx.uc);
#else
/*
* If we had a context switch, run any
* installed switch hooks.
* Ensure the process signal mask is set
* correctly:
*/
if ((_sched_switch_hook != NULL) &&
(_last_user_thread != _thread_run)) {
thread_run_switch_hook(_last_user_thread,
_thread_run);
}
_thread_sys_sigreturn(&_thread_run->saved_sigcontext);
} else {
/*
* Do a longjmp to restart the thread that
* was context switched out (by a longjmp to
* a different thread):
*/
__longjmp(_thread_run->saved_jmp_buf, 1);
psf->ctx.uc.uc_sigmask = _process_sigmask;
_thread_sys_sigreturn(&psf->ctx.uc);
#endif
break;
}
/* This point should not be reached. */
PANIC("Thread has returned from sigreturn or longjmp");
}
@ -645,7 +616,6 @@ _thread_kern_sched_state(enum pthread_state state, char *fname, int lineno)
/* Schedule the next thread that is ready: */
_thread_kern_sched(NULL);
return;
}
void
@ -675,11 +645,10 @@ _thread_kern_sched_state_unlock(enum pthread_state state,
/* Schedule the next thread that is ready: */
_thread_kern_sched(NULL);
return;
}
static void
_thread_kern_poll(int wait_reqd)
thread_kern_poll(int wait_reqd)
{
int count = 0;
int i, found;
@ -696,7 +665,7 @@ _thread_kern_poll(int wait_reqd)
}
else {
/* Get the current time of day: */
gettimeofday(&tv, NULL);
GET_CURRENT_TOD(tv);
TIMEVAL_TO_TIMESPEC(&tv, &ts);
_queue_signals = 1;
@ -713,11 +682,11 @@ _thread_kern_poll(int wait_reqd)
else {
/*
* Calculate the time left for the next thread to
* timeout allowing for the clock resolution:
* timeout:
*/
timeout_ms = ((pthread->wakeup_time.tv_sec - ts.tv_sec) *
1000) + ((pthread->wakeup_time.tv_nsec - ts.tv_nsec +
_clock_res_nsec) / 1000000);
1000) + ((pthread->wakeup_time.tv_nsec - ts.tv_nsec) /
1000000);
/*
* Don't allow negative timeouts:
*/
@ -1002,9 +971,6 @@ _thread_kern_poll(int wait_reqd)
/* Unprotect the scheduling queues: */
_queue_signals = 0;
}
/* Nothing to return. */
return;
}
void
@ -1032,7 +998,7 @@ _thread_kern_set_timeout(const struct timespec * timeout)
_thread_run->wakeup_time.tv_nsec = 0;
} else {
/* Get the current time: */
gettimeofday(&tv, NULL);
GET_CURRENT_TOD(tv);
TIMEVAL_TO_TIMESPEC(&tv, &current_time);
/* Calculate the time for the current thread to wake up: */
@ -1046,7 +1012,6 @@ _thread_kern_set_timeout(const struct timespec * timeout)
_thread_run->wakeup_time.tv_nsec -= 1000000000;
}
}
return;
}
void
@ -1059,9 +1024,6 @@ _thread_kern_sig_defer(void)
void
_thread_kern_sig_undefer(void)
{
pthread_t pthread;
int need_resched = 0;
/*
* Perform checks to yield only if we are about to undefer
* signals.
@ -1077,33 +1039,25 @@ _thread_kern_sig_undefer(void)
/*
* Check if there are queued signals:
*/
while (_sigq_check_reqd != 0) {
/* Defer scheduling while we process queued signals: */
_thread_run->sig_defer_count = 1;
if (_sigq_check_reqd != 0)
_thread_kern_sched(NULL);
/* Clear the flag before checking the signal queue: */
_sigq_check_reqd = 0;
/*
* Check for asynchronous cancellation before delivering any
* pending signals:
*/
if (((_thread_run->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0) &&
((_thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0))
pthread_testcancel();
/* Dequeue and handle signals: */
dequeue_signals();
/*
* Avoiding an unnecessary check to reschedule, check
* to see if signal handling caused a higher priority
* thread to become ready.
*/
if ((need_resched == 0) &&
(((pthread = PTHREAD_PRIOQ_FIRST()) != NULL) &&
(pthread->active_priority > _thread_run->active_priority))) {
need_resched = 1;
}
/* Reenable signals: */
_thread_run->sig_defer_count = 0;
}
/* Yield the CPU if necessary: */
if (need_resched || _thread_run->yield_on_sig_undefer != 0) {
/*
* If there are pending signals or this thread has
* to yield the CPU, call the kernel scheduler:
*
* XXX - Come back and revisit the pending signal problem
*/
if ((_thread_run->yield_on_sig_undefer != 0) ||
SIGNOTEMPTY(_thread_run->sigpend)) {
_thread_run->yield_on_sig_undefer = 0;
_thread_kern_sched(NULL);
}
@ -1114,35 +1068,13 @@ static void
dequeue_signals(void)
{
char bufr[128];
int i, num;
pthread_t pthread;
int num;
/*
* Enter a loop to read and handle queued signals from the
* pthread kernel pipe:
* Enter a loop to clear the pthread kernel pipe:
*/
while (((num = _thread_sys_read(_thread_kern_pipe[0], bufr,
sizeof(bufr))) > 0) || (num == -1 && errno == EINTR)) {
/*
* The buffer read contains one byte per signal and
* each byte is the signal number.
*/
for (i = 0; i < num; i++) {
if ((int) bufr[i] == _SCHED_SIGNAL) {
/*
* Scheduling signals shouldn't ever be
* queued; just ignore it for now.
*/
}
else {
/* Handle this signal: */
pthread = _thread_sig_handle((int) bufr[i],
NULL);
if (pthread != NULL)
_thread_sig_deliver(pthread,
(int) bufr[i]);
}
}
}
if ((num < 0) && (errno != EAGAIN)) {
/*
@ -1151,6 +1083,8 @@ dequeue_signals(void)
*/
PANIC("Unable to read from thread kernel pipe");
}
/* Handle any pending signals: */
_thread_sig_handle_pending();
}
static inline void

View file

@ -79,7 +79,7 @@ static spinlock_t static_init_lock = _SPINLOCK_INITIALIZER;
int
_mutex_reinit(pthread_mutex_t * mutex)
{
int ret = 0;
int ret = 0;
if (mutex == NULL)
ret = EINVAL;
@ -113,7 +113,7 @@ pthread_mutex_init(pthread_mutex_t * mutex,
int protocol;
int ceiling;
pthread_mutex_t pmutex;
int ret = 0;
int ret = 0;
if (mutex == NULL)
ret = EINVAL;
@ -203,7 +203,7 @@ pthread_mutex_init(pthread_mutex_t * mutex,
int
pthread_mutex_destroy(pthread_mutex_t * mutex)
{
int ret = 0;
int ret = 0;
if (mutex == NULL || *mutex == NULL)
ret = EINVAL;
@ -245,7 +245,7 @@ pthread_mutex_destroy(pthread_mutex_t * mutex)
static int
init_static(pthread_mutex_t *mutex)
{
int ret;
int ret;
_SPINLOCK(&static_init_lock);
@ -262,7 +262,7 @@ init_static(pthread_mutex_t *mutex)
int
pthread_mutex_trylock(pthread_mutex_t * mutex)
{
int ret = 0;
int ret = 0;
if (mutex == NULL)
ret = EINVAL;
@ -400,7 +400,7 @@ pthread_mutex_trylock(pthread_mutex_t * mutex)
int
pthread_mutex_lock(pthread_mutex_t * mutex)
{
int ret = 0;
int ret = 0;
if (mutex == NULL)
ret = EINVAL;
@ -610,9 +610,8 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
* Check to see if this thread was interrupted and
* is still in the mutex queue of waiting threads:
*/
if (_thread_run->interrupted != 0) {
if (_thread_run->interrupted != 0)
mutex_queue_remove(*mutex, _thread_run);
}
/* Unlock the mutex structure: */
_SPINUNLOCK(&(*mutex)->lock);
@ -647,7 +646,7 @@ _mutex_cv_unlock(pthread_mutex_t * mutex)
int
_mutex_cv_lock(pthread_mutex_t * mutex)
{
int ret;
int ret;
if ((ret = pthread_mutex_lock(mutex)) == 0)
(*mutex)->m_refcount--;
return (ret);
@ -656,7 +655,7 @@ _mutex_cv_lock(pthread_mutex_t * mutex)
static inline int
mutex_self_trylock(pthread_mutex_t mutex)
{
int ret = 0;
int ret = 0;
switch (mutex->m_type) {
@ -723,7 +722,7 @@ mutex_self_lock(pthread_mutex_t mutex)
static inline int
mutex_unlock_common(pthread_mutex_t * mutex, int add_reference)
{
int ret = 0;
int ret = 0;
if (mutex == NULL || *mutex == NULL) {
ret = EINVAL;
@ -1369,6 +1368,38 @@ _mutex_unlock_private(pthread_t pthread)
}
}
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();
if (pthread->state == PS_MUTEX_WAIT) {
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: */
mutex->m_owner->data.mutex = NULL;
/* Unlock the mutex structure: */
_SPINUNLOCK(&mutex->lock);
}
/*
* Undefer and handle pending signals, yielding if
* necessary:
*/
_thread_kern_sig_undefer();
}
/*
* Dequeue a waiting thread from the head of a mutex queue in descending
* priority order.
@ -1379,7 +1410,7 @@ mutex_queue_deq(pthread_mutex_t mutex)
pthread_t pthread;
while ((pthread = TAILQ_FIRST(&mutex->m_queue)) != NULL) {
TAILQ_REMOVE(&mutex->m_queue, pthread, qe);
TAILQ_REMOVE(&mutex->m_queue, pthread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_MUTEXQ;
/*
@ -1400,7 +1431,7 @@ static inline void
mutex_queue_remove(pthread_mutex_t mutex, pthread_t pthread)
{
if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0) {
TAILQ_REMOVE(&mutex->m_queue, pthread, qe);
TAILQ_REMOVE(&mutex->m_queue, pthread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_MUTEXQ;
}
}
@ -1413,18 +1444,19 @@ mutex_queue_enq(pthread_mutex_t mutex, pthread_t pthread)
{
pthread_t tid = TAILQ_LAST(&mutex->m_queue, mutex_head);
PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread);
/*
* 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))
TAILQ_INSERT_TAIL(&mutex->m_queue, pthread, qe);
TAILQ_INSERT_TAIL(&mutex->m_queue, pthread, sqe);
else {
tid = TAILQ_FIRST(&mutex->m_queue);
while (pthread->active_priority <= tid->active_priority)
tid = TAILQ_NEXT(tid, qe);
TAILQ_INSERT_BEFORE(tid, pthread, qe);
tid = TAILQ_NEXT(tid, sqe);
TAILQ_INSERT_BEFORE(tid, pthread, sqe);
}
pthread->flags |= PTHREAD_FLAGS_IN_MUTEXQ;
}

View file

@ -66,9 +66,13 @@ static int _pq_active = 0;
PANIC(msg); \
} while (0)
#define _PQ_ASSERT_NOT_QUEUED(thrd, msg) do { \
if ((thrd)->flags & _PQ_IN_SCHEDQ) \
if (((thrd)->flags & _PQ_IN_SCHEDQ) != 0) \
PANIC(msg); \
} while (0)
#define _PQ_ASSERT_PROTECTED(msg) \
PTHREAD_ASSERT((_thread_kern_in_sched != 0) || \
(_thread_run->sig_defer_count > 0) || \
(_sig_in_handler != 0), msg);
#else
@ -79,11 +83,10 @@ static int _pq_active = 0;
#define _PQ_ASSERT_IN_WAITQ(thrd, msg)
#define _PQ_ASSERT_IN_PRIOQ(thrd, msg)
#define _PQ_ASSERT_NOT_QUEUED(thrd, msg)
#define _PQ_CHECK_PRIO()
#define _PQ_ASSERT_PROTECTED(msg)
#endif
int
_pq_alloc(pq_queue_t *pq, int minprio, int maxprio)
{
@ -101,9 +104,7 @@ _pq_alloc(pq_queue_t *pq, int minprio, int maxprio)
else {
/* Remember the queue size: */
pq->pq_size = prioslots;
ret = _pq_init(pq);
}
return (ret);
}
@ -142,6 +143,7 @@ _pq_remove(pq_queue_t *pq, pthread_t pthread)
_PQ_ASSERT_INACTIVE("_pq_remove: pq_active");
_PQ_SET_ACTIVE();
_PQ_ASSERT_IN_PRIOQ(pthread, "_pq_remove: Not in priority queue");
_PQ_ASSERT_PROTECTED("_pq_remove: prioq not protected!");
/*
* Remove this thread from priority list. Note that if
@ -172,6 +174,7 @@ _pq_insert_head(pq_queue_t *pq, pthread_t pthread)
_PQ_SET_ACTIVE();
_PQ_ASSERT_NOT_QUEUED(pthread,
"_pq_insert_head: Already in priority queue");
_PQ_ASSERT_PROTECTED("_pq_insert_head: prioq not protected!");
TAILQ_INSERT_HEAD(&pq->pq_lists[prio].pl_head, pthread, pqe);
if (pq->pq_lists[prio].pl_queued == 0)
@ -197,6 +200,7 @@ _pq_insert_tail(pq_queue_t *pq, pthread_t pthread)
_PQ_SET_ACTIVE();
_PQ_ASSERT_NOT_QUEUED(pthread,
"_pq_insert_tail: Already in priority queue");
_PQ_ASSERT_PROTECTED("_pq_insert_tail: prioq not protected!");
TAILQ_INSERT_TAIL(&pq->pq_lists[prio].pl_head, pthread, pqe);
if (pq->pq_lists[prio].pl_queued == 0)
@ -221,6 +225,7 @@ _pq_first(pq_queue_t *pq)
*/
_PQ_ASSERT_INACTIVE("_pq_first: pq_active");
_PQ_SET_ACTIVE();
_PQ_ASSERT_PROTECTED("_pq_first: prioq not protected!");
while (((pql = TAILQ_FIRST(&pq->pq_queue)) != NULL) &&
(pthread == NULL)) {
@ -250,6 +255,7 @@ pq_insert_prio_list(pq_queue_t *pq, int prio)
* Make some assertions when debugging is enabled:
*/
_PQ_ASSERT_ACTIVE("pq_insert_prio_list: pq_active");
_PQ_ASSERT_PROTECTED("_pq_insert_prio_list: prioq not protected!");
/*
* The priority queue is in descending priority order. Start at
@ -270,11 +276,10 @@ pq_insert_prio_list(pq_queue_t *pq, int prio)
pq->pq_lists[prio].pl_queued = 1;
}
#if defined(_PTHREADS_INVARIANTS)
void
_waitq_insert(pthread_t pthread)
{
pthread_t tid;
pthread_t tid;
/*
* Make some assertions when debugging is enabled:
@ -332,4 +337,3 @@ _waitq_clearactive(void)
_PQ_CLEAR_ACTIVE();
}
#endif
#endif

View file

@ -51,6 +51,7 @@
*/
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/time.h>
@ -59,14 +60,68 @@
#include <spinlock.h>
#include <pthread_np.h>
/*
* Define machine dependent macros to get and set the stack pointer
* from the supported contexts. Also define a macro to set the return
* address in a jmp_buf context.
*
* XXX - These need to be moved into architecture dependent support files.
*/
#if defined(__i386__)
#define GET_STACK_JB(jb) ((unsigned long)((jb)[0]._jb[2]))
#define GET_STACK_SJB(sjb) ((unsigned long)((sjb)[0]._sjb[2]))
#define GET_STACK_UC(ucp) ((unsigned long)((ucp)->uc_mcontext.mc_esp))
#define SET_STACK_JB(jb, stk) (jb)[0]._jb[2] = (int)(stk)
#define SET_STACK_SJB(sjb, stk) (sjb)[0]._sjb[2] = (int)(stk)
#define SET_STACK_UC(ucp, stk) (ucp)->uc_mcontext.mc_esp = (int)(stk)
#define FP_SAVE_UC(ucp) do { \
char *fdata; \
fdata = (char *) (ucp)->uc_mcontext.mc_fpregs; \
__asm__("fnsave %0": :"m"(*fdata)); \
} while (0)
#define FP_RESTORE_UC(ucp) do { \
char *fdata; \
fdata = (char *) (ucp)->uc_mcontext.mc_fpregs; \
__asm__("frstor %0": :"m"(*fdata)); \
} while (0)
#define SET_RETURN_ADDR_JB(jb, ra) (jb)[0]._jb[0] = (int)(ra)
#elif defined(__alpha__)
#include <machine/reg.h>
#define GET_STACK_JB(jb, stk) ((unsigned long)((jb)[0]._jb[R_SP + 4]))
#define GET_STACK_SJB(sjb, stk) ((unsigned long)((sjb)[0]._sjb[R_SP + 4]))
#define GET_STACK_UC(ucp, stk) ((ucp)->uc_mcontext.mc_regs[R_SP])
#define SET_STACK_JB(jb, stk) (jb)[0]._jb[R_SP + 4] = (long)(stk)
#define SET_STACK_SJB(sjb, stk) (sjb)[0]._sjb[R_SP + 4] = (long)(stk)
#define SET_STACK_UC(ucp, stk) (ucp)->uc_mcontext.mc_regs[R_SP] = (unsigned long)(stk)
#define FP_SAVE_UC(ucp)
#define FP_RESTORE_UC(ucp)
#define SET_RETURN_ADDR_JB(jb, ra) do { \
(jb)[0]._jb[2] = (long)(ra); \
(jb)[0]._jb[R_RA + 4] = 0; \
(jb)[0]._jb[R_T12 + 4] = (long)(ra); \
} while (0)
#else
#error "Don't recognize this architecture!"
#endif
/*
* Kernel fatal error handler macro.
*/
#define PANIC(string) _thread_exit(__FILE__,__LINE__,string)
/* Output debug messages like this: */
#define stdout_debug(_x) _thread_sys_write(1,_x,strlen(_x));
#define stderr_debug(_x) _thread_sys_write(2,_x,strlen(_x));
#define stdout_debug(args...) do { \
char buf[128]; \
snprintf(buf, sizeof(buf), ##args); \
_thread_sys_write(1, buf, strlen(buf)); \
} while (0)
#define stderr_debug(args...) do { \
char buf[128]; \
snprintf(buf, sizeof(buf), ##args); \
_thread_sys_write(2, buf, strlen(buf)); \
} while (0)
/*
@ -80,34 +135,13 @@
/*
* Waiting queue manipulation macros (using pqe link):
*/
#if defined(_PTHREADS_INVARIANTS)
#define PTHREAD_WAITQ_REMOVE(thrd) _waitq_remove(thrd)
#define PTHREAD_WAITQ_INSERT(thrd) _waitq_insert(thrd)
#if defined(_PTHREADS_INVARIANTS)
#define PTHREAD_WAITQ_CLEARACTIVE() _waitq_clearactive()
#define PTHREAD_WAITQ_SETACTIVE() _waitq_setactive()
#else
#define PTHREAD_WAITQ_REMOVE(thrd) do { \
TAILQ_REMOVE(&_waitingq,thrd,pqe); \
(thrd)->flags &= ~PTHREAD_FLAGS_IN_WAITQ; \
} while (0)
#define PTHREAD_WAITQ_INSERT(thrd) do { \
if ((thrd)->wakeup_time.tv_sec == -1) \
TAILQ_INSERT_TAIL(&_waitingq,thrd,pqe); \
else { \
pthread_t tid = TAILQ_FIRST(&_waitingq); \
while ((tid != NULL) && (tid->wakeup_time.tv_sec != -1) && \
((tid->wakeup_time.tv_sec < (thrd)->wakeup_time.tv_sec) || \
((tid->wakeup_time.tv_sec == (thrd)->wakeup_time.tv_sec) && \
(tid->wakeup_time.tv_nsec <= (thrd)->wakeup_time.tv_nsec)))) \
tid = TAILQ_NEXT(tid, pqe); \
if (tid == NULL) \
TAILQ_INSERT_TAIL(&_waitingq,thrd,pqe); \
else \
TAILQ_INSERT_BEFORE(tid,thrd,pqe); \
} \
(thrd)->flags |= PTHREAD_FLAGS_IN_WAITQ; \
} while (0)
#define PTHREAD_WAITQ_CLEARACTIVE()
#define PTHREAD_WAITQ_SETACTIVE()
#endif
@ -139,6 +173,14 @@
* called with preemption deferred (see thread_kern_sched_[un]defer).
*/
#if defined(_PTHREADS_INVARIANTS)
#include <assert.h>
#define PTHREAD_ASSERT(cond, msg) do { \
if (!(cond)) \
PANIC(msg); \
} while (0)
#define PTHREAD_ASSERT_NOT_IN_SYNCQ(thrd) \
PTHREAD_ASSERT((((thrd)->flags & PTHREAD_FLAGS_IN_SYNCQ) == 0), \
"Illegal call from signal handler");
#define PTHREAD_NEW_STATE(thrd, newstate) do { \
if (_thread_kern_new_state != 0) \
PANIC("Recursive PTHREAD_NEW_STATE"); \
@ -156,6 +198,8 @@
PTHREAD_SET_STATE(thrd, newstate); \
} while (0)
#else
#define PTHREAD_ASSERT(cond, msg)
#define PTHREAD_ASSERT_NOT_IN_SYNCQ(thrd)
#define PTHREAD_NEW_STATE(thrd, newstate) do { \
if ((thrd)->state != newstate) { \
if ((thrd)->state == PS_RUNNING) { \
@ -379,21 +423,52 @@ enum pthread_susp {
* almost entirely on this stack.
*/
#define PTHREAD_STACK_INITIAL 0x100000
/* Address immediately beyond the beginning of the initial thread stack. */
#define PTHREAD_DEFAULT_PRIORITY 64
#define PTHREAD_MAX_PRIORITY 126
#define PTHREAD_MIN_PRIORITY 0
#define _POSIX_THREAD_ATTR_STACKSIZE
/*
* Clock resolution in nanoseconds.
* Define the different priority ranges. All applications have thread
* priorities constrained within 0-31. The threads library raises the
* priority when delivering signals in order to ensure that signal
* delivery happens (from the POSIX spec) "as soon as possible".
* In the future, the threads library will also be able to map specific
* threads into real-time (cooperating) processes or kernel threads.
* The RT and SIGNAL priorities will be used internally and added to
* thread base priorities so that the scheduling queue can handle both
* normal and RT priority threads with and without signal handling.
*
* The approach taken is that, within each class, signal delivery
* always has priority over thread execution.
*/
#define CLOCK_RES_NSEC 10000000
#define PTHREAD_DEFAULT_PRIORITY 15
#define PTHREAD_MIN_PRIORITY 0
#define PTHREAD_MAX_PRIORITY 31 /* 0x1F */
#define PTHREAD_SIGNAL_PRIORITY 32 /* 0x20 */
#define PTHREAD_RT_PRIORITY 64 /* 0x40 */
#define PTHREAD_FIRST_PRIORITY PTHREAD_MIN_PRIORITY
#define PTHREAD_LAST_PRIORITY \
(PTHREAD_MAX_PRIORITY + PTHREAD_SIGNAL_PRIORITY + PTHREAD_RT_PRIORITY)
#define PTHREAD_BASE_PRIORITY(prio) ((prio) & PTHREAD_MAX_PRIORITY)
/*
* Clock resolution in microseconds.
*/
#define CLOCK_RES_USEC 10000
/*
* Time slice period in microseconds.
*/
#define TIMESLICE_USEC 100000
#define TIMESLICE_USEC 20000
/*
* Define a thread-safe macro to get the current time of day
* which is updated at regular intervals by the scheduling signal
* handler.
*/
#define GET_CURRENT_TOD(tv) \
do { \
tv.tv_sec = _sched_tod.tv_sec; \
tv.tv_usec = _sched_tod.tv_usec; \
} while (tv.tv_sec != _sched_tod.tv_sec)
struct pthread_key {
spinlock_t lock;
@ -487,8 +562,10 @@ union pthread_wait_data {
short branch; /* Line number, for debugging. */
char *fname; /* Source file name for debugging.*/
} fd;
struct pthread_poll_data * poll_data;
FILE *fp;
struct pthread_poll_data *poll_data;
spinlock_t *spinlock;
struct pthread *thread;
};
/*
@ -497,6 +574,83 @@ union pthread_wait_data {
*/
typedef void (*thread_continuation_t) (void *);
struct pthread_state_data {
int psd_interrupted;
sigset_t psd_sigmask;
enum pthread_state psd_state;
int psd_flags;
struct timespec psd_wakeup_time;
union pthread_wait_data psd_wait_data;
/* XXX - What about thread->timeout and/or thread->error? */
};
/*
* Normally thread contexts are stored as jmp_bufs via _setjmp()/_longjmp(),
* but they may also be sigjmp_buf and ucontext_t. When a thread is
* interrupted by a signal, it's context is saved as a ucontext_t. An
* application is also free to use [_]longjmp()/[_]siglongjmp() to jump
* between contexts within the same thread. Future support will also
* include setcontext()/getcontext().
*
* Define an enumerated type that can identify the 4 different context
* types.
*/
typedef enum {
CTX_JB_NOSIG, /* context is jmp_buf without saved sigset */
CTX_JB, /* context is jmp_buf (with saved sigset) */
CTX_SJB, /* context is sigjmp_buf (with saved sigset) */
CTX_UC /* context is ucontext_t (with saved sigset) */
} thread_context_t;
/*
* There are 2 basic contexts that a frame may contain at any
* one time:
*
* o ctx - The context that the thread should return to after normal
* completion of the signal handler.
* o sig_jb - The context just before the signal handler is invoked.
* Attempts at abnormal returns from user supplied signal handlers
* will return back to the signal context to perform any necessary
* cleanup.
*/
struct pthread_signal_frame {
/*
* This stores the threads state before the signal.
*/
struct pthread_state_data saved_state;
/* Beginning (bottom) of threads stack frame for this signal. */
unsigned long stackp;
/*
* Threads return context; ctxtype identifies the type of context.
* For signal frame 0, these point to the context storage area
* within the pthread structure. When handling signals (frame > 0),
* these point to a context storage area that is allocated off the
* threads stack.
*/
union {
jmp_buf jb;
sigjmp_buf sigjb;
ucontext_t uc;
} ctx;
thread_context_t ctxtype;
int longjmp_val;
/* Threads "jump out of signal handler" destination frame. */
int dst_frame;
/*
* Used to return back to the signal handling frame in case
* the application tries to change contexts from the handler.
*/
jmp_buf *sig_jb;
int signo; /* signal, arg 1 to sighandler */
int sig_has_args; /* use signal args if true */
};
/*
* Thread structure.
*/
@ -530,54 +684,19 @@ struct pthread {
void *stack;
struct pthread_attr attr;
#if (defined(__FreeBSD__) || defined(__NetBSD__)) && defined(__i386__)
/*
* Saved floating point registers on systems where they are not
* saved in the signal context.
*/
char saved_fp[108];
#endif
/*
* Saved signal context used in call to sigreturn by
* _thread_kern_sched if sig_saved is TRUE.
*/
ucontext_t saved_sigcontext;
/*
* Saved jump buffer used in call to longjmp by _thread_kern_sched
* if sig_saved is FALSE.
*/
jmp_buf saved_jmp_buf;
jmp_buf *sighandler_jmp_buf;
/*
* Saved jump buffers for use when doing nested [sig|_]longjmp()s, as
* when doing signal delivery.
*/
union {
jmp_buf jmp;
sigjmp_buf sigjmp;
} nested_jmp;
int longjmp_val;
#define JMPFLAGS_NONE 0x00
#define JMPFLAGS_LONGJMP 0x01
#define JMPFLAGS__LONGJMP 0x02
#define JMPFLAGS_SIGLONGJMP 0x04
#define JMPFLAGS_DEFERRED 0x08
int jmpflags;
/*
* TRUE if the last state saved was a signal context. FALSE if the
* last state saved was a jump buffer.
*/
int sig_saved;
/*
* Used for tracking delivery of nested signal handlers.
* Signal frame 0 is used for normal context (when no
* signal handlers are active for the thread). Frame
* 1 is used as the context for the first signal, and
* frames 2 .. NSIG-1 are used when additional signals
* arrive interrupting already active signal handlers.
*/
int signal_nest_level;
struct pthread_signal_frame *sigframes[NSIG];
struct pthread_signal_frame sigframe0;
struct pthread_signal_frame *curframe;
int sigframe_count;
int sigframe_done;
/*
* Cancelability flags - the lower 2 bits are used by cancel
@ -588,7 +707,7 @@ struct pthread {
#define PTHREAD_CANCEL_NEEDED 0x0010
int cancelflags;
enum pthread_susp suspended;
enum pthread_susp suspended;
thread_continuation_t continuation;
@ -597,16 +716,16 @@ struct pthread {
*/
sigset_t sigmask;
sigset_t sigpend;
int check_pending;
/* Thread state: */
enum pthread_state state;
enum pthread_state oldstate;
/* Time that this thread was last made active. */
struct timeval last_active;
/* Scheduling clock when this thread was last made active. */
long last_active;
/* Time that this thread was last made inactive. */
struct timeval last_inactive;
/* Scheduling clock when this thread was last made inactive. */
long last_inactive;
/*
* Number of microseconds accumulated by this thread when
@ -614,12 +733,6 @@ struct pthread {
*/
long slice_usec;
/*
* Incremental priority accumulated by thread while it is ready to
* run but is denied being run.
*/
int inc_prio;
/*
* Time to wake up thread. This is used for sleeping threads and
* for any operation which may time out (such as select).
@ -640,8 +753,7 @@ struct pthread {
/*
* The current thread can belong to only one scheduling queue at
* a time (ready or waiting queue). It can also belong to (only)
* one of:
* a time (ready or waiting queue). It can also belong to:
*
* o A queue of threads waiting for a mutex
* o A queue of threads waiting for a condition variable
@ -651,15 +763,21 @@ struct pthread {
* o A queue of threads needing work done by the kernel thread
* (waiting for a spinlock or file I/O)
*
* It is possible for a thread to belong to more than one of the
* above queues if it is handling a signal. A thread may only
* enter a mutex, condition variable, or join queue when it is
* not being called from a signal handler. If a thread is a
* member of one of these queues when a signal handler is invoked,
* it must remain in the queue. For this reason, the links for
* these queues must not be (re)used for other queues.
*
* Use pqe for the scheduling queue link (both ready and waiting),
* and qe for other links.
* sqe for synchronization (mutex, condition variable, and join)
* queue links, and qe for all other links.
*/
/* Priority queue entry for this thread: */
TAILQ_ENTRY(pthread) pqe;
/* Queue entry for this thread: */
TAILQ_ENTRY(pthread) qe;
TAILQ_ENTRY(pthread) pqe; /* priority queue link */
TAILQ_ENTRY(pthread) sqe; /* synchronization queue link */
TAILQ_ENTRY(pthread) qe; /* all other queues link */
/* Wait data. */
union pthread_wait_data data;
@ -694,14 +812,17 @@ struct pthread {
int flags;
#define PTHREAD_FLAGS_PRIVATE 0x0001
#define PTHREAD_EXITING 0x0002
#define PTHREAD_FLAGS_IN_CONDQ 0x0004 /* in condition queue using qe link*/
#define PTHREAD_FLAGS_IN_WORKQ 0x0008 /* in work queue using qe link */
#define PTHREAD_FLAGS_IN_WAITQ 0x0010 /* in waiting queue using pqe link */
#define PTHREAD_FLAGS_IN_PRIOQ 0x0020 /* in priority queue using pqe link */
#define PTHREAD_FLAGS_IN_MUTEXQ 0x0040 /* in mutex queue using qe link */
#define PTHREAD_FLAGS_IN_FILEQ 0x0080 /* in file lock queue using qe link */
#define PTHREAD_FLAGS_IN_FDQ 0x0100 /* in fd lock queue using qe link */
#define PTHREAD_FLAGS_TRACE 0x0200 /* for debugging purposes */
#define PTHREAD_FLAGS_IN_WAITQ 0x0004 /* in waiting queue using pqe link */
#define PTHREAD_FLAGS_IN_PRIOQ 0x0008 /* in priority queue using pqe link */
#define PTHREAD_FLAGS_IN_WORKQ 0x0010 /* in work queue using qe link */
#define PTHREAD_FLAGS_IN_FILEQ 0x0020 /* in file lock queue using qe link */
#define PTHREAD_FLAGS_IN_FDQ 0x0040 /* in fd lock queue using qe link */
#define PTHREAD_FLAGS_IN_CONDQ 0x0080 /* in condition queue using sqe link*/
#define PTHREAD_FLAGS_IN_MUTEXQ 0x0100 /* in mutex queue using sqe link */
#define PTHREAD_FLAGS_IN_JOINQ 0x0200 /* in join queue using sqe link */
#define PTHREAD_FLAGS_TRACE 0x0400 /* for debugging purposes */
#define PTHREAD_FLAGS_IN_SYNCQ \
(PTHREAD_FLAGS_IN_CONDQ | PTHREAD_FLAGS_IN_MUTEXQ | PTHREAD_FLAGS_IN_JOINQ)
/*
* Base priority is the user setable and retrievable priority
@ -820,14 +941,31 @@ SCLASS int _thread_kern_in_sched
;
#endif
/* Last time that an incremental priority update was performed: */
SCLASS struct timeval kern_inc_prio_time
SCLASS int _sig_in_handler
#ifdef GLOBAL_PTHREAD_PRIVATE
= 0;
#else
;
#endif
/* Time of day at last scheduling timer signal: */
SCLASS struct timeval volatile _sched_tod
#ifdef GLOBAL_PTHREAD_PRIVATE
= { 0, 0 };
#else
;
#endif
/*
* Current scheduling timer ticks; used as resource usage.
*/
SCLASS unsigned int volatile _sched_ticks
#ifdef GLOBAL_PTHREAD_PRIVATE
= 0;
#else
;
#endif
/* Dead threads: */
SCLASS TAILQ_HEAD(, pthread) _dead_list
#ifdef GLOBAL_PTHREAD_PRIVATE
@ -905,9 +1043,9 @@ SCLASS int _thread_dtablesize /* Descriptor table size. */
;
#endif
SCLASS int _clock_res_nsec /* Clock resolution in nsec. */
SCLASS int _clock_res_usec /* Clock resolution in usec. */
#ifdef GLOBAL_PTHREAD_PRIVATE
= CLOCK_RES_NSEC;
= CLOCK_RES_USEC;
#else
;
#endif
@ -937,9 +1075,10 @@ SCLASS struct sigaction _thread_sigact[NSIG];
SCLASS int _thread_dfl_count[NSIG];
/*
* Pending signals for this process.
* Pending signals and mask for this process:
*/
SCLASS sigset_t _process_sigpending;
SCLASS sigset_t _process_sigmask;
/*
* Scheduling queues:
@ -959,6 +1098,21 @@ SCLASS volatile int _spinblock_count
#endif
;
/* Used to maintain pending and active signals: */
struct sigstatus {
int pending; /* Is this a pending signal? */
int blocked; /*
* A handler is currently active for
* this signal; ignore subsequent
* signals until the handler is done.
*/
int signo; /* arg 1 to signal handler */
siginfo_t siginfo; /* arg 2 to signal handler */
ucontext_t uc; /* arg 3 to signal handler */
};
SCLASS struct sigstatus _thread_sigq[NSIG];
/* Indicates that the signal queue needs to be checked. */
SCLASS volatile int _sigq_check_reqd
#ifdef GLOBAL_PTHREAD_PRIVATE
@ -998,6 +1152,18 @@ SCLASS void * _next_stack
#endif
;
/*
* Declare the kernel scheduler jump buffer and stack:
*/
SCLASS jmp_buf _thread_kern_sched_jb;
SCLASS void * _thread_kern_sched_stack
#ifdef GLOBAL_PTHREAD_PRIVATE
= NULL
#endif
;
/* Used for _PTHREADS_INVARIANTS checking. */
SCLASS int _thread_kern_new_state
#ifdef GLOBAL_PTHREAD_PRIVATE
@ -1025,15 +1191,19 @@ __BEGIN_DECLS
char *__ttyname_basic(int);
char *__ttyname_r_basic(int, char *, size_t);
char *ttyname_r(int, char *, size_t);
void _cond_wait_backout(pthread_t);
void _fd_lock_backout(pthread_t);
int _find_dead_thread(pthread_t);
int _find_thread(pthread_t);
void _flockfile_backout(pthread_t);
void _funlock_owned(pthread_t);
void _join_backout(pthread_t);
int _thread_create(pthread_t *,const pthread_attr_t *,void *(*start_routine)(void *),void *,pthread_t);
int _thread_fd_lock(int, int, struct timespec *);
int _thread_fd_lock_debug(int, int, struct timespec *,char *fname,int lineno);
void _dispatch_signals(void);
int _mutex_cv_lock(pthread_mutex_t *);
int _mutex_cv_unlock(pthread_mutex_t *);
void _mutex_lock_backout(pthread_t);
void _mutex_notify_priochange(pthread_t);
int _mutex_reinit(pthread_mutex_t *);
void _mutex_unlock_private(pthread_t);
@ -1044,14 +1214,15 @@ void _pq_remove(struct pq_queue *pq, struct pthread *);
void _pq_insert_head(struct pq_queue *pq, struct pthread *);
void _pq_insert_tail(struct pq_queue *pq, struct pthread *);
struct pthread *_pq_first(struct pq_queue *pq);
#if defined(_PTHREADS_INVARIANTS)
void _waitq_insert(pthread_t pthread);
void _waitq_remove(pthread_t pthread);
#if defined(_PTHREADS_INVARIANTS)
void _waitq_setactive(void);
void _waitq_clearactive(void);
#endif
void _thread_exit(char *, int, char *);
void _thread_exit_cleanup(void);
void _thread_exit_finish(void);
void _thread_fd_unlock(int, int);
void _thread_fd_unlock_debug(int, int, char *, int);
void _thread_fd_unlock_owned(pthread_t);
@ -1060,20 +1231,23 @@ void _thread_cleanupspecific(void);
void _thread_dump_info(void);
void _thread_init(void);
void _thread_kern_sched(ucontext_t *);
void _thread_kern_sched_state(enum pthread_state,char *fname,int lineno);
void _thread_kern_scheduler(void);
void _thread_kern_sched_frame(int frame);
void _thread_kern_sched_sig(void);
void _thread_kern_sched_state(enum pthread_state, char *fname, int lineno);
void _thread_kern_sched_state_unlock(enum pthread_state state,
spinlock_t *lock, char *fname, int lineno);
void _thread_kern_set_timeout(const struct timespec *);
void _thread_kern_sig_defer(void);
void _thread_kern_sig_undefer(void);
void _thread_sig_handler(int, int, ucontext_t *);
pthread_t _thread_sig_handle(int, ucontext_t *);
void _thread_sig_init(void);
void _thread_sig_handler(int, siginfo_t *, ucontext_t *);
void _thread_sig_check_pending(pthread_t pthread);
void _thread_sig_handle_pending(void);
void _thread_sig_send(pthread_t pthread, int sig);
void _thread_sig_deliver(pthread_t pthread, int sig);
void _thread_sig_wrapper(void);
int _thread_sigframe_find(pthread_t pthread, void *stackp);
void _thread_start(void);
void _thread_start_sig_handler(void);
void _thread_seterrno(pthread_t,int);
void _thread_seterrno(pthread_t, int);
int _thread_fd_table_init(int fd);
pthread_addr_t _thread_gc(pthread_addr_t);
void _thread_enter_cancellation_point(void);

View file

@ -29,6 +29,7 @@
* $FreeBSD$
*/
#include <stdlib.h>
#include <errno.h>
#ifdef _THREAD_SAFE
#include <semaphore.h>

View file

@ -59,7 +59,8 @@ pthread_setschedparam(pthread_t pthread, int policy,
*/
_thread_kern_sig_defer();
if (param->sched_priority != pthread->base_priority) {
if (param->sched_priority !=
PTHREAD_BASE_PRIORITY(pthread->base_priority)) {
/*
* Remove the thread from its current priority
* queue before any adjustments are made to its
@ -72,6 +73,8 @@ pthread_setschedparam(pthread_t pthread, int policy,
}
/* Set the thread base priority: */
pthread->base_priority &=
(PTHREAD_SIGNAL_PRIORITY | PTHREAD_RT_PRIORITY);
pthread->base_priority = param->sched_priority;
/* Recalculate the active priority: */

File diff suppressed because it is too large Load diff

View file

@ -74,12 +74,13 @@ _sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
*/
if (act != NULL && sig != _SCHED_SIGNAL && sig != SIGCHLD &&
sig != SIGINFO) {
/* Initialise the global signal action structure: */
gact.sa_mask = act->sa_mask;
gact.sa_flags = 0;
/* Ensure the scheduling signal is masked: */
sigaddset(&gact.sa_mask, _SCHED_SIGNAL);
/*
* Ensure the signal handler cannot be interrupted
* by other signals. Always request the POSIX signal
* handler arguments.
*/
sigfillset(&gact.sa_mask);
gact.sa_flags = SA_SIGINFO;
/*
* Check if the signal handler is being set to

View file

@ -43,7 +43,8 @@
int
pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
{
int ret = 0;
sigset_t sigset;
int ret = 0;
/* Check if the existing signal process mask is to be returned: */
if (oset != NULL) {
@ -81,10 +82,18 @@ pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
}
/*
* Dispatch signals to the running thread that are pending
* and now unblocked:
* Check if there are pending signals for the running
* thread or process that aren't blocked:
*/
_dispatch_signals();
sigset = _thread_run->sigpend;
SIGSETOR(sigset, _process_sigpending);
SIGSETNAND(sigset, _thread_run->sigmask);
if (SIGNOTEMPTY(sigset))
/*
* Call the kernel scheduler which will safely
* install a signal frame for the running thread:
*/
_thread_kern_sched_sig();
}
/* Return the completion status: */

View file

@ -41,53 +41,9 @@
#include "pthread_private.h"
int
_sigprocmask(int how, const sigset_t * set, sigset_t * oset)
_sigprocmask(int how, const sigset_t *set, sigset_t *oset)
{
int ret = 0;
/* Check if the existing signal process mask is to be returned: */
if (oset != NULL) {
/* Return the current mask: */
*oset = _thread_run->sigmask;
}
/* Check if a new signal set was provided by the caller: */
if (set != NULL) {
/* Process according to what to do: */
switch (how) {
/* Block signals: */
case SIG_BLOCK:
/* Add signals to the existing mask: */
SIGSETOR(_thread_run->sigmask, *set);
break;
/* Unblock signals: */
case SIG_UNBLOCK:
/* Clear signals from the existing mask: */
SIGSETNAND(_thread_run->sigmask, *set);
break;
/* Set the signal process mask: */
case SIG_SETMASK:
/* Set the new mask: */
_thread_run->sigmask = *set;
break;
/* Trap invalid actions: */
default:
/* Return an invalid argument: */
errno = EINVAL;
ret = -1;
break;
}
/*
* Dispatch signals to the running thread that are pending
* and now unblocked:
*/
_dispatch_signals();
}
/* Return the completion status: */
return (ret);
return (pthread_sigmask(how, set, oset));
}
__strong_reference(_sigprocmask, sigprocmask);

View file

@ -40,7 +40,7 @@
#include "pthread_private.h"
int
sigwait(const sigset_t * set, int *sig)
sigwait(const sigset_t *set, int *sig)
{
int ret = 0;
int i;
@ -52,11 +52,9 @@ sigwait(const sigset_t * set, int *sig)
* Specify the thread kernel signal handler.
*/
act.sa_handler = (void (*) ()) _thread_sig_handler;
act.sa_flags = SA_RESTART;
act.sa_mask = *set;
/* Ensure the scheduling signal is masked: */
sigaddset(&act.sa_mask, _SCHED_SIGNAL);
act.sa_flags = SA_RESTART | SA_SIGINFO;
/* Ensure the signal handler cannot be interrupted by other signals: */
sigfillset(&act.sa_mask);
/*
* Initialize the set of signals that will be waited on:

View file

@ -127,7 +127,7 @@ _write(int fd, const void *buf, size_t nbytes)
/* Return the number of bytes written: */
ret = num;
}
_FD_UNLOCK(fd, FD_RDWR);
_FD_UNLOCK(fd, FD_WRITE);
}
return (ret);
}

View file

@ -57,8 +57,5 @@ pthread_yield(void)
/* Schedule the next thread: */
_thread_kern_sched(NULL);
/* Nothing to return. */
return;
}
#endif

View file

@ -45,6 +45,10 @@ pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param
ret = EINVAL;
else if (param == NULL) {
ret = ENOTSUP;
} else if ((param->sched_priority < PTHREAD_MIN_PRIORITY) ||
(param->sched_priority > PTHREAD_MAX_PRIORITY)) {
/* Return an unsupported value error. */
ret = ENOTSUP;
} else
(*attr)->prio = param->sched_priority;

View file

@ -170,10 +170,7 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
* perform the dynamic initialization:
*/
else if (*cond != NULL ||
(rval = pthread_cond_init(cond,NULL)) == 0) {
_thread_enter_cancellation_point();
(rval = pthread_cond_init(cond, NULL)) == 0) {
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
@ -286,8 +283,6 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
if (_thread_run->continuation != NULL)
_thread_run->continuation((void *) _thread_run);
}
_thread_leave_cancellation_point();
}
_thread_leave_cancellation_point();
@ -313,8 +308,6 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
* initialization.
*/
else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
_thread_enter_cancellation_point();
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
@ -446,8 +439,6 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
if (_thread_run->continuation != NULL)
_thread_run->continuation((void *) _thread_run);
}
_thread_leave_cancellation_point();
}
_thread_leave_cancellation_point();
@ -589,6 +580,48 @@ pthread_cond_broadcast(pthread_cond_t * cond)
return (rval);
}
void
_cond_wait_backout(pthread_t pthread)
{
pthread_cond_t cond;
cond = pthread->data.cond;
if (cond != NULL) {
/*
* Defer signals to protect the scheduling queues
* from access by the signal handler:
*/
_thread_kern_sig_defer();
/* Lock the condition variable structure: */
_SPINLOCK(&cond->lock);
/* Process according to condition variable type: */
switch (cond->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
cond_queue_remove(cond, pthread);
/* Check for no more waiters: */
if (TAILQ_FIRST(&cond->c_queue) == NULL)
cond->c_mutex = NULL;
break;
default:
break;
}
/* Unlock the condition variable structure: */
_SPINUNLOCK(&cond->lock);
/*
* Undefer and handle pending signals, yielding if
* necessary:
*/
_thread_kern_sig_undefer();
}
}
/*
* Dequeue a waiting thread from the head of a condition queue in
* descending priority order.
@ -599,7 +632,7 @@ cond_queue_deq(pthread_cond_t cond)
pthread_t pthread;
while ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
TAILQ_REMOVE(&cond->c_queue, pthread, qe);
TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
if ((pthread->timeout == 0) && (pthread->interrupted == 0))
/*
@ -628,7 +661,7 @@ cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
* it isn't in the queue.
*/
if (pthread->flags & PTHREAD_FLAGS_IN_CONDQ) {
TAILQ_REMOVE(&cond->c_queue, pthread, qe);
TAILQ_REMOVE(&cond->c_queue, pthread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_CONDQ;
}
}
@ -642,19 +675,22 @@ cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
{
pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread);
/*
* 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))
TAILQ_INSERT_TAIL(&cond->c_queue, pthread, qe);
TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe);
else {
tid = TAILQ_FIRST(&cond->c_queue);
while (pthread->active_priority <= tid->active_priority)
tid = TAILQ_NEXT(tid, qe);
TAILQ_INSERT_BEFORE(tid, pthread, qe);
tid = TAILQ_NEXT(tid, sqe);
TAILQ_INSERT_BEFORE(tid, pthread, sqe);
}
pthread->flags |= PTHREAD_FLAGS_IN_CONDQ;
pthread->data.cond = cond;
}
#endif

View file

@ -49,17 +49,24 @@
static u_int64_t next_uniqueid = 1;
#define OFF(f) offsetof(struct pthread, f)
#define SIGFRAME_OFF(f) offsetof(struct pthread_signal_frame, f)
int _thread_next_offset = OFF(tle.tqe_next);
int _thread_uniqueid_offset = OFF(uniqueid);
int _thread_state_offset = OFF(state);
int _thread_name_offset = OFF(name);
int _thread_sig_saved_offset = OFF(sig_saved);
int _thread_saved_sigcontext_offset = OFF(saved_sigcontext);
int _thread_saved_jmp_buf_offset = OFF(saved_jmp_buf);
int _thread_curframe_offset = OFF(curframe);
int _thread_sigframe_ctx_offset = SIGFRAME_OFF(ctx);
int _thread_sigframe_ctxtype_offset = SIGFRAME_OFF(ctxtype);
#undef OFF
#undef SIGFRAME_OFF
int _thread_PS_RUNNING_value = PS_RUNNING;
int _thread_PS_DEAD_value = PS_DEAD;
int _thread_CTX_JB_NOSIG_value = CTX_JB_NOSIG;
int _thread_CTX_JB_value = CTX_JB;
int _thread_CTX_SJB_value = CTX_SJB;
int _thread_CTX_UC_value = CTX_UC;
int _thread_sigframe_size_value = sizeof(struct pthread_signal_frame);
int
pthread_create(pthread_t * thread, const pthread_attr_t * attr,
@ -162,7 +169,6 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Initialise the thread structure: */
memset(new_thread, 0, sizeof(struct pthread));
new_thread->slice_usec = -1;
new_thread->sig_saved = 0;
new_thread->stack = stack;
new_thread->start_routine = start_routine;
new_thread->arg = arg;
@ -179,62 +185,32 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Initialise the thread for signals: */
new_thread->sigmask = _thread_run->sigmask;
/* Initialize the first signal frame: */
new_thread->sigframes[0] = &new_thread->sigframe0;
new_thread->curframe = &new_thread->sigframe0;
/* Initialise the jump buffer: */
setjmp(new_thread->saved_jmp_buf);
_setjmp(new_thread->curframe->ctx.jb);
/*
* Set up new stack frame so that it looks like it
* returned from a longjmp() to the beginning of
* _thread_start().
*/
#if defined(__FreeBSD__)
#if defined(__alpha__)
new_thread->saved_jmp_buf[0]._jb[2] =
(long)_thread_start;
new_thread->saved_jmp_buf[0]._jb[4 + R_RA] =
0;
new_thread->saved_jmp_buf[0]._jb[4 + R_T12] =
(long)_thread_start;
#else
new_thread->saved_jmp_buf[0]._jb[0] =
(long)_thread_start;
#endif
#elif defined(__NetBSD__)
#if defined(__alpha__)
new_thread->saved_jmp_buf[2] = (long)_thread_start;
new_thread->saved_jmp_buf[4 + R_RA] = 0;
new_thread->saved_jmp_buf[4 + R_T12] =
(long)_thread_start;
#else
new_thread->saved_jmp_buf[0] = (long)_thread_start;
#endif
#else
#error "Don't recognize this operating system!"
#endif
SET_RETURN_ADDR_JB(new_thread->curframe->ctx.jb,
_thread_start);
/* The stack starts high and builds down: */
#if defined(__FreeBSD__)
#if defined(__alpha__)
new_thread->saved_jmp_buf[0]._jb[4 + R_SP] =
(long)new_thread->stack + pattr->stacksize_attr
- sizeof(double);
#else
new_thread->saved_jmp_buf[0]._jb[2] =
(int)(new_thread->stack + pattr->stacksize_attr -
sizeof(double));
#endif
#elif defined(__NetBSD__)
#if defined(__alpha__)
new_thread->saved_jmp_buf[4 + R_SP] =
(long)new_thread->stack + pattr->stacksize_attr -
sizeof(double);
#else
new_thread->saved_jmp_buf[2] = (long)new_thread->stack
+ pattr->stacksize_attr - sizeof(double);
#endif
#else
#error "Don't recognize this operating system!"
#endif
SET_STACK_JB(new_thread->curframe->ctx.jb,
(long)new_thread->stack + pattr->stacksize_attr
- sizeof(double));
/* Initialize the rest of the frame: */
new_thread->curframe->ctxtype = CTX_JB_NOSIG;
/* Set the base of the stack: */
new_thread->curframe->stackp =
GET_STACK_JB(new_thread->curframe->ctx.jb);
new_thread->sigframe_count = 0;
/* Copy the thread attributes: */
memcpy(&new_thread->attr, pattr, sizeof(struct pthread_attr));
@ -245,20 +221,22 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
*/
if (new_thread->attr.flags & PTHREAD_INHERIT_SCHED) {
/* Copy the scheduling attributes: */
new_thread->base_priority
= _thread_run->base_priority;
new_thread->attr.prio
= _thread_run->base_priority;
new_thread->attr.sched_policy
= _thread_run->attr.sched_policy;
new_thread->base_priority =
_thread_run->base_priority &
~PTHREAD_SIGNAL_PRIORITY;
new_thread->attr.prio =
_thread_run->base_priority &
~PTHREAD_SIGNAL_PRIORITY;
new_thread->attr.sched_policy =
_thread_run->attr.sched_policy;
} else {
/*
* Use just the thread priority, leaving the
* other scheduling attributes as their
* default values:
*/
new_thread->base_priority
= new_thread->attr.prio;
new_thread->base_priority =
new_thread->attr.prio;
}
new_thread->active_priority = new_thread->base_priority;
new_thread->inherited_priority = 0;
@ -275,7 +253,6 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
new_thread->flags = 0;
new_thread->poll_data.nfds = 0;
new_thread->poll_data.fds = NULL;
new_thread->jmpflags = 0;
new_thread->continuation = NULL;
/*
@ -317,7 +294,6 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Schedule the new user thread: */
_thread_kern_sched(NULL);
/*
* Start a garbage collector thread
* if necessary.
@ -325,6 +301,7 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
if (f_gc && pthread_create(&gc_thread,NULL,
_thread_gc,NULL) != 0)
PANIC("Can't create gc thread");
}
}

View file

@ -61,9 +61,10 @@ pthread_detach(pthread_t pthread)
/* Enter a loop to bring all threads off the join queue: */
while ((next_thread = TAILQ_FIRST(&pthread->join_queue)) != NULL) {
/* Remove the thread from the queue: */
TAILQ_REMOVE(&pthread->join_queue, next_thread, qe);
TAILQ_REMOVE(&pthread->join_queue, next_thread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
/* Make the thread run: */
/* Make the thread runnable: */
PTHREAD_NEW_STATE(next_thread,PS_RUNNING);
}

View file

@ -41,6 +41,9 @@
#include <pthread.h>
#include "pthread_private.h"
#define FLAGS_IN_SCHEDQ \
(PTHREAD_FLAGS_IN_PRIOQ|PTHREAD_FLAGS_IN_WAITQ|PTHREAD_FLAGS_IN_WORKQ)
void __exit(int status)
{
int flags;
@ -138,7 +141,7 @@ _thread_exit_cleanup(void)
void
pthread_exit(void *status)
{
pthread_t pthread;
int frame;
/* Check if this thread is already in the process of exiting: */
if ((_thread_run->flags & PTHREAD_EXITING) != 0) {
@ -172,25 +175,24 @@ pthread_exit(void *status)
_thread_run->poll_data.fds = NULL;
}
/*
* Defer signals to protect the scheduling queues from access
* by the signal handler:
*/
_thread_kern_sig_defer();
/* Check if there are any threads joined to this one: */
while ((pthread = TAILQ_FIRST(&(_thread_run->join_queue))) != NULL) {
/* Remove the thread from the queue: */
TAILQ_REMOVE(&_thread_run->join_queue, pthread, qe);
/* Wake the joined thread and let it detach this thread: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
if ((frame = _thread_run->sigframe_count) == 0)
_thread_exit_finish();
else {
/*
* Jump back and unwind the signal frames to gracefully
* cleanup.
*/
___longjmp(*_thread_run->sigframes[frame]->sig_jb, 1);
}
/*
* Undefer and handle pending signals, yielding if necessary:
*/
_thread_kern_sig_undefer();
/* This point should not be reached. */
PANIC("Dead thread has resumed");
}
void
_thread_exit_finish(void)
{
pthread_t pthread;
/*
* Lock the garbage collector mutex to ensure that the garbage
@ -202,20 +204,6 @@ pthread_exit(void *status)
/* Add this thread to the list of dead threads. */
TAILQ_INSERT_HEAD(&_dead_list, _thread_run, dle);
/*
* Defer signals to protect the scheduling queues from access
* by the signal handler:
*/
_thread_kern_sig_defer();
/* Remove this thread from the thread list: */
TAILQ_REMOVE(&_thread_list, _thread_run, tle);
/*
* Undefer and handle pending signals, yielding if necessary:
*/
_thread_kern_sig_undefer();
/*
* Signal the garbage collector thread that there is something
* to clean up.
@ -224,19 +212,33 @@ pthread_exit(void *status)
PANIC("Cannot signal gc cond");
/*
* Mark the thread as dead so it will not return if it
* gets context switched out when the mutex is unlocked.
* Avoid a race condition where a scheduling signal can occur
* causing the garbage collector thread to run. If this happens,
* the current thread can be cleaned out from under us.
*/
PTHREAD_SET_STATE(_thread_run, PS_DEAD);
_thread_kern_sig_defer();
/* Unlock the garbage collector mutex: */
if (pthread_mutex_unlock(&_gc_mutex) != 0)
PANIC("Cannot lock gc mutex");
/* This this thread will never be re-scheduled. */
_thread_kern_sched(NULL);
/* Check if there are any threads joined to this one: */
while ((pthread = TAILQ_FIRST(&(_thread_run->join_queue))) != NULL) {
/* Remove the thread from the queue: */
TAILQ_REMOVE(&_thread_run->join_queue, pthread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
/* This point should not be reached. */
PANIC("Dead thread has resumed");
/*
* Wake the joined thread and let it
* detach this thread:
*/
PTHREAD_NEW_STATE(pthread, PS_RUNNING);
}
/* Remove this thread from the thread list: */
TAILQ_REMOVE(&_thread_list, _thread_run, tle);
/* This thread will never be re-scheduled. */
_thread_kern_sched_state(PS_DEAD, __FILE__, __LINE__);
}
#endif

View file

@ -183,9 +183,6 @@ _fork(void)
/* Don't queue signals yet: */
_queue_signals = 0;
/* Initialize signal handling: */
_thread_sig_init();
/* Initialize the scheduling switch hook routine: */
_sched_switch_hook = NULL;

View file

@ -57,8 +57,8 @@ _thread_gc(pthread_addr_t arg)
void *p_stack;
/* Block all signals */
sigfillset (&mask);
sigprocmask (SIG_BLOCK, &mask, NULL);
sigfillset(&mask);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
/* Mark this thread as a library thread (not a user thread). */
_thread_run->flags |= PTHREAD_FLAGS_PRIVATE;

View file

@ -49,7 +49,8 @@ pthread_getschedparam(pthread_t pthread, int *policy,
/* Find the thread in the list of active threads: */
else if ((ret = _find_thread(pthread)) == 0) {
/* Return the threads base priority and scheduling policy: */
param->sched_priority = pthread->base_priority;
param->sched_priority =
PTHREAD_BASE_PRIORITY(pthread->base_priority);
*policy = pthread->attr.sched_policy;
}

View file

@ -32,6 +32,7 @@
* $FreeBSD$
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
@ -296,7 +297,6 @@ _thread_dump_info(void)
/* Close the dump file: */
_thread_sys_close(fd);
}
return;
}
/* Set the thread name for debug: */

View file

@ -90,9 +90,9 @@ _thread_init(void)
int i;
size_t len;
int mib[2];
struct timeval tv;
struct clockinfo clockinfo;
struct sigaction act;
struct itimerval itimer;
/* Check if this function has already been called: */
if (_thread_initial)
@ -160,7 +160,7 @@ _thread_init(void)
PANIC("Cannot get kernel write pipe flags");
}
/* Allocate and initialize the ready queue: */
else if (_pq_alloc(&_readyq, PTHREAD_MIN_PRIORITY, PTHREAD_MAX_PRIORITY) != 0) {
else if (_pq_alloc(&_readyq, PTHREAD_MIN_PRIORITY, PTHREAD_LAST_PRIORITY) != 0) {
/* Abort this application: */
PANIC("Cannot allocate priority ready queue.");
}
@ -171,7 +171,11 @@ _thread_init(void)
* abort:
*/
PANIC("Cannot allocate memory for initial thread");
} else {
}
/* Allocate memory for the scheduler stack: */
else if ((_thread_kern_sched_stack = malloc(PAGE_SIZE * 10)) == NULL)
PANIC("Failed to allocate stack for scheduler");
else {
/* Zero the global kernel thread structure: */
memset(&_thread_kern_thread, 0, sizeof(struct pthread));
_thread_kern_thread.flags = PTHREAD_FLAGS_PRIVATE;
@ -211,6 +215,12 @@ _thread_init(void)
_thread_initial->attr.stackaddr_attr = _thread_initial->stack;
_thread_initial->attr.stacksize_attr = PTHREAD_STACK_INITIAL;
/* Setup the context for the scheduler: */
_setjmp(_thread_kern_sched_jb);
SET_STACK_JB(_thread_kern_sched_jb,
_thread_kern_sched_stack + PAGE_SIZE*10 - sizeof(double));
SET_RETURN_ADDR_JB(_thread_kern_sched_jb, _thread_kern_scheduler);
/*
* Write a magic value to the thread structure
* to help identify valid ones:
@ -236,10 +246,19 @@ _thread_init(void)
TAILQ_INIT(&(_thread_initial->mutexq));
_thread_initial->priority_mutex_count = 0;
/* Initialize last active time to now: */
gettimeofday(&tv, NULL);
_thread_initial->last_active.tv_sec = tv.tv_sec;
_thread_initial->last_active.tv_usec = tv.tv_usec;
/* Initialize the global scheduling time: */
_sched_ticks = 0;
gettimeofday((struct timeval *) &_sched_tod, NULL);
/* Initialize last active: */
_thread_initial->last_active = (long) _sched_ticks;
/* Initialize the initial signal frame: */
_thread_initial->sigframes[0] = &_thread_initial->sigframe0;
_thread_initial->curframe = &_thread_initial->sigframe0;
_thread_initial->curframe->ctxtype = CTX_JB_NOSIG;
/* Set the base of the stack: */
_thread_initial->curframe->stackp = (unsigned long) USRSTACK;
/* Initialise the rest of the fields: */
_thread_initial->poll_data.nfds = 0;
@ -257,10 +276,13 @@ _thread_init(void)
/* Initialise the global signal action structure: */
sigfillset(&act.sa_mask);
act.sa_handler = (void (*) ()) _thread_sig_handler;
act.sa_flags = 0;
act.sa_flags = SA_SIGINFO;
/* Initialize signal handling: */
_thread_sig_init();
/* Clear pending signals for the process: */
sigemptyset(&_process_sigpending);
/* Clear the signal queue: */
memset(_thread_sigq, 0, sizeof(_thread_sigq));
/* Enter a loop to get the existing signal status: */
for (i = 1; i < NSIG; i++) {
@ -295,13 +317,19 @@ _thread_init(void)
*/
PANIC("Cannot initialise signal handler");
}
_thread_sigact[_SCHED_SIGNAL - 1].sa_flags = SA_SIGINFO;
_thread_sigact[SIGINFO - 1].sa_flags = SA_SIGINFO;
_thread_sigact[SIGCHLD - 1].sa_flags = SA_SIGINFO;
/* Get the process signal mask: */
_thread_sys_sigprocmask(SIG_SETMASK, NULL, &_process_sigmask);
/* Get the kernel clockrate: */
mib[0] = CTL_KERN;
mib[1] = KERN_CLOCKRATE;
len = sizeof (struct clockinfo);
if (sysctl(mib, 2, &clockinfo, &len, NULL, 0) == 0)
_clock_res_nsec = clockinfo.tick * 1000;
_clock_res_usec = clockinfo.tick;
/* Get the table size: */
if ((_thread_dtablesize = getdtablesize()) < 0) {
@ -346,6 +374,14 @@ _thread_init(void)
PANIC("Cannot initialize stdio file "
"descriptor table entry");
}
/* Install the scheduling timer: */
itimer.it_interval.tv_sec = 0;
itimer.it_interval.tv_usec = _clock_res_usec;
itimer.it_value = itimer.it_interval;
if (setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL) != 0)
PANIC("Cannot set interval timer");
}
}
@ -362,10 +398,6 @@ _thread_init(void)
if (pthread_mutex_init(&_gc_mutex,NULL) != 0 ||
pthread_cond_init(&_gc_cond,NULL) != 0)
PANIC("Failed to initialise garbage collector mutex or condvar");
gettimeofday(&kern_inc_prio_time, NULL);
return;
}
/*

View file

@ -40,7 +40,6 @@ int
pthread_join(pthread_t pthread, void **thread_return)
{
int ret = 0;
pthread_t pthread1 = NULL;
_thread_enter_cancellation_point();
@ -62,11 +61,7 @@ pthread_join(pthread_t pthread, void **thread_return)
* Find the thread in the list of active threads or in the
* list of dead threads:
*/
if (_find_thread(pthread) == 0 ||
_find_dead_thread(pthread) == 0)
pthread1 = pthread;
if (pthread1 == NULL)
if ((_find_thread(pthread) != 0) && (_find_dead_thread(pthread) != 0))
/* Return an error: */
ret = ESRCH;
@ -77,6 +72,8 @@ pthread_join(pthread_t pthread, void **thread_return)
/* Check if the thread is not dead: */
else if (pthread->state != PS_DEAD) {
PTHREAD_ASSERT_NOT_IN_SYNCQ(_thread_run);
/* Clear the interrupted flag: */
_thread_run->interrupted = 0;
@ -87,13 +84,18 @@ pthread_join(pthread_t pthread, void **thread_return)
_thread_kern_sig_defer();
/* Add the running thread to the join queue: */
TAILQ_INSERT_TAIL(&(pthread->join_queue), _thread_run, qe);
TAILQ_INSERT_TAIL(&(pthread->join_queue), _thread_run, sqe);
_thread_run->flags |= PTHREAD_FLAGS_IN_JOINQ;
_thread_run->data.thread = pthread;
/* Schedule the next thread: */
_thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
if (_thread_run->interrupted != 0)
TAILQ_REMOVE(&(pthread->join_queue), _thread_run, qe);
if (_thread_run->interrupted != 0) {
TAILQ_REMOVE(&(pthread->join_queue), _thread_run, sqe);
_thread_run->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
}
_thread_run->data.thread = NULL;
_thread_kern_sig_undefer();
@ -122,4 +124,15 @@ pthread_join(pthread_t pthread, void **thread_return)
/* Return the completion status: */
return (ret);
}
void
_join_backout(pthread_t pthread)
{
_thread_kern_sig_defer();
if (pthread->state == PS_JOIN) {
TAILQ_REMOVE(&pthread->data.thread->join_queue, pthread, sqe);
_thread_run->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
}
_thread_kern_sig_undefer();
}
#endif

View file

@ -52,9 +52,16 @@
#include <pthread.h>
#include "pthread_private.h"
/* #define DEBUG_THREAD_KERN */
#ifdef DEBUG_THREAD_KERN
#define DBG_MSG stdout_debug
#else
#define DBG_MSG(x...)
#endif
/* Static function prototype definitions: */
static void
_thread_kern_poll(int wait_reqd);
thread_kern_poll(int wait_reqd);
static void
dequeue_signals(void);
@ -62,18 +69,39 @@ dequeue_signals(void);
static inline void
thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in);
void
_thread_kern_sched(ucontext_t * scp)
{
#ifndef __alpha__
char *fdata;
#endif
pthread_t pthread, pthread_h = NULL;
struct itimerval itimer;
struct timespec ts, ts1;
struct timeval tv, tv1;
int set_timer = 0;
/* Static variables: */
static int last_tick = 0;
/*
* This is called when a signal handler finishes and wants to
* return to a previous frame.
*/
void
_thread_kern_sched_frame(int frame)
{
/*
* Flag the pthread kernel as executing scheduler code
* to avoid a signal from interrupting this execution and
* corrupting the (soon-to-be) current frame.
*/
_thread_kern_in_sched = 1;
/* Return to the specified frame: */
_thread_run->curframe = _thread_run->sigframes[frame];
_thread_run->sigframe_count = frame;
if (_thread_run->sigframe_count == 0)
/* Restore the threads priority: */
_thread_run->active_priority &= ~PTHREAD_SIGNAL_PRIORITY;
/* Switch to the thread scheduler: */
___longjmp(_thread_kern_sched_jb, 1);
}
void
_thread_kern_sched(ucontext_t *scp)
{
/*
* Flag the pthread kernel as executing scheduler code
* to avoid a scheduler signal from interrupting this
@ -84,67 +112,94 @@ _thread_kern_sched(ucontext_t * scp)
/* Check if this function was called from the signal handler: */
if (scp != NULL) {
/*
* Copy the signal context to the current thread's jump
* buffer:
* The signal handler should have saved the state of
* the current thread. Restore the process signal
* mask.
*/
memcpy(&_thread_run->saved_sigcontext, scp, sizeof(_thread_run->saved_sigcontext));
#ifndef __alpha__
/* Point to the floating point data in the running thread: */
fdata = _thread_run->saved_fp;
/* Save the floating point data: */
__asm__("fnsave %0": :"m"(*fdata));
#endif
/* Flag the signal context as the last state saved: */
_thread_run->sig_saved = 1;
}
/* Save the state of the current thread: */
else if (setjmp(_thread_run->saved_jmp_buf) != 0) {
if (_thread_sys_sigprocmask(SIG_SETMASK,
&_process_sigmask, NULL) != 0)
PANIC("Unable to restore process mask after signal");
/*
* This point is reached when a longjmp() is called to
* restore the state of a thread.
*
* This is the normal way out of the scheduler.
* We're running on the signal stack; just call the
* kernel scheduler directly.
*/
_thread_kern_in_sched = 0;
if (((_thread_run->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0) &&
((_thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)) {
/*
* Cancellations override signals.
DBG_MSG("Entering scheduler due to signal\n");
_thread_kern_scheduler();
} else {
/* Save the state of the current thread: */
if (_setjmp(_thread_run->curframe->ctx.jb) == 0) {
/* Flag the jump buffer was the last state saved: */
_thread_run->curframe->ctxtype = CTX_JB_NOSIG;
_thread_run->curframe->longjmp_val = 1;
} else {
DBG_MSG("Returned from ___longjmp, thread %p\n",
_thread_run);
/*
* This point is reached when a longjmp() is called
* to restore the state of a thread.
*
* Stick a cancellation point at the start of
* each async-cancellable thread's resumption.
*
* We allow threads woken at cancel points to do their
* own checks.
* This is the normal way out of the scheduler.
*/
pthread_testcancel();
_thread_kern_in_sched = 0;
if (_thread_run->sig_defer_count == 0) {
if (((_thread_run->cancelflags &
PTHREAD_AT_CANCEL_POINT) == 0) &&
((_thread_run->cancelflags &
PTHREAD_CANCEL_ASYNCHRONOUS) != 0))
/*
* Cancellations override signals.
*
* Stick a cancellation point at the
* start of each async-cancellable
* thread's resumption.
*
* We allow threads woken at cancel
* points to do their own checks.
*/
pthread_testcancel();
}
if (_sched_switch_hook != NULL) {
/* Run the installed switch hook: */
thread_run_switch_hook(_last_user_thread,
_thread_run);
}
return;
}
/* Switch to the thread scheduler: */
___longjmp(_thread_kern_sched_jb, 1);
}
}
/*
* Check for undispatched signals due to calls to
* pthread_kill().
*/
if (SIGNOTEMPTY(_thread_run->sigpend))
_dispatch_signals();
void
_thread_kern_sched_sig(void)
{
_thread_run->check_pending = 1;
_thread_kern_sched(NULL);
}
if (_sched_switch_hook != NULL) {
/* Run the installed switch hook: */
thread_run_switch_hook(_last_user_thread, _thread_run);
}
return;
} else
/* Flag the jump buffer was the last state saved: */
_thread_run->sig_saved = 0;
void
_thread_kern_scheduler(void)
{
struct pthread_signal_frame *psf;
struct timespec ts;
struct timeval tv;
pthread_t pthread, pthread_h;
unsigned int current_tick;
int add_to_prioq;
/* If the currently running thread is a user thread, save it: */
if ((_thread_run->flags & PTHREAD_FLAGS_PRIVATE) == 0)
_last_user_thread = _thread_run;
/* Are there pending signals for this thread? */
if (_thread_run->check_pending != 0) {
_thread_run->check_pending = 0;
_thread_sig_check_pending(_thread_run);
}
/*
* Enter a scheduling loop that finds the next thread that is
* ready to run. This loop completes when there are no more threads
@ -154,29 +209,37 @@ __asm__("fnsave %0": :"m"(*fdata));
*/
while (!(TAILQ_EMPTY(&_thread_list))) {
/* Get the current time of day: */
gettimeofday(&tv, NULL);
GET_CURRENT_TOD(tv);
TIMEVAL_TO_TIMESPEC(&tv, &ts);
current_tick = _sched_ticks;
/*
* Protect the scheduling queues from access by the signal
* handler.
*/
_queue_signals = 1;
add_to_prioq = 0;
if (_thread_run != &_thread_kern_thread) {
/*
* This thread no longer needs to yield the CPU.
*/
_thread_run->yield_on_sig_undefer = 0;
/*
* Save the current time as the time that the thread
* became inactive:
*/
_thread_run->last_inactive.tv_sec = tv.tv_sec;
_thread_run->last_inactive.tv_usec = tv.tv_usec;
if (_thread_run->state != PS_RUNNING) {
/*
* Save the current time as the time that the
* thread became inactive:
*/
_thread_run->last_inactive = (long)current_tick;
if (_thread_run->last_inactive <
_thread_run->last_active) {
/* Account for a rollover: */
_thread_run->last_inactive =+
UINT_MAX + 1;
}
}
/*
* Place the currently running thread into the
* appropriate queue(s).
@ -198,22 +261,7 @@ __asm__("fnsave %0": :"m"(*fdata));
* are polled (to preserve round-robin
* scheduling).
*/
if ((_thread_run->slice_usec != -1) &&
(_thread_run->attr.sched_policy != SCHED_FIFO)) {
/*
* Accumulate the number of microseconds that
* this thread has run for:
*/
_thread_run->slice_usec +=
(_thread_run->last_inactive.tv_sec -
_thread_run->last_active.tv_sec) * 1000000 +
_thread_run->last_inactive.tv_usec -
_thread_run->last_active.tv_usec;
/* Check for time quantum exceeded: */
if (_thread_run->slice_usec > TIMESLICE_USEC)
_thread_run->slice_usec = -1;
}
add_to_prioq = 1;
break;
/*
@ -260,7 +308,7 @@ __asm__("fnsave %0": :"m"(*fdata));
/* Increment spinblock count: */
_spinblock_count++;
/* fall through */
/* FALLTHROUGH */
case PS_FDR_WAIT:
case PS_FDW_WAIT:
case PS_POLL_WAIT:
@ -277,17 +325,26 @@ __asm__("fnsave %0": :"m"(*fdata));
}
}
/* Unprotect the scheduling queues: */
_queue_signals = 0;
/*
* Poll file descriptors to update the state of threads
* waiting on file I/O where data may be available:
* Poll file descriptors only if a new scheduling signal
* has occurred or if we have no more runnable threads.
*/
_thread_kern_poll(0);
if (((current_tick = _sched_ticks) != last_tick) ||
((_thread_run->state != PS_RUNNING) &&
(PTHREAD_PRIOQ_FIRST() == NULL))) {
/* Unprotect the scheduling queues: */
_queue_signals = 0;
/* Protect the scheduling queues: */
_queue_signals = 1;
/*
* Poll file descriptors to update the state of threads
* waiting on file I/O where data may be available:
*/
thread_kern_poll(0);
/* Protect the scheduling queues: */
_queue_signals = 1;
}
last_tick = current_tick;
/*
* Wake up threads that have timedout. This has to be
@ -329,12 +386,37 @@ __asm__("fnsave %0": :"m"(*fdata));
PTHREAD_WAITQ_CLEARACTIVE();
/*
* Check if there is a current runnable thread that isn't
* already in the ready queue:
* Check to see if the current thread needs to be added
* to the priority queue:
*/
if ((_thread_run != &_thread_kern_thread) &&
(_thread_run->state == PS_RUNNING) &&
((_thread_run->flags & PTHREAD_FLAGS_IN_PRIOQ) == 0)) {
if (add_to_prioq != 0) {
/*
* Save the current time as the time that the
* thread became inactive:
*/
current_tick = _sched_ticks;
_thread_run->last_inactive = (long)current_tick;
if (_thread_run->last_inactive <
_thread_run->last_active) {
/* Account for a rollover: */
_thread_run->last_inactive =+ UINT_MAX + 1;
}
if ((_thread_run->slice_usec != -1) &&
(_thread_run->attr.sched_policy != SCHED_FIFO)) {
/*
* Accumulate the number of microseconds for
* which the current thread has run:
*/
_thread_run->slice_usec +=
(_thread_run->last_inactive -
_thread_run->last_active) *
(long)_clock_res_usec;
/* Check for time quantum exceeded: */
if (_thread_run->slice_usec > TIMESLICE_USEC)
_thread_run->slice_usec = -1;
}
if (_thread_run->slice_usec == -1) {
/*
* The thread exceeded its time
@ -366,6 +448,8 @@ __asm__("fnsave %0": :"m"(*fdata));
* thread structure:
*/
_thread_run = &_thread_kern_thread;
DBG_MSG("No runnable threads, using kernel thread %p\n",
_thread_run);
/* Unprotect the scheduling queues: */
_queue_signals = 0;
@ -374,21 +458,28 @@ __asm__("fnsave %0": :"m"(*fdata));
* There are no threads ready to run, so wait until
* something happens that changes this condition:
*/
_thread_kern_poll(1);
} else {
thread_kern_poll(1);
/*
* This process' usage will likely be very small
* while waiting in a poll. Since the scheduling
* clock is based on the profiling timer, it is
* unlikely that the profiling timer will fire
* and update the time of day. To account for this,
* get the time of day after polling with a timeout.
*/
gettimeofday((struct timeval *) &_sched_tod, NULL);
/* Check once more for a runnable thread: */
_queue_signals = 1;
pthread_h = PTHREAD_PRIOQ_FIRST();
_queue_signals = 0;
}
if (pthread_h != NULL) {
/* Remove the thread from the ready queue: */
PTHREAD_PRIOQ_REMOVE(pthread_h);
/* Get first thread on the waiting list: */
pthread = TAILQ_FIRST(&_waitingq);
/* Check to see if there is more than one thread: */
if (pthread_h != TAILQ_FIRST(&_thread_list) ||
TAILQ_NEXT(pthread_h, tle) != NULL)
set_timer = 1;
else
set_timer = 0;
/* Unprotect the scheduling queues: */
_queue_signals = 0;
@ -411,32 +502,19 @@ __asm__("fnsave %0": :"m"(*fdata));
*/
if (((pthread = PTHREAD_PRIOQ_FIRST()) != NULL) &&
(pthread->active_priority > pthread_h->active_priority)) {
/* Remove the thread from the ready queue: */
PTHREAD_PRIOQ_REMOVE(pthread);
/*
* Insert the lower priority thread
* at the head of its priority list:
*/
PTHREAD_PRIOQ_INSERT_HEAD(pthread_h);
/* Remove the thread from the ready queue: */
PTHREAD_PRIOQ_REMOVE(pthread);
/* There's a new thread in town: */
pthread_h = pthread;
}
/* Get first thread on the waiting list: */
pthread = TAILQ_FIRST(&_waitingq);
/*
* Check to see if there is more than one
* thread:
*/
if (pthread_h != TAILQ_FIRST(&_thread_list) ||
TAILQ_NEXT(pthread_h, tle) != NULL)
set_timer = 1;
else
set_timer = 0;
/* Unprotect the scheduling queues: */
_queue_signals = 0;
}
@ -448,78 +526,8 @@ __asm__("fnsave %0": :"m"(*fdata));
* Save the current time as the time that the thread
* became active:
*/
_thread_run->last_active.tv_sec = tv.tv_sec;
_thread_run->last_active.tv_usec = tv.tv_usec;
/*
* Define the maximum time before a scheduling signal
* is required:
*/
itimer.it_value.tv_sec = 0;
itimer.it_value.tv_usec = TIMESLICE_USEC;
/*
* The interval timer is not reloaded when it
* times out. The interval time needs to be
* calculated every time.
*/
itimer.it_interval.tv_sec = 0;
itimer.it_interval.tv_usec = 0;
/* Get first thread on the waiting list: */
if ((pthread != NULL) &&
(pthread->wakeup_time.tv_sec != -1)) {
/*
* Calculate the time until this thread
* is ready, allowing for the clock
* resolution:
*/
ts1.tv_sec = pthread->wakeup_time.tv_sec
- ts.tv_sec;
ts1.tv_nsec = pthread->wakeup_time.tv_nsec
- ts.tv_nsec + _clock_res_nsec;
/*
* Check for underflow of the nanosecond field:
*/
while (ts1.tv_nsec < 0) {
/*
* Allow for the underflow of the
* nanosecond field:
*/
ts1.tv_sec--;
ts1.tv_nsec += 1000000000;
}
/*
* Check for overflow of the nanosecond field:
*/
while (ts1.tv_nsec >= 1000000000) {
/*
* Allow for the overflow of the
* nanosecond field:
*/
ts1.tv_sec++;
ts1.tv_nsec -= 1000000000;
}
/*
* Convert the timespec structure to a
* timeval structure:
*/
TIMESPEC_TO_TIMEVAL(&tv1, &ts1);
/*
* Check if the thread will be ready
* sooner than the earliest ones found
* so far:
*/
if (timercmp(&tv1, &itimer.it_value, <)) {
/*
* Update the time value:
*/
itimer.it_value.tv_sec = tv1.tv_sec;
itimer.it_value.tv_usec = tv1.tv_usec;
}
}
current_tick = _sched_ticks;
_thread_run->last_active = (long) current_tick;
/*
* Check if this thread is running for the first time
@ -531,88 +539,51 @@ __asm__("fnsave %0": :"m"(*fdata));
_thread_run->slice_usec = 0;
}
/* Check if there is more than one thread: */
if (set_timer != 0) {
/*
* Start the interval timer for the
* calculated time interval:
*/
if (setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL) != 0) {
/*
* Cannot initialise the timer, so
* abort this process:
*/
PANIC("Cannot set scheduling timer");
}
/*
* If we had a context switch, run any
* installed switch hooks.
*/
if ((_sched_switch_hook != NULL) &&
(_last_user_thread != _thread_run)) {
thread_run_switch_hook(_last_user_thread,
_thread_run);
}
/*
* Continue the thread at its current frame:
*/
psf = _thread_run->curframe;
switch(psf->ctxtype) {
case CTX_JB_NOSIG:
___longjmp(psf->ctx.jb, psf->longjmp_val);
break;
case CTX_JB:
__longjmp(psf->ctx.jb, psf->longjmp_val);
break;
case CTX_SJB:
__siglongjmp(psf->ctx.sigjb, psf->longjmp_val);
break;
case CTX_UC:
/* XXX - Restore FP regsisters? */
FP_RESTORE_UC(&psf->ctx.uc);
/*
* Check if this thread is being continued from a
* longjmp() out of a signal handler:
*/
if ((_thread_run->jmpflags & JMPFLAGS_LONGJMP) != 0) {
_thread_run->jmpflags = 0;
__longjmp(_thread_run->nested_jmp.jmp,
_thread_run->longjmp_val);
}
/*
* Check if this thread is being continued from a
* _longjmp() out of a signal handler:
*/
else if ((_thread_run->jmpflags & JMPFLAGS__LONGJMP) !=
0) {
_thread_run->jmpflags = 0;
___longjmp(_thread_run->nested_jmp.jmp,
_thread_run->longjmp_val);
}
/*
* Check if this thread is being continued from a
* siglongjmp() out of a signal handler:
*/
else if ((_thread_run->jmpflags & JMPFLAGS_SIGLONGJMP)
!= 0) {
_thread_run->jmpflags = 0;
__siglongjmp(
_thread_run->nested_jmp.sigjmp,
_thread_run->longjmp_val);
}
/* Check if a signal context was saved: */
else if (_thread_run->sig_saved == 1) {
#ifndef __alpha__
/*
* Point to the floating point data in the
* running thread:
*/
fdata = _thread_run->saved_fp;
/* Restore the floating point state: */
__asm__("frstor %0": :"m"(*fdata));
#endif
/*
* Do a sigreturn to restart the thread that
* was interrupted by a signal:
*/
_thread_kern_in_sched = 0;
#if NOT_YET
_setcontext(&psf->ctx.uc);
#else
/*
* If we had a context switch, run any
* installed switch hooks.
* Ensure the process signal mask is set
* correctly:
*/
if ((_sched_switch_hook != NULL) &&
(_last_user_thread != _thread_run)) {
thread_run_switch_hook(_last_user_thread,
_thread_run);
}
_thread_sys_sigreturn(&_thread_run->saved_sigcontext);
} else {
/*
* Do a longjmp to restart the thread that
* was context switched out (by a longjmp to
* a different thread):
*/
__longjmp(_thread_run->saved_jmp_buf, 1);
psf->ctx.uc.uc_sigmask = _process_sigmask;
_thread_sys_sigreturn(&psf->ctx.uc);
#endif
break;
}
/* This point should not be reached. */
PANIC("Thread has returned from sigreturn or longjmp");
}
@ -645,7 +616,6 @@ _thread_kern_sched_state(enum pthread_state state, char *fname, int lineno)
/* Schedule the next thread that is ready: */
_thread_kern_sched(NULL);
return;
}
void
@ -675,11 +645,10 @@ _thread_kern_sched_state_unlock(enum pthread_state state,
/* Schedule the next thread that is ready: */
_thread_kern_sched(NULL);
return;
}
static void
_thread_kern_poll(int wait_reqd)
thread_kern_poll(int wait_reqd)
{
int count = 0;
int i, found;
@ -696,7 +665,7 @@ _thread_kern_poll(int wait_reqd)
}
else {
/* Get the current time of day: */
gettimeofday(&tv, NULL);
GET_CURRENT_TOD(tv);
TIMEVAL_TO_TIMESPEC(&tv, &ts);
_queue_signals = 1;
@ -713,11 +682,11 @@ _thread_kern_poll(int wait_reqd)
else {
/*
* Calculate the time left for the next thread to
* timeout allowing for the clock resolution:
* timeout:
*/
timeout_ms = ((pthread->wakeup_time.tv_sec - ts.tv_sec) *
1000) + ((pthread->wakeup_time.tv_nsec - ts.tv_nsec +
_clock_res_nsec) / 1000000);
1000) + ((pthread->wakeup_time.tv_nsec - ts.tv_nsec) /
1000000);
/*
* Don't allow negative timeouts:
*/
@ -1002,9 +971,6 @@ _thread_kern_poll(int wait_reqd)
/* Unprotect the scheduling queues: */
_queue_signals = 0;
}
/* Nothing to return. */
return;
}
void
@ -1032,7 +998,7 @@ _thread_kern_set_timeout(const struct timespec * timeout)
_thread_run->wakeup_time.tv_nsec = 0;
} else {
/* Get the current time: */
gettimeofday(&tv, NULL);
GET_CURRENT_TOD(tv);
TIMEVAL_TO_TIMESPEC(&tv, &current_time);
/* Calculate the time for the current thread to wake up: */
@ -1046,7 +1012,6 @@ _thread_kern_set_timeout(const struct timespec * timeout)
_thread_run->wakeup_time.tv_nsec -= 1000000000;
}
}
return;
}
void
@ -1059,9 +1024,6 @@ _thread_kern_sig_defer(void)
void
_thread_kern_sig_undefer(void)
{
pthread_t pthread;
int need_resched = 0;
/*
* Perform checks to yield only if we are about to undefer
* signals.
@ -1077,33 +1039,25 @@ _thread_kern_sig_undefer(void)
/*
* Check if there are queued signals:
*/
while (_sigq_check_reqd != 0) {
/* Defer scheduling while we process queued signals: */
_thread_run->sig_defer_count = 1;
if (_sigq_check_reqd != 0)
_thread_kern_sched(NULL);
/* Clear the flag before checking the signal queue: */
_sigq_check_reqd = 0;
/*
* Check for asynchronous cancellation before delivering any
* pending signals:
*/
if (((_thread_run->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0) &&
((_thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0))
pthread_testcancel();
/* Dequeue and handle signals: */
dequeue_signals();
/*
* Avoiding an unnecessary check to reschedule, check
* to see if signal handling caused a higher priority
* thread to become ready.
*/
if ((need_resched == 0) &&
(((pthread = PTHREAD_PRIOQ_FIRST()) != NULL) &&
(pthread->active_priority > _thread_run->active_priority))) {
need_resched = 1;
}
/* Reenable signals: */
_thread_run->sig_defer_count = 0;
}
/* Yield the CPU if necessary: */
if (need_resched || _thread_run->yield_on_sig_undefer != 0) {
/*
* If there are pending signals or this thread has
* to yield the CPU, call the kernel scheduler:
*
* XXX - Come back and revisit the pending signal problem
*/
if ((_thread_run->yield_on_sig_undefer != 0) ||
SIGNOTEMPTY(_thread_run->sigpend)) {
_thread_run->yield_on_sig_undefer = 0;
_thread_kern_sched(NULL);
}
@ -1114,35 +1068,13 @@ static void
dequeue_signals(void)
{
char bufr[128];
int i, num;
pthread_t pthread;
int num;
/*
* Enter a loop to read and handle queued signals from the
* pthread kernel pipe:
* Enter a loop to clear the pthread kernel pipe:
*/
while (((num = _thread_sys_read(_thread_kern_pipe[0], bufr,
sizeof(bufr))) > 0) || (num == -1 && errno == EINTR)) {
/*
* The buffer read contains one byte per signal and
* each byte is the signal number.
*/
for (i = 0; i < num; i++) {
if ((int) bufr[i] == _SCHED_SIGNAL) {
/*
* Scheduling signals shouldn't ever be
* queued; just ignore it for now.
*/
}
else {
/* Handle this signal: */
pthread = _thread_sig_handle((int) bufr[i],
NULL);
if (pthread != NULL)
_thread_sig_deliver(pthread,
(int) bufr[i]);
}
}
}
if ((num < 0) && (errno != EAGAIN)) {
/*
@ -1151,6 +1083,8 @@ dequeue_signals(void)
*/
PANIC("Unable to read from thread kernel pipe");
}
/* Handle any pending signals: */
_thread_sig_handle_pending();
}
static inline void

View file

@ -79,7 +79,7 @@ static spinlock_t static_init_lock = _SPINLOCK_INITIALIZER;
int
_mutex_reinit(pthread_mutex_t * mutex)
{
int ret = 0;
int ret = 0;
if (mutex == NULL)
ret = EINVAL;
@ -113,7 +113,7 @@ pthread_mutex_init(pthread_mutex_t * mutex,
int protocol;
int ceiling;
pthread_mutex_t pmutex;
int ret = 0;
int ret = 0;
if (mutex == NULL)
ret = EINVAL;
@ -203,7 +203,7 @@ pthread_mutex_init(pthread_mutex_t * mutex,
int
pthread_mutex_destroy(pthread_mutex_t * mutex)
{
int ret = 0;
int ret = 0;
if (mutex == NULL || *mutex == NULL)
ret = EINVAL;
@ -245,7 +245,7 @@ pthread_mutex_destroy(pthread_mutex_t * mutex)
static int
init_static(pthread_mutex_t *mutex)
{
int ret;
int ret;
_SPINLOCK(&static_init_lock);
@ -262,7 +262,7 @@ init_static(pthread_mutex_t *mutex)
int
pthread_mutex_trylock(pthread_mutex_t * mutex)
{
int ret = 0;
int ret = 0;
if (mutex == NULL)
ret = EINVAL;
@ -400,7 +400,7 @@ pthread_mutex_trylock(pthread_mutex_t * mutex)
int
pthread_mutex_lock(pthread_mutex_t * mutex)
{
int ret = 0;
int ret = 0;
if (mutex == NULL)
ret = EINVAL;
@ -610,9 +610,8 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
* Check to see if this thread was interrupted and
* is still in the mutex queue of waiting threads:
*/
if (_thread_run->interrupted != 0) {
if (_thread_run->interrupted != 0)
mutex_queue_remove(*mutex, _thread_run);
}
/* Unlock the mutex structure: */
_SPINUNLOCK(&(*mutex)->lock);
@ -647,7 +646,7 @@ _mutex_cv_unlock(pthread_mutex_t * mutex)
int
_mutex_cv_lock(pthread_mutex_t * mutex)
{
int ret;
int ret;
if ((ret = pthread_mutex_lock(mutex)) == 0)
(*mutex)->m_refcount--;
return (ret);
@ -656,7 +655,7 @@ _mutex_cv_lock(pthread_mutex_t * mutex)
static inline int
mutex_self_trylock(pthread_mutex_t mutex)
{
int ret = 0;
int ret = 0;
switch (mutex->m_type) {
@ -723,7 +722,7 @@ mutex_self_lock(pthread_mutex_t mutex)
static inline int
mutex_unlock_common(pthread_mutex_t * mutex, int add_reference)
{
int ret = 0;
int ret = 0;
if (mutex == NULL || *mutex == NULL) {
ret = EINVAL;
@ -1369,6 +1368,38 @@ _mutex_unlock_private(pthread_t pthread)
}
}
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();
if (pthread->state == PS_MUTEX_WAIT) {
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: */
mutex->m_owner->data.mutex = NULL;
/* Unlock the mutex structure: */
_SPINUNLOCK(&mutex->lock);
}
/*
* Undefer and handle pending signals, yielding if
* necessary:
*/
_thread_kern_sig_undefer();
}
/*
* Dequeue a waiting thread from the head of a mutex queue in descending
* priority order.
@ -1379,7 +1410,7 @@ mutex_queue_deq(pthread_mutex_t mutex)
pthread_t pthread;
while ((pthread = TAILQ_FIRST(&mutex->m_queue)) != NULL) {
TAILQ_REMOVE(&mutex->m_queue, pthread, qe);
TAILQ_REMOVE(&mutex->m_queue, pthread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_MUTEXQ;
/*
@ -1400,7 +1431,7 @@ static inline void
mutex_queue_remove(pthread_mutex_t mutex, pthread_t pthread)
{
if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0) {
TAILQ_REMOVE(&mutex->m_queue, pthread, qe);
TAILQ_REMOVE(&mutex->m_queue, pthread, sqe);
pthread->flags &= ~PTHREAD_FLAGS_IN_MUTEXQ;
}
}
@ -1413,18 +1444,19 @@ mutex_queue_enq(pthread_mutex_t mutex, pthread_t pthread)
{
pthread_t tid = TAILQ_LAST(&mutex->m_queue, mutex_head);
PTHREAD_ASSERT_NOT_IN_SYNCQ(pthread);
/*
* 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))
TAILQ_INSERT_TAIL(&mutex->m_queue, pthread, qe);
TAILQ_INSERT_TAIL(&mutex->m_queue, pthread, sqe);
else {
tid = TAILQ_FIRST(&mutex->m_queue);
while (pthread->active_priority <= tid->active_priority)
tid = TAILQ_NEXT(tid, qe);
TAILQ_INSERT_BEFORE(tid, pthread, qe);
tid = TAILQ_NEXT(tid, sqe);
TAILQ_INSERT_BEFORE(tid, pthread, sqe);
}
pthread->flags |= PTHREAD_FLAGS_IN_MUTEXQ;
}

View file

@ -66,9 +66,13 @@ static int _pq_active = 0;
PANIC(msg); \
} while (0)
#define _PQ_ASSERT_NOT_QUEUED(thrd, msg) do { \
if ((thrd)->flags & _PQ_IN_SCHEDQ) \
if (((thrd)->flags & _PQ_IN_SCHEDQ) != 0) \
PANIC(msg); \
} while (0)
#define _PQ_ASSERT_PROTECTED(msg) \
PTHREAD_ASSERT((_thread_kern_in_sched != 0) || \
(_thread_run->sig_defer_count > 0) || \
(_sig_in_handler != 0), msg);
#else
@ -79,11 +83,10 @@ static int _pq_active = 0;
#define _PQ_ASSERT_IN_WAITQ(thrd, msg)
#define _PQ_ASSERT_IN_PRIOQ(thrd, msg)
#define _PQ_ASSERT_NOT_QUEUED(thrd, msg)
#define _PQ_CHECK_PRIO()
#define _PQ_ASSERT_PROTECTED(msg)
#endif
int
_pq_alloc(pq_queue_t *pq, int minprio, int maxprio)
{
@ -101,9 +104,7 @@ _pq_alloc(pq_queue_t *pq, int minprio, int maxprio)
else {
/* Remember the queue size: */
pq->pq_size = prioslots;
ret = _pq_init(pq);
}
return (ret);
}
@ -142,6 +143,7 @@ _pq_remove(pq_queue_t *pq, pthread_t pthread)
_PQ_ASSERT_INACTIVE("_pq_remove: pq_active");
_PQ_SET_ACTIVE();
_PQ_ASSERT_IN_PRIOQ(pthread, "_pq_remove: Not in priority queue");
_PQ_ASSERT_PROTECTED("_pq_remove: prioq not protected!");
/*
* Remove this thread from priority list. Note that if
@ -172,6 +174,7 @@ _pq_insert_head(pq_queue_t *pq, pthread_t pthread)
_PQ_SET_ACTIVE();
_PQ_ASSERT_NOT_QUEUED(pthread,
"_pq_insert_head: Already in priority queue");
_PQ_ASSERT_PROTECTED("_pq_insert_head: prioq not protected!");
TAILQ_INSERT_HEAD(&pq->pq_lists[prio].pl_head, pthread, pqe);
if (pq->pq_lists[prio].pl_queued == 0)
@ -197,6 +200,7 @@ _pq_insert_tail(pq_queue_t *pq, pthread_t pthread)
_PQ_SET_ACTIVE();
_PQ_ASSERT_NOT_QUEUED(pthread,
"_pq_insert_tail: Already in priority queue");
_PQ_ASSERT_PROTECTED("_pq_insert_tail: prioq not protected!");
TAILQ_INSERT_TAIL(&pq->pq_lists[prio].pl_head, pthread, pqe);
if (pq->pq_lists[prio].pl_queued == 0)
@ -221,6 +225,7 @@ _pq_first(pq_queue_t *pq)
*/
_PQ_ASSERT_INACTIVE("_pq_first: pq_active");
_PQ_SET_ACTIVE();
_PQ_ASSERT_PROTECTED("_pq_first: prioq not protected!");
while (((pql = TAILQ_FIRST(&pq->pq_queue)) != NULL) &&
(pthread == NULL)) {
@ -250,6 +255,7 @@ pq_insert_prio_list(pq_queue_t *pq, int prio)
* Make some assertions when debugging is enabled:
*/
_PQ_ASSERT_ACTIVE("pq_insert_prio_list: pq_active");
_PQ_ASSERT_PROTECTED("_pq_insert_prio_list: prioq not protected!");
/*
* The priority queue is in descending priority order. Start at
@ -270,11 +276,10 @@ pq_insert_prio_list(pq_queue_t *pq, int prio)
pq->pq_lists[prio].pl_queued = 1;
}
#if defined(_PTHREADS_INVARIANTS)
void
_waitq_insert(pthread_t pthread)
{
pthread_t tid;
pthread_t tid;
/*
* Make some assertions when debugging is enabled:
@ -332,4 +337,3 @@ _waitq_clearactive(void)
_PQ_CLEAR_ACTIVE();
}
#endif
#endif

View file

@ -51,6 +51,7 @@
*/
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/time.h>
@ -59,14 +60,68 @@
#include <spinlock.h>
#include <pthread_np.h>
/*
* Define machine dependent macros to get and set the stack pointer
* from the supported contexts. Also define a macro to set the return
* address in a jmp_buf context.
*
* XXX - These need to be moved into architecture dependent support files.
*/
#if defined(__i386__)
#define GET_STACK_JB(jb) ((unsigned long)((jb)[0]._jb[2]))
#define GET_STACK_SJB(sjb) ((unsigned long)((sjb)[0]._sjb[2]))
#define GET_STACK_UC(ucp) ((unsigned long)((ucp)->uc_mcontext.mc_esp))
#define SET_STACK_JB(jb, stk) (jb)[0]._jb[2] = (int)(stk)
#define SET_STACK_SJB(sjb, stk) (sjb)[0]._sjb[2] = (int)(stk)
#define SET_STACK_UC(ucp, stk) (ucp)->uc_mcontext.mc_esp = (int)(stk)
#define FP_SAVE_UC(ucp) do { \
char *fdata; \
fdata = (char *) (ucp)->uc_mcontext.mc_fpregs; \
__asm__("fnsave %0": :"m"(*fdata)); \
} while (0)
#define FP_RESTORE_UC(ucp) do { \
char *fdata; \
fdata = (char *) (ucp)->uc_mcontext.mc_fpregs; \
__asm__("frstor %0": :"m"(*fdata)); \
} while (0)
#define SET_RETURN_ADDR_JB(jb, ra) (jb)[0]._jb[0] = (int)(ra)
#elif defined(__alpha__)
#include <machine/reg.h>
#define GET_STACK_JB(jb, stk) ((unsigned long)((jb)[0]._jb[R_SP + 4]))
#define GET_STACK_SJB(sjb, stk) ((unsigned long)((sjb)[0]._sjb[R_SP + 4]))
#define GET_STACK_UC(ucp, stk) ((ucp)->uc_mcontext.mc_regs[R_SP])
#define SET_STACK_JB(jb, stk) (jb)[0]._jb[R_SP + 4] = (long)(stk)
#define SET_STACK_SJB(sjb, stk) (sjb)[0]._sjb[R_SP + 4] = (long)(stk)
#define SET_STACK_UC(ucp, stk) (ucp)->uc_mcontext.mc_regs[R_SP] = (unsigned long)(stk)
#define FP_SAVE_UC(ucp)
#define FP_RESTORE_UC(ucp)
#define SET_RETURN_ADDR_JB(jb, ra) do { \
(jb)[0]._jb[2] = (long)(ra); \
(jb)[0]._jb[R_RA + 4] = 0; \
(jb)[0]._jb[R_T12 + 4] = (long)(ra); \
} while (0)
#else
#error "Don't recognize this architecture!"
#endif
/*
* Kernel fatal error handler macro.
*/
#define PANIC(string) _thread_exit(__FILE__,__LINE__,string)
/* Output debug messages like this: */
#define stdout_debug(_x) _thread_sys_write(1,_x,strlen(_x));
#define stderr_debug(_x) _thread_sys_write(2,_x,strlen(_x));
#define stdout_debug(args...) do { \
char buf[128]; \
snprintf(buf, sizeof(buf), ##args); \
_thread_sys_write(1, buf, strlen(buf)); \
} while (0)
#define stderr_debug(args...) do { \
char buf[128]; \
snprintf(buf, sizeof(buf), ##args); \
_thread_sys_write(2, buf, strlen(buf)); \
} while (0)
/*
@ -80,34 +135,13 @@
/*
* Waiting queue manipulation macros (using pqe link):
*/
#if defined(_PTHREADS_INVARIANTS)
#define PTHREAD_WAITQ_REMOVE(thrd) _waitq_remove(thrd)
#define PTHREAD_WAITQ_INSERT(thrd) _waitq_insert(thrd)
#if defined(_PTHREADS_INVARIANTS)
#define PTHREAD_WAITQ_CLEARACTIVE() _waitq_clearactive()
#define PTHREAD_WAITQ_SETACTIVE() _waitq_setactive()
#else
#define PTHREAD_WAITQ_REMOVE(thrd) do { \
TAILQ_REMOVE(&_waitingq,thrd,pqe); \
(thrd)->flags &= ~PTHREAD_FLAGS_IN_WAITQ; \
} while (0)
#define PTHREAD_WAITQ_INSERT(thrd) do { \
if ((thrd)->wakeup_time.tv_sec == -1) \
TAILQ_INSERT_TAIL(&_waitingq,thrd,pqe); \
else { \
pthread_t tid = TAILQ_FIRST(&_waitingq); \
while ((tid != NULL) && (tid->wakeup_time.tv_sec != -1) && \
((tid->wakeup_time.tv_sec < (thrd)->wakeup_time.tv_sec) || \
((tid->wakeup_time.tv_sec == (thrd)->wakeup_time.tv_sec) && \
(tid->wakeup_time.tv_nsec <= (thrd)->wakeup_time.tv_nsec)))) \
tid = TAILQ_NEXT(tid, pqe); \
if (tid == NULL) \
TAILQ_INSERT_TAIL(&_waitingq,thrd,pqe); \
else \
TAILQ_INSERT_BEFORE(tid,thrd,pqe); \
} \
(thrd)->flags |= PTHREAD_FLAGS_IN_WAITQ; \
} while (0)
#define PTHREAD_WAITQ_CLEARACTIVE()
#define PTHREAD_WAITQ_SETACTIVE()
#endif
@ -139,6 +173,14 @@
* called with preemption deferred (see thread_kern_sched_[un]defer).
*/
#if defined(_PTHREADS_INVARIANTS)
#include <assert.h>
#define PTHREAD_ASSERT(cond, msg) do { \
if (!(cond)) \
PANIC(msg); \
} while (0)
#define PTHREAD_ASSERT_NOT_IN_SYNCQ(thrd) \
PTHREAD_ASSERT((((thrd)->flags & PTHREAD_FLAGS_IN_SYNCQ) == 0), \
"Illegal call from signal handler");
#define PTHREAD_NEW_STATE(thrd, newstate) do { \
if (_thread_kern_new_state != 0) \
PANIC("Recursive PTHREAD_NEW_STATE"); \
@ -156,6 +198,8 @@
PTHREAD_SET_STATE(thrd, newstate); \
} while (0)
#else
#define PTHREAD_ASSERT(cond, msg)
#define PTHREAD_ASSERT_NOT_IN_SYNCQ(thrd)
#define PTHREAD_NEW_STATE(thrd, newstate) do { \
if ((thrd)->state != newstate) { \
if ((thrd)->state == PS_RUNNING) { \
@ -379,21 +423,52 @@ enum pthread_susp {
* almost entirely on this stack.
*/
#define PTHREAD_STACK_INITIAL 0x100000
/* Address immediately beyond the beginning of the initial thread stack. */
#define PTHREAD_DEFAULT_PRIORITY 64
#define PTHREAD_MAX_PRIORITY 126
#define PTHREAD_MIN_PRIORITY 0
#define _POSIX_THREAD_ATTR_STACKSIZE
/*
* Clock resolution in nanoseconds.
* Define the different priority ranges. All applications have thread
* priorities constrained within 0-31. The threads library raises the
* priority when delivering signals in order to ensure that signal
* delivery happens (from the POSIX spec) "as soon as possible".
* In the future, the threads library will also be able to map specific
* threads into real-time (cooperating) processes or kernel threads.
* The RT and SIGNAL priorities will be used internally and added to
* thread base priorities so that the scheduling queue can handle both
* normal and RT priority threads with and without signal handling.
*
* The approach taken is that, within each class, signal delivery
* always has priority over thread execution.
*/
#define CLOCK_RES_NSEC 10000000
#define PTHREAD_DEFAULT_PRIORITY 15
#define PTHREAD_MIN_PRIORITY 0
#define PTHREAD_MAX_PRIORITY 31 /* 0x1F */
#define PTHREAD_SIGNAL_PRIORITY 32 /* 0x20 */
#define PTHREAD_RT_PRIORITY 64 /* 0x40 */
#define PTHREAD_FIRST_PRIORITY PTHREAD_MIN_PRIORITY
#define PTHREAD_LAST_PRIORITY \
(PTHREAD_MAX_PRIORITY + PTHREAD_SIGNAL_PRIORITY + PTHREAD_RT_PRIORITY)
#define PTHREAD_BASE_PRIORITY(prio) ((prio) & PTHREAD_MAX_PRIORITY)
/*
* Clock resolution in microseconds.
*/
#define CLOCK_RES_USEC 10000
/*
* Time slice period in microseconds.
*/
#define TIMESLICE_USEC 100000
#define TIMESLICE_USEC 20000
/*
* Define a thread-safe macro to get the current time of day
* which is updated at regular intervals by the scheduling signal
* handler.
*/
#define GET_CURRENT_TOD(tv) \
do { \
tv.tv_sec = _sched_tod.tv_sec; \
tv.tv_usec = _sched_tod.tv_usec; \
} while (tv.tv_sec != _sched_tod.tv_sec)
struct pthread_key {
spinlock_t lock;
@ -487,8 +562,10 @@ union pthread_wait_data {
short branch; /* Line number, for debugging. */
char *fname; /* Source file name for debugging.*/
} fd;
struct pthread_poll_data * poll_data;
FILE *fp;
struct pthread_poll_data *poll_data;
spinlock_t *spinlock;
struct pthread *thread;
};
/*
@ -497,6 +574,83 @@ union pthread_wait_data {
*/
typedef void (*thread_continuation_t) (void *);
struct pthread_state_data {
int psd_interrupted;
sigset_t psd_sigmask;
enum pthread_state psd_state;
int psd_flags;
struct timespec psd_wakeup_time;
union pthread_wait_data psd_wait_data;
/* XXX - What about thread->timeout and/or thread->error? */
};
/*
* Normally thread contexts are stored as jmp_bufs via _setjmp()/_longjmp(),
* but they may also be sigjmp_buf and ucontext_t. When a thread is
* interrupted by a signal, it's context is saved as a ucontext_t. An
* application is also free to use [_]longjmp()/[_]siglongjmp() to jump
* between contexts within the same thread. Future support will also
* include setcontext()/getcontext().
*
* Define an enumerated type that can identify the 4 different context
* types.
*/
typedef enum {
CTX_JB_NOSIG, /* context is jmp_buf without saved sigset */
CTX_JB, /* context is jmp_buf (with saved sigset) */
CTX_SJB, /* context is sigjmp_buf (with saved sigset) */
CTX_UC /* context is ucontext_t (with saved sigset) */
} thread_context_t;
/*
* There are 2 basic contexts that a frame may contain at any
* one time:
*
* o ctx - The context that the thread should return to after normal
* completion of the signal handler.
* o sig_jb - The context just before the signal handler is invoked.
* Attempts at abnormal returns from user supplied signal handlers
* will return back to the signal context to perform any necessary
* cleanup.
*/
struct pthread_signal_frame {
/*
* This stores the threads state before the signal.
*/
struct pthread_state_data saved_state;
/* Beginning (bottom) of threads stack frame for this signal. */
unsigned long stackp;
/*
* Threads return context; ctxtype identifies the type of context.
* For signal frame 0, these point to the context storage area
* within the pthread structure. When handling signals (frame > 0),
* these point to a context storage area that is allocated off the
* threads stack.
*/
union {
jmp_buf jb;
sigjmp_buf sigjb;
ucontext_t uc;
} ctx;
thread_context_t ctxtype;
int longjmp_val;
/* Threads "jump out of signal handler" destination frame. */
int dst_frame;
/*
* Used to return back to the signal handling frame in case
* the application tries to change contexts from the handler.
*/
jmp_buf *sig_jb;
int signo; /* signal, arg 1 to sighandler */
int sig_has_args; /* use signal args if true */
};
/*
* Thread structure.
*/
@ -530,54 +684,19 @@ struct pthread {
void *stack;
struct pthread_attr attr;
#if (defined(__FreeBSD__) || defined(__NetBSD__)) && defined(__i386__)
/*
* Saved floating point registers on systems where they are not
* saved in the signal context.
*/
char saved_fp[108];
#endif
/*
* Saved signal context used in call to sigreturn by
* _thread_kern_sched if sig_saved is TRUE.
*/
ucontext_t saved_sigcontext;
/*
* Saved jump buffer used in call to longjmp by _thread_kern_sched
* if sig_saved is FALSE.
*/
jmp_buf saved_jmp_buf;
jmp_buf *sighandler_jmp_buf;
/*
* Saved jump buffers for use when doing nested [sig|_]longjmp()s, as
* when doing signal delivery.
*/
union {
jmp_buf jmp;
sigjmp_buf sigjmp;
} nested_jmp;
int longjmp_val;
#define JMPFLAGS_NONE 0x00
#define JMPFLAGS_LONGJMP 0x01
#define JMPFLAGS__LONGJMP 0x02
#define JMPFLAGS_SIGLONGJMP 0x04
#define JMPFLAGS_DEFERRED 0x08
int jmpflags;
/*
* TRUE if the last state saved was a signal context. FALSE if the
* last state saved was a jump buffer.
*/
int sig_saved;
/*
* Used for tracking delivery of nested signal handlers.
* Signal frame 0 is used for normal context (when no
* signal handlers are active for the thread). Frame
* 1 is used as the context for the first signal, and
* frames 2 .. NSIG-1 are used when additional signals
* arrive interrupting already active signal handlers.
*/
int signal_nest_level;
struct pthread_signal_frame *sigframes[NSIG];
struct pthread_signal_frame sigframe0;
struct pthread_signal_frame *curframe;
int sigframe_count;
int sigframe_done;
/*
* Cancelability flags - the lower 2 bits are used by cancel
@ -588,7 +707,7 @@ struct pthread {
#define PTHREAD_CANCEL_NEEDED 0x0010
int cancelflags;
enum pthread_susp suspended;
enum pthread_susp suspended;
thread_continuation_t continuation;
@ -597,16 +716,16 @@ struct pthread {
*/
sigset_t sigmask;
sigset_t sigpend;
int check_pending;
/* Thread state: */
enum pthread_state state;
enum pthread_state oldstate;
/* Time that this thread was last made active. */
struct timeval last_active;
/* Scheduling clock when this thread was last made active. */
long last_active;
/* Time that this thread was last made inactive. */
struct timeval last_inactive;
/* Scheduling clock when this thread was last made inactive. */
long last_inactive;
/*
* Number of microseconds accumulated by this thread when
@ -614,12 +733,6 @@ struct pthread {
*/
long slice_usec;
/*
* Incremental priority accumulated by thread while it is ready to
* run but is denied being run.
*/
int inc_prio;
/*
* Time to wake up thread. This is used for sleeping threads and
* for any operation which may time out (such as select).
@ -640,8 +753,7 @@ struct pthread {
/*
* The current thread can belong to only one scheduling queue at
* a time (ready or waiting queue). It can also belong to (only)
* one of:
* a time (ready or waiting queue). It can also belong to:
*
* o A queue of threads waiting for a mutex
* o A queue of threads waiting for a condition variable
@ -651,15 +763,21 @@ struct pthread {
* o A queue of threads needing work done by the kernel thread
* (waiting for a spinlock or file I/O)
*
* It is possible for a thread to belong to more than one of the
* above queues if it is handling a signal. A thread may only
* enter a mutex, condition variable, or join queue when it is
* not being called from a signal handler. If a thread is a
* member of one of these queues when a signal handler is invoked,
* it must remain in the queue. For this reason, the links for
* these queues must not be (re)used for other queues.
*
* Use pqe for the scheduling queue link (both ready and waiting),
* and qe for other links.
* sqe for synchronization (mutex, condition variable, and join)
* queue links, and qe for all other links.
*/
/* Priority queue entry for this thread: */
TAILQ_ENTRY(pthread) pqe;
/* Queue entry for this thread: */
TAILQ_ENTRY(pthread) qe;
TAILQ_ENTRY(pthread) pqe; /* priority queue link */
TAILQ_ENTRY(pthread) sqe; /* synchronization queue link */
TAILQ_ENTRY(pthread) qe; /* all other queues link */
/* Wait data. */
union pthread_wait_data data;
@ -694,14 +812,17 @@ struct pthread {
int flags;
#define PTHREAD_FLAGS_PRIVATE 0x0001
#define PTHREAD_EXITING 0x0002
#define PTHREAD_FLAGS_IN_CONDQ 0x0004 /* in condition queue using qe link*/
#define PTHREAD_FLAGS_IN_WORKQ 0x0008 /* in work queue using qe link */
#define PTHREAD_FLAGS_IN_WAITQ 0x0010 /* in waiting queue using pqe link */
#define PTHREAD_FLAGS_IN_PRIOQ 0x0020 /* in priority queue using pqe link */
#define PTHREAD_FLAGS_IN_MUTEXQ 0x0040 /* in mutex queue using qe link */
#define PTHREAD_FLAGS_IN_FILEQ 0x0080 /* in file lock queue using qe link */
#define PTHREAD_FLAGS_IN_FDQ 0x0100 /* in fd lock queue using qe link */
#define PTHREAD_FLAGS_TRACE 0x0200 /* for debugging purposes */
#define PTHREAD_FLAGS_IN_WAITQ 0x0004 /* in waiting queue using pqe link */
#define PTHREAD_FLAGS_IN_PRIOQ 0x0008 /* in priority queue using pqe link */
#define PTHREAD_FLAGS_IN_WORKQ 0x0010 /* in work queue using qe link */
#define PTHREAD_FLAGS_IN_FILEQ 0x0020 /* in file lock queue using qe link */
#define PTHREAD_FLAGS_IN_FDQ 0x0040 /* in fd lock queue using qe link */
#define PTHREAD_FLAGS_IN_CONDQ 0x0080 /* in condition queue using sqe link*/
#define PTHREAD_FLAGS_IN_MUTEXQ 0x0100 /* in mutex queue using sqe link */
#define PTHREAD_FLAGS_IN_JOINQ 0x0200 /* in join queue using sqe link */
#define PTHREAD_FLAGS_TRACE 0x0400 /* for debugging purposes */
#define PTHREAD_FLAGS_IN_SYNCQ \
(PTHREAD_FLAGS_IN_CONDQ | PTHREAD_FLAGS_IN_MUTEXQ | PTHREAD_FLAGS_IN_JOINQ)
/*
* Base priority is the user setable and retrievable priority
@ -820,14 +941,31 @@ SCLASS int _thread_kern_in_sched
;
#endif
/* Last time that an incremental priority update was performed: */
SCLASS struct timeval kern_inc_prio_time
SCLASS int _sig_in_handler
#ifdef GLOBAL_PTHREAD_PRIVATE
= 0;
#else
;
#endif
/* Time of day at last scheduling timer signal: */
SCLASS struct timeval volatile _sched_tod
#ifdef GLOBAL_PTHREAD_PRIVATE
= { 0, 0 };
#else
;
#endif
/*
* Current scheduling timer ticks; used as resource usage.
*/
SCLASS unsigned int volatile _sched_ticks
#ifdef GLOBAL_PTHREAD_PRIVATE
= 0;
#else
;
#endif
/* Dead threads: */
SCLASS TAILQ_HEAD(, pthread) _dead_list
#ifdef GLOBAL_PTHREAD_PRIVATE
@ -905,9 +1043,9 @@ SCLASS int _thread_dtablesize /* Descriptor table size. */
;
#endif
SCLASS int _clock_res_nsec /* Clock resolution in nsec. */
SCLASS int _clock_res_usec /* Clock resolution in usec. */
#ifdef GLOBAL_PTHREAD_PRIVATE
= CLOCK_RES_NSEC;
= CLOCK_RES_USEC;
#else
;
#endif
@ -937,9 +1075,10 @@ SCLASS struct sigaction _thread_sigact[NSIG];
SCLASS int _thread_dfl_count[NSIG];
/*
* Pending signals for this process.
* Pending signals and mask for this process:
*/
SCLASS sigset_t _process_sigpending;
SCLASS sigset_t _process_sigmask;
/*
* Scheduling queues:
@ -959,6 +1098,21 @@ SCLASS volatile int _spinblock_count
#endif
;
/* Used to maintain pending and active signals: */
struct sigstatus {
int pending; /* Is this a pending signal? */
int blocked; /*
* A handler is currently active for
* this signal; ignore subsequent
* signals until the handler is done.
*/
int signo; /* arg 1 to signal handler */
siginfo_t siginfo; /* arg 2 to signal handler */
ucontext_t uc; /* arg 3 to signal handler */
};
SCLASS struct sigstatus _thread_sigq[NSIG];
/* Indicates that the signal queue needs to be checked. */
SCLASS volatile int _sigq_check_reqd
#ifdef GLOBAL_PTHREAD_PRIVATE
@ -998,6 +1152,18 @@ SCLASS void * _next_stack
#endif
;
/*
* Declare the kernel scheduler jump buffer and stack:
*/
SCLASS jmp_buf _thread_kern_sched_jb;
SCLASS void * _thread_kern_sched_stack
#ifdef GLOBAL_PTHREAD_PRIVATE
= NULL
#endif
;
/* Used for _PTHREADS_INVARIANTS checking. */
SCLASS int _thread_kern_new_state
#ifdef GLOBAL_PTHREAD_PRIVATE
@ -1025,15 +1191,19 @@ __BEGIN_DECLS
char *__ttyname_basic(int);
char *__ttyname_r_basic(int, char *, size_t);
char *ttyname_r(int, char *, size_t);
void _cond_wait_backout(pthread_t);
void _fd_lock_backout(pthread_t);
int _find_dead_thread(pthread_t);
int _find_thread(pthread_t);
void _flockfile_backout(pthread_t);
void _funlock_owned(pthread_t);
void _join_backout(pthread_t);
int _thread_create(pthread_t *,const pthread_attr_t *,void *(*start_routine)(void *),void *,pthread_t);
int _thread_fd_lock(int, int, struct timespec *);
int _thread_fd_lock_debug(int, int, struct timespec *,char *fname,int lineno);
void _dispatch_signals(void);
int _mutex_cv_lock(pthread_mutex_t *);
int _mutex_cv_unlock(pthread_mutex_t *);
void _mutex_lock_backout(pthread_t);
void _mutex_notify_priochange(pthread_t);
int _mutex_reinit(pthread_mutex_t *);
void _mutex_unlock_private(pthread_t);
@ -1044,14 +1214,15 @@ void _pq_remove(struct pq_queue *pq, struct pthread *);
void _pq_insert_head(struct pq_queue *pq, struct pthread *);
void _pq_insert_tail(struct pq_queue *pq, struct pthread *);
struct pthread *_pq_first(struct pq_queue *pq);
#if defined(_PTHREADS_INVARIANTS)
void _waitq_insert(pthread_t pthread);
void _waitq_remove(pthread_t pthread);
#if defined(_PTHREADS_INVARIANTS)
void _waitq_setactive(void);
void _waitq_clearactive(void);
#endif
void _thread_exit(char *, int, char *);
void _thread_exit_cleanup(void);
void _thread_exit_finish(void);
void _thread_fd_unlock(int, int);
void _thread_fd_unlock_debug(int, int, char *, int);
void _thread_fd_unlock_owned(pthread_t);
@ -1060,20 +1231,23 @@ void _thread_cleanupspecific(void);
void _thread_dump_info(void);
void _thread_init(void);
void _thread_kern_sched(ucontext_t *);
void _thread_kern_sched_state(enum pthread_state,char *fname,int lineno);
void _thread_kern_scheduler(void);
void _thread_kern_sched_frame(int frame);
void _thread_kern_sched_sig(void);
void _thread_kern_sched_state(enum pthread_state, char *fname, int lineno);
void _thread_kern_sched_state_unlock(enum pthread_state state,
spinlock_t *lock, char *fname, int lineno);
void _thread_kern_set_timeout(const struct timespec *);
void _thread_kern_sig_defer(void);
void _thread_kern_sig_undefer(void);
void _thread_sig_handler(int, int, ucontext_t *);
pthread_t _thread_sig_handle(int, ucontext_t *);
void _thread_sig_init(void);
void _thread_sig_handler(int, siginfo_t *, ucontext_t *);
void _thread_sig_check_pending(pthread_t pthread);
void _thread_sig_handle_pending(void);
void _thread_sig_send(pthread_t pthread, int sig);
void _thread_sig_deliver(pthread_t pthread, int sig);
void _thread_sig_wrapper(void);
int _thread_sigframe_find(pthread_t pthread, void *stackp);
void _thread_start(void);
void _thread_start_sig_handler(void);
void _thread_seterrno(pthread_t,int);
void _thread_seterrno(pthread_t, int);
int _thread_fd_table_init(int fd);
pthread_addr_t _thread_gc(pthread_addr_t);
void _thread_enter_cancellation_point(void);

View file

@ -29,6 +29,7 @@
* $FreeBSD$
*/
#include <stdlib.h>
#include <errno.h>
#ifdef _THREAD_SAFE
#include <semaphore.h>

View file

@ -59,7 +59,8 @@ pthread_setschedparam(pthread_t pthread, int policy,
*/
_thread_kern_sig_defer();
if (param->sched_priority != pthread->base_priority) {
if (param->sched_priority !=
PTHREAD_BASE_PRIORITY(pthread->base_priority)) {
/*
* Remove the thread from its current priority
* queue before any adjustments are made to its
@ -72,6 +73,8 @@ pthread_setschedparam(pthread_t pthread, int policy,
}
/* Set the thread base priority: */
pthread->base_priority &=
(PTHREAD_SIGNAL_PRIORITY | PTHREAD_RT_PRIORITY);
pthread->base_priority = param->sched_priority;
/* Recalculate the active priority: */

File diff suppressed because it is too large Load diff

View file

@ -74,12 +74,13 @@ _sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
*/
if (act != NULL && sig != _SCHED_SIGNAL && sig != SIGCHLD &&
sig != SIGINFO) {
/* Initialise the global signal action structure: */
gact.sa_mask = act->sa_mask;
gact.sa_flags = 0;
/* Ensure the scheduling signal is masked: */
sigaddset(&gact.sa_mask, _SCHED_SIGNAL);
/*
* Ensure the signal handler cannot be interrupted
* by other signals. Always request the POSIX signal
* handler arguments.
*/
sigfillset(&gact.sa_mask);
gact.sa_flags = SA_SIGINFO;
/*
* Check if the signal handler is being set to

View file

@ -43,7 +43,8 @@
int
pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
{
int ret = 0;
sigset_t sigset;
int ret = 0;
/* Check if the existing signal process mask is to be returned: */
if (oset != NULL) {
@ -81,10 +82,18 @@ pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
}
/*
* Dispatch signals to the running thread that are pending
* and now unblocked:
* Check if there are pending signals for the running
* thread or process that aren't blocked:
*/
_dispatch_signals();
sigset = _thread_run->sigpend;
SIGSETOR(sigset, _process_sigpending);
SIGSETNAND(sigset, _thread_run->sigmask);
if (SIGNOTEMPTY(sigset))
/*
* Call the kernel scheduler which will safely
* install a signal frame for the running thread:
*/
_thread_kern_sched_sig();
}
/* Return the completion status: */

View file

@ -41,53 +41,9 @@
#include "pthread_private.h"
int
_sigprocmask(int how, const sigset_t * set, sigset_t * oset)
_sigprocmask(int how, const sigset_t *set, sigset_t *oset)
{
int ret = 0;
/* Check if the existing signal process mask is to be returned: */
if (oset != NULL) {
/* Return the current mask: */
*oset = _thread_run->sigmask;
}
/* Check if a new signal set was provided by the caller: */
if (set != NULL) {
/* Process according to what to do: */
switch (how) {
/* Block signals: */
case SIG_BLOCK:
/* Add signals to the existing mask: */
SIGSETOR(_thread_run->sigmask, *set);
break;
/* Unblock signals: */
case SIG_UNBLOCK:
/* Clear signals from the existing mask: */
SIGSETNAND(_thread_run->sigmask, *set);
break;
/* Set the signal process mask: */
case SIG_SETMASK:
/* Set the new mask: */
_thread_run->sigmask = *set;
break;
/* Trap invalid actions: */
default:
/* Return an invalid argument: */
errno = EINVAL;
ret = -1;
break;
}
/*
* Dispatch signals to the running thread that are pending
* and now unblocked:
*/
_dispatch_signals();
}
/* Return the completion status: */
return (ret);
return (pthread_sigmask(how, set, oset));
}
__strong_reference(_sigprocmask, sigprocmask);

View file

@ -40,7 +40,7 @@
#include "pthread_private.h"
int
sigwait(const sigset_t * set, int *sig)
sigwait(const sigset_t *set, int *sig)
{
int ret = 0;
int i;
@ -52,11 +52,9 @@ sigwait(const sigset_t * set, int *sig)
* Specify the thread kernel signal handler.
*/
act.sa_handler = (void (*) ()) _thread_sig_handler;
act.sa_flags = SA_RESTART;
act.sa_mask = *set;
/* Ensure the scheduling signal is masked: */
sigaddset(&act.sa_mask, _SCHED_SIGNAL);
act.sa_flags = SA_RESTART | SA_SIGINFO;
/* Ensure the signal handler cannot be interrupted by other signals: */
sigfillset(&act.sa_mask);
/*
* Initialize the set of signals that will be waited on:

View file

@ -127,7 +127,7 @@ _write(int fd, const void *buf, size_t nbytes)
/* Return the number of bytes written: */
ret = num;
}
_FD_UNLOCK(fd, FD_RDWR);
_FD_UNLOCK(fd, FD_WRITE);
}
return (ret);
}

View file

@ -57,8 +57,5 @@ pthread_yield(void)
/* Schedule the next thread: */
_thread_kern_sched(NULL);
/* Nothing to return. */
return;
}
#endif