[ The author's description... ]

o Runnable threads are now maintained in priority queues.  The
    implementation requires two things:

      1.) The priority queues must be protected during insertion
          and removal of threads.  Since the kernel scheduler
          must modify the priority queues, a spinlock for
          protection cannot be used.   The functions
          _thread_kern_sched_defer() and _thread_kern_sched_undefer()
          were added to {un}defer kernel scheduler activation.

      2.) A thread (active) priority change can be performed only
          when the thread is removed from the priority queue.  The
          implementation uses a threads active priority when
          inserting it into the queue.

    A by-product is that thread switches are much faster.  A
    separate queue is used for waiting and/or blocked threads,
    and it is searched at most 2 times in the kernel scheduler
    when there are active threads.  It should be possible to
    reduce this to once by combining polling of threads waiting
    on I/O with the loop that looks for timed out threads and
    the minimum timeout value.

  o Functions to defer kernel scheduler activation were added.  These
    are _thread_kern_sched_defer() and _thread_kern_sched_undefer()
    and may be called recursively.  These routines do not block the
    scheduling signal, but latch its occurrence.  The signal handler
    will not call the kernel scheduler when the running thread has
    deferred scheduling, but it will be called when running thread
    undefers scheduling.

  o Added support for _POSIX_THREAD_PRIORITY_SCHEDULING.  All the
    POSIX routines required by this should now be implemented.
    One note, SCHED_OTHER, SCHED_FIFO, and SCHED_RR are required
    to be defined by including pthread.h.  These defines are currently
    in sched.h.  I modified pthread.h to include sched.h but don't
    know if this is the proper thing to do.

  o Added support for priority protection and inheritence mutexes.
    This allows definition of _POSIX_THREAD_PRIO_PROTECT and
    _POSIX_THREAD_PRIO_INHERIT.

  o Added additional error checks required by POSIX for mutexes and
    condition variables.

  o Provided a wrapper for sigpending which is marked as a hidden
    syscall.

  o Added a non-portable function as a debugging aid to allow an
    application to monitor thread context switches.  An application
    can install a routine that gets called everytime a thread
    (explicitly created by the application) gets context switched.
    The routine gets passed the pthread IDs of the threads that are
    being switched in and out.

Submitted by: Dan Eischen <eischen@vigrid.com>

Changes by me:

  o Added a PS_SPINBLOCK state to deal with the priority inversion
    problem most often (I think) seen by threads calling malloc/free/realloc.

  o Dispatch signals to the running thread directly rather than at a
    context switch to avoid the situation where the switch never occurs.
This commit is contained in:
John Birrell 1999-03-23 05:07:56 +00:00
parent f5a7833449
commit 58a7cc5d1b
115 changed files with 9525 additions and 2039 deletions

View file

@ -1,4 +1,4 @@
# $Id: Makefile.inc,v 1.15 1998/09/12 22:03:20 dt Exp $
# $Id: Makefile.inc,v 1.16 1998/09/30 06:36:55 jb Exp $
# uthread sources
.PATH: ${.CURDIR}/uthread
@ -8,10 +8,18 @@ SRCS+= \
uthread_attr_destroy.c \
uthread_attr_init.c \
uthread_attr_getdetachstate.c \
uthread_attr_getinheritsched.c \
uthread_attr_getschedparam.c \
uthread_attr_getschedpolicy.c \
uthread_attr_getscope.c \
uthread_attr_getstackaddr.c \
uthread_attr_getstacksize.c \
uthread_attr_setcreatesuspend_np.c \
uthread_attr_setdetachstate.c \
uthread_attr_setinheritsched.c \
uthread_attr_setschedparam.c \
uthread_attr_setschedpolicy.c \
uthread_attr_setscope.c \
uthread_attr_setstackaddr.c \
uthread_attr_setstacksize.c \
uthread_autoinit.cc \
@ -44,6 +52,7 @@ SRCS+= \
uthread_getdirentries.c \
uthread_getpeername.c \
uthread_getprio.c \
uthread_getschedparam.c \
uthread_getsockname.c \
uthread_getsockopt.c \
uthread_info.c \
@ -57,11 +66,14 @@ SRCS+= \
uthread_mattr_kind_np.c \
uthread_multi_np.c \
uthread_mutex.c \
uthread_mutex_prioceiling.c \
uthread_mutex_protocol.c \
uthread_mutexattr_destroy.c \
uthread_nanosleep.c \
uthread_once.c \
uthread_open.c \
uthread_pipe.c \
uthread_priority_queue.c \
uthread_queue.c \
uthread_read.c \
uthread_readv.c \
@ -76,12 +88,14 @@ SRCS+= \
uthread_sendto.c \
uthread_seterrno.c \
uthread_setprio.c \
uthread_setschedparam.c \
uthread_setsockopt.c \
uthread_shutdown.c \
uthread_sig.c \
uthread_sigaction.c \
uthread_sigblock.c \
uthread_sigmask.c \
uthread_sigpending.c \
uthread_sigprocmask.c \
uthread_sigsetmask.c \
uthread_sigsuspend.c \
@ -92,6 +106,7 @@ SRCS+= \
uthread_spec.c \
uthread_spinlock.c \
uthread_suspend_np.c \
uthread_switch_np.c \
uthread_vfork.c \
uthread_wait4.c \
uthread_write.c \

View file

@ -55,6 +55,7 @@
#include <sys/time.h>
#include <sched.h>
#include <spinlock.h>
#include <pthread_np.h>
/*
* Kernel fatal error handler macro.
@ -65,15 +66,58 @@
#define stdout_debug(_x) _thread_sys_write(1,_x,strlen(_x));
#define stderr_debug(_x) _thread_sys_write(2,_x,strlen(_x));
/*
* State change macro:
* Priority queue manipulation macros:
*/
#define PTHREAD_NEW_STATE(thrd, newstate) { \
#define PTHREAD_PRIOQ_INSERT_HEAD(thrd) _pq_insert_head(&_readyq,thrd)
#define PTHREAD_PRIOQ_INSERT_TAIL(thrd) _pq_insert_tail(&_readyq,thrd)
#define PTHREAD_PRIOQ_REMOVE(thrd) _pq_remove(&_readyq,thrd)
#define PTHREAD_PRIOQ_FIRST _pq_first(&_readyq)
/*
* Waiting queue manipulation macros:
*/
#define PTHREAD_WAITQ_INSERT(thrd) TAILQ_INSERT_TAIL(&_waitingq,thrd,pqe)
#define PTHREAD_WAITQ_REMOVE(thrd) TAILQ_REMOVE(&_waitingq,thrd,pqe)
/*
* State change macro without scheduling queue change:
*/
#define PTHREAD_SET_STATE(thrd, newstate) { \
(thrd)->state = newstate; \
(thrd)->fname = __FILE__; \
(thrd)->lineno = __LINE__; \
}
/*
* State change macro with scheduling queue change - This must be
* called with preemption deferred (see thread_kern_sched_[un]defer).
*/
#define PTHREAD_NEW_STATE(thrd, newstate) { \
if ((thrd)->state != newstate) { \
if ((thrd)->state == PS_RUNNING) { \
PTHREAD_PRIOQ_REMOVE(thrd); \
PTHREAD_WAITQ_INSERT(thrd); \
} else if (newstate == PS_RUNNING) { \
PTHREAD_WAITQ_REMOVE(thrd); \
PTHREAD_PRIOQ_INSERT_TAIL(thrd); \
} \
} \
PTHREAD_SET_STATE(thrd, newstate); \
}
/*
* Define the signals to be used for scheduling.
*/
#if defined(_PTHREADS_COMPAT_SCHED)
#define _ITIMER_SCHED_TIMER ITIMER_VIRTUAL
#define _SCHED_SIGNAL SIGVTALRM
#else
#define _ITIMER_SCHED_TIMER ITIMER_PROF
#define _SCHED_SIGNAL SIGPROF
#endif
/*
* Queue definitions.
*/
@ -83,11 +127,35 @@ struct pthread_queue {
void *q_data;
};
/*
* Priority queues.
*
* XXX It'd be nice if these were contained in uthread_priority_queue.[ch].
*/
typedef struct pq_list {
TAILQ_HEAD(, pthread) pl_head; /* list of threads at this priority */
TAILQ_ENTRY(pq_list) pl_link; /* link for queue of priority lists */
int pl_prio; /* the priority of this list */
int pl_queued; /* is this in the priority queue */
} pq_list_t;
typedef struct pq_queue {
TAILQ_HEAD(, pq_list) pq_queue; /* queue of priority lists */
pq_list_t *pq_lists; /* array of all priority lists */
int pq_size; /* number of priority lists */
} pq_queue_t;
/*
* Static queue initialization values.
*/
#define PTHREAD_QUEUE_INITIALIZER { NULL, NULL, NULL }
/*
* TailQ initialization values.
*/
#define TAILQ_INITIALIZER { NULL, NULL }
/*
* Mutex definitions.
*/
@ -98,10 +166,31 @@ union pthread_mutex_data {
struct pthread_mutex {
enum pthread_mutextype m_type;
struct pthread_queue m_queue;
int m_protocol;
TAILQ_HEAD(mutex_head, pthread) m_queue;
struct pthread *m_owner;
union pthread_mutex_data m_data;
long m_flags;
int m_refcount;
/*
* Used for priority inheritence and protection.
*
* m_prio - For priority inheritence, the highest active
* priority (threads locking the mutex inherit
* this priority). For priority protection, the
* ceiling priority of this mutex.
* m_saved_prio - mutex owners inherited priority before
* taking the mutex, restored when the owner
* unlocks the mutex.
*/
int m_prio;
int m_saved_prio;
/*
* Link for list of all mutexes a thread currently owns.
*/
TAILQ_ENTRY(pthread_mutex) m_qe;
/*
* Lock for accesses to this structure.
@ -120,11 +209,13 @@ struct pthread_mutex {
* Static mutex initialization values.
*/
#define PTHREAD_MUTEX_STATIC_INITIALIZER \
{ MUTEX_TYPE_FAST, PTHREAD_QUEUE_INITIALIZER, \
NULL, { NULL }, MUTEX_FLAGS_INITED }
{ PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, TAILQ_INITIALIZER, \
NULL, { NULL }, MUTEX_FLAGS_INITED, 0, 0, 0, TAILQ_INITIALIZER }
struct pthread_mutex_attr {
enum pthread_mutextype m_type;
int m_protocol;
int m_ceiling;
long m_flags;
};
@ -137,15 +228,16 @@ enum pthread_cond_type {
};
struct pthread_cond {
enum pthread_cond_type c_type;
struct pthread_queue c_queue;
void *c_data;
long c_flags;
enum pthread_cond_type c_type;
TAILQ_HEAD(cond_head, pthread) c_queue;
pthread_mutex_t c_mutex;
void *c_data;
long c_flags;
/*
* Lock for accesses to this structure.
*/
spinlock_t lock;
spinlock_t lock;
};
struct pthread_cond_attr {
@ -164,7 +256,8 @@ struct pthread_cond_attr {
* Static cond initialization values.
*/
#define PTHREAD_COND_STATIC_INITIALIZER \
{ COND_TYPE_FAST, PTHREAD_QUEUE_INITIALIZER, NULL, COND_FLAGS_INITED }
{ COND_TYPE_FAST, PTHREAD_QUEUE_INITIALIZER, NULL, NULL \
COND_FLAGS_INITED }
/*
* Cleanup definitions.
@ -176,7 +269,9 @@ struct pthread_cleanup {
};
struct pthread_attr {
int schedparam_policy;
int sched_policy;
int sched_inherit;
int sched_interval;
int prio;
int suspend;
int flags;
@ -254,9 +349,11 @@ enum pthread_state {
PS_WAIT_WAIT,
PS_SIGSUSPEND,
PS_SIGWAIT,
PS_SPINBLOCK,
PS_JOIN,
PS_SUSPENDED,
PS_DEAD,
PS_DEADLOCK,
PS_STATE_MAX
};
@ -300,8 +397,8 @@ struct pthread_select_data {
};
union pthread_wait_data {
pthread_mutex_t *mutex;
pthread_cond_t *cond;
pthread_mutex_t mutex;
pthread_cond_t cond;
const sigset_t *sigwait; /* Waiting on a signal in sigwait */
struct {
short fd; /* Used when thread waiting on fd */
@ -309,6 +406,7 @@ union pthread_wait_data {
char *fname; /* Source file name for debugging.*/
} fd;
struct pthread_select_data * select_data;
spinlock_t *spinlock;
};
/*
@ -419,7 +517,11 @@ struct pthread {
struct pthread_queue join_queue;
/*
* The current thread can belong to only one queue at a time.
* The current thread can belong to only one scheduling queue
* at a time (ready or waiting queue). It can also belong to
* a queue of threads waiting on mutexes or condition variables.
* Use pqe for the scheduling queue link (both ready and waiting),
* and qe for other links (mutexes and condition variables).
*
* Pointer to queue (if any) on which the current thread is waiting.
*
@ -431,8 +533,11 @@ struct pthread {
/* Pointer to next element in queue. */
struct pthread *qnxt;
/* Priority queue entry for this thread: */
TAILQ_ENTRY(pthread) pqe;
/* Queue entry for this thread: */
TAILQ_ENTRY(pthread) qe;
TAILQ_ENTRY(pthread) qe;
/* Wait data. */
union pthread_wait_data data;
@ -446,10 +551,59 @@ struct pthread {
/* Signal number when in state PS_SIGWAIT: */
int signo;
/*
* Set to non-zero when this thread has deferred thread
* scheduling. We allow for recursive deferral.
*/
int sched_defer_count;
/*
* Set to TRUE if this thread should yield after undeferring
* thread scheduling.
*/
int yield_on_sched_undefer;
/* Miscellaneous data. */
int flags;
#define PTHREAD_EXITING 0x0100
char pthread_priority;
int flags;
#define PTHREAD_FLAGS_PRIVATE 0x0001
#define PTHREAD_EXITING 0x0002
#define PTHREAD_FLAGS_QUEUED 0x0004 /* in queue (qe is used) */
#define PTHREAD_FLAGS_TRACE 0x0008
/*
* Base priority is the user setable and retrievable priority
* of the thread. It is only affected by explicit calls to
* set thread priority and upon thread creation via a thread
* attribute or default priority.
*/
char base_priority;
/*
* Inherited priority is the priority a thread inherits by
* taking a priority inheritence or protection mutex. It
* is not affected by base priority changes. Inherited
* priority defaults to and remains 0 until a mutex is taken
* that is being waited on by any other thread whose priority
* is non-zero.
*/
char inherited_priority;
/*
* Active priority is always the maximum of the threads base
* priority and inherited priority. When there is a change
* in either the real or inherited priority, the active
* priority must be recalculated.
*/
char active_priority;
/* Number of priority ceiling or protection mutexes owned. */
int priority_mutex_count;
/*
* Queue of currently owned mutexes.
*/
TAILQ_HEAD(, pthread_mutex) mutexq;
void *ret;
const void **specific_data;
int specific_data_count;
@ -475,6 +629,14 @@ SCLASS struct pthread * volatile _thread_run
;
#endif
/* Ptr to the thread structure for the last user thread to run: */
SCLASS struct pthread * volatile _last_user_thread
#ifdef GLOBAL_PTHREAD_PRIVATE
= &_thread_kern_thread;
#else
;
#endif
/*
* Ptr to the thread running in single-threaded mode or NULL if
* running multi-threaded (default POSIX behaviour).
@ -547,7 +709,7 @@ SCLASS struct pthread *_thread_initial
/* Default thread attributes: */
SCLASS struct pthread_attr pthread_attr_default
#ifdef GLOBAL_PTHREAD_PRIVATE
= { SCHED_RR, PTHREAD_DEFAULT_PRIORITY, PTHREAD_CREATE_RUNNING,
= { SCHED_RR, 0, TIMESLICE_USEC, PTHREAD_DEFAULT_PRIORITY, PTHREAD_CREATE_RUNNING,
PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL, PTHREAD_STACK_DEFAULT };
#else
;
@ -556,7 +718,7 @@ SCLASS struct pthread_attr pthread_attr_default
/* Default mutex attributes: */
SCLASS struct pthread_mutex_attr pthread_mutexattr_default
#ifdef GLOBAL_PTHREAD_PRIVATE
= { MUTEX_TYPE_FAST, 0 };
= { PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, 0, 0 };
#else
;
#endif
@ -614,6 +776,27 @@ SCLASS pthread_cond_t _gc_cond
*/
struct sigaction _thread_sigact[NSIG];
/*
* Scheduling queues:
*/
SCLASS pq_queue_t _readyq;
SCLASS TAILQ_HEAD(, pthread) _waitingq;
/* Indicates that the waitingq now has threads ready to run. */
SCLASS volatile int _waitingq_check_reqd
#ifdef GLOBAL_PTHREAD_PRIVATE
= 0
#endif
;
/* Thread switch hook. */
SCLASS pthread_switch_routine_t _sched_switch_hook
#ifdef GLOBAL_PTHREAD_PRIVATE
= NULL
#endif
;
/* Undefine the storage class specifier: */
#undef SCLASS
@ -645,6 +828,14 @@ void _lock_thread(void);
void _lock_thread_list(void);
void _unlock_thread(void);
void _unlock_thread_list(void);
int _mutex_cv_lock(pthread_mutex_t *);
int _mutex_cv_unlock(pthread_mutex_t *);
void _mutex_notify_priochange(struct pthread *);
int _pq_init(struct pq_queue *pq, int, int);
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);
void _thread_exit(char *, int, char *);
void _thread_fd_unlock(int, int);
void _thread_fd_unlock_debug(int, int, char *, int);
@ -657,6 +848,8 @@ 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(struct timespec *);
void _thread_kern_sched_defer(void);
void _thread_kern_sched_undefer(void);
void _thread_sig_handler(int, int, struct sigcontext *);
void _thread_start(void);
void _thread_start_sig_handler(void);

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_getinheritsched(pthread_attr_t *attr, int *sched_inherit)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL))
ret = EINVAL;
else
*sched_inherit = (*attr)->sched_inherit;
return(ret);
}
#endif

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_getschedparam(pthread_attr_t *attr, struct sched_param *param)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) || (param == NULL))
ret = EINVAL;
else
param->sched_priority = (*attr)->prio;
return(ret);
}
#endif

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_getschedpolicy(pthread_attr_t *attr, int *policy)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) || (policy == NULL))
ret = EINVAL;
else
*policy = (*attr)->sched_policy;
return(ret);
}
#endif

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_getscope(pthread_attr_t *attr, int *contentionscope)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) || (contentionscope == NULL))
/* Return an invalid argument: */
ret = EINVAL;
else
*contentionscope = (*attr)->flags & PTHREAD_SCOPE_SYSTEM ?
PTHREAD_SCOPE_SYSTEM : PTHREAD_SCOPE_PROCESS;
return(ret);
}
#endif

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_setinheritsched(pthread_attr_t *attr, int sched_inherit)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL))
ret = EINVAL;
else
(*attr)->sched_inherit = sched_inherit;
return(ret);
}
#endif

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_setschedparam(pthread_attr_t *attr, struct sched_param *param)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) || (param == NULL))
ret = EINVAL;
else
(*attr)->prio = param->sched_priority;
return(ret);
}
#endif

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) || (policy < SCHED_FIFO) ||
(policy > SCHED_RR))
ret = EINVAL;
else
(*attr)->sched_policy = policy;
return(ret);
}
#endif

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_setscope(pthread_attr_t *attr, int contentionscope)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) ||
(contentionscope != PTHREAD_SCOPE_PROCESS) ||
(contentionscope != PTHREAD_SCOPE_SYSTEM))
/* Return an invalid argument: */
ret = EINVAL;
else if (contentionscope == PTHREAD_SCOPE_SYSTEM)
/* We don't support system wide contention: */
#ifdef NOT_YET
ret = ENOTSUP;
#else
ret = EOPNOTSUPP;
#endif
else
(*attr)->flags |= contentionscope;
return(ret);
}
#endif

View file

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -37,6 +37,14 @@
#include <pthread.h>
#include "pthread_private.h"
/*
* Prototypes
*/
static inline pthread_t cond_queue_deq(pthread_cond_t);
static inline void cond_queue_remove(pthread_cond_t, pthread_t);
static inline void cond_queue_enq(pthread_cond_t, pthread_t);
int
pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
{
@ -83,9 +91,10 @@ pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
* Initialise the condition variable
* structure:
*/
_thread_queue_init(&pcond->c_queue);
TAILQ_INIT(&pcond->c_queue);
pcond->c_flags |= COND_FLAGS_INITED;
pcond->c_type = type;
pcond->c_mutex = NULL;
memset(&pcond->lock,0,sizeof(pcond->lock));
*cond = pcond;
}
@ -144,33 +153,57 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
switch ((*cond)->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
/* Wait forever: */
_thread_run->wakeup_time.tv_sec = -1;
/*
* Queue the running thread for the condition
* variable:
*/
_thread_queue_enq(&(*cond)->c_queue, _thread_run);
/* Unlock the mutex: */
if ((rval = pthread_mutex_unlock(mutex)) != 0) {
/*
* Cannot unlock the mutex, so remove the
* running thread from the condition
* variable queue:
*/
_thread_queue_deq(&(*cond)->c_queue);
if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
((*cond)->c_mutex != *mutex))) {
/* Unlock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
} else {
/* Schedule the next thread: */
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
/* Lock the mutex: */
rval = pthread_mutex_lock(mutex);
/* Return invalid argument error: */
rval = EINVAL;
} else {
/* Reset the timeout flag: */
_thread_run->timeout = 0;
/*
* Queue the running thread for the condition
* variable:
*/
cond_queue_enq(*cond, _thread_run);
/* Remember the mutex that is being used: */
(*cond)->c_mutex = *mutex;
/* Wait forever: */
_thread_run->wakeup_time.tv_sec = -1;
/* Unlock the mutex: */
if ((rval = _mutex_cv_unlock(mutex)) != 0) {
/*
* Cannot unlock the mutex, so remove
* the running thread from the condition
* variable queue:
*/
cond_queue_remove(*cond, _thread_run);
/* Check for no more waiters: */
if (TAILQ_FIRST(&(*cond)->c_queue) ==
NULL)
(*cond)->c_mutex = NULL;
/* Unlock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
}
else {
/*
* Schedule the next thread and unlock
* the condition variable structure:
*/
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
/* Lock the mutex: */
rval = _mutex_cv_lock(mutex);
}
}
break;
@ -183,7 +216,6 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
rval = EINVAL;
break;
}
}
/* Return the completion status: */
@ -213,42 +245,88 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
switch ((*cond)->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
/* Set the wakeup time: */
_thread_run->wakeup_time.tv_sec = abstime->tv_sec;
_thread_run->wakeup_time.tv_nsec = abstime->tv_nsec;
/* Reset the timeout flag: */
_thread_run->timeout = 0;
/*
* Queue the running thread for the condition
* variable:
*/
_thread_queue_enq(&(*cond)->c_queue, _thread_run);
/* Unlock the mutex: */
if ((rval = pthread_mutex_unlock(mutex)) != 0) {
/*
* Cannot unlock the mutex, so remove the
* running thread from the condition
* variable queue:
*/
_thread_queue_deq(&(*cond)->c_queue);
if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
((*cond)->c_mutex != *mutex))) {
/* Return invalid argument error: */
rval = EINVAL;
/* Unlock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
} else {
/* Schedule the next thread: */
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
/* Set the wakeup time: */
_thread_run->wakeup_time.tv_sec =
abstime->tv_sec;
_thread_run->wakeup_time.tv_nsec =
abstime->tv_nsec;
/* Lock the mutex: */
if ((rval = pthread_mutex_lock(mutex)) != 0) {
}
/* Check if the wait timed out: */
else if (_thread_run->timeout) {
/* Return a timeout error: */
rval = ETIMEDOUT;
/* Reset the timeout flag: */
_thread_run->timeout = 0;
/*
* Queue the running thread for the condition
* variable:
*/
cond_queue_enq(*cond, _thread_run);
/* Remember the mutex that is being used: */
(*cond)->c_mutex = *mutex;
/* Unlock the mutex: */
if ((rval = _mutex_cv_unlock(mutex)) != 0) {
/*
* Cannot unlock the mutex, so remove
* the running thread from the condition
* variable queue:
*/
cond_queue_remove(*cond, _thread_run);
/* Check for no more waiters: */
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
(*cond)->c_mutex = NULL;
/* Unlock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
} else {
/*
* Schedule the next thread and unlock
* the condition variable structure:
*/
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
/* Check if the wait timedout: */
if (_thread_run->timeout == 0) {
/* Lock the mutex: */
rval = _mutex_cv_lock(mutex);
}
else {
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
/*
* The wait timed out; remove
* the thread from the condition
* variable queue:
*/
cond_queue_remove(*cond,
_thread_run);
/* Check for no more waiters: */
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
(*cond)->c_mutex = NULL;
/* Unock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
/* Return a timeout error: */
rval = ETIMEDOUT;
/*
* Lock the mutex and ignore
* any errors:
*/
(void)_mutex_cv_lock(mutex);
}
}
}
break;
@ -273,7 +351,6 @@ int
pthread_cond_signal(pthread_cond_t * cond)
{
int rval = 0;
int status;
pthread_t pthread;
if (cond == NULL || *cond == NULL)
@ -286,11 +363,22 @@ pthread_cond_signal(pthread_cond_t * cond)
switch ((*cond)->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
/* Bring the next thread off the condition queue: */
if ((pthread = _thread_queue_deq(&(*cond)->c_queue)) != NULL) {
/*
* Enter a loop to dequeue threads from the condition
* queue until we find one that hasn't previously
* timed out.
*/
while (((pthread = cond_queue_deq(*cond)) != NULL) &&
(pthread->timeout != 0)) {
}
if (pthread != NULL)
/* Allow the thread to run: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
}
/* Check for no more waiters: */
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
(*cond)->c_mutex = NULL;
break;
/* Trap invalid condition variable types: */
@ -312,12 +400,21 @@ int
pthread_cond_broadcast(pthread_cond_t * cond)
{
int rval = 0;
int status;
pthread_t pthread;
if (cond == NULL || *cond == NULL)
rval = EINVAL;
else {
/*
* Guard against preemption by a scheduling signal.
* A change of thread state modifies the waiting
* and priority queues. In addition, we must assure
* that all threads currently waiting on the condition
* variable are signaled and are not timedout by a
* scheduling signal that causes a preemption.
*/
_thread_kern_sched_defer();
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
@ -329,11 +426,17 @@ pthread_cond_broadcast(pthread_cond_t * cond)
* Enter a loop to bring all threads off the
* condition queue:
*/
while ((pthread =
_thread_queue_deq(&(*cond)->c_queue)) != NULL) {
/* Allow the thread to run: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
while ((pthread = cond_queue_deq(*cond)) != NULL) {
/*
* The thread is already running if the
* timeout flag is set.
*/
if (pthread->timeout == 0)
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
}
/* There are no more waiting threads: */
(*cond)->c_mutex = NULL;
break;
/* Trap invalid condition variable types: */
@ -345,9 +448,74 @@ pthread_cond_broadcast(pthread_cond_t * cond)
/* Unlock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
/* Reenable preemption and yield if necessary.
*/
_thread_kern_sched_undefer();
}
/* Return the completion status: */
return (rval);
}
/*
* Dequeue a waiting thread from the head of a condition queue in
* descending priority order.
*/
static inline pthread_t
cond_queue_deq(pthread_cond_t cond)
{
pthread_t pthread;
if ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
TAILQ_REMOVE(&cond->c_queue, pthread, qe);
pthread->flags &= ~PTHREAD_FLAGS_QUEUED;
}
return(pthread);
}
/*
* Remove a waiting thread from a condition queue in descending priority
* order.
*/
static inline void
cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
{
/*
* Because pthread_cond_timedwait() can timeout as well
* as be signaled by another thread, it is necessary to
* guard against removing the thread from the queue if
* it isn't in the queue.
*/
if (pthread->flags & PTHREAD_FLAGS_QUEUED) {
TAILQ_REMOVE(&cond->c_queue, pthread, qe);
pthread->flags &= ~PTHREAD_FLAGS_QUEUED;
}
}
/*
* Enqueue a waiting thread to a condition queue in descending priority
* order.
*/
static inline void
cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
{
pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
/*
* 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);
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);
}
pthread->flags |= PTHREAD_FLAGS_QUEUED;
}
#endif

View file

@ -99,12 +99,6 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
*/
new_thread->magic = PTHREAD_MAGIC;
if (pattr->suspend == PTHREAD_CREATE_SUSPENDED) {
PTHREAD_NEW_STATE(new_thread,PS_SUSPENDED);
} else {
PTHREAD_NEW_STATE(new_thread,PS_RUNNING);
}
/* Initialise the thread for signals: */
new_thread->sigmask = _thread_run->sigmask;
@ -162,21 +156,26 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
*/
if (new_thread->attr.flags & PTHREAD_INHERIT_SCHED) {
/* Copy the scheduling attributes: */
new_thread->pthread_priority = _thread_run->pthread_priority;
new_thread->attr.prio = _thread_run->pthread_priority;
new_thread->attr.schedparam_policy = _thread_run->attr.schedparam_policy;
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;
} else {
/*
* Use just the thread priority, leaving the
* other scheduling attributes as their
* default values:
*/
new_thread->pthread_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;
/* Initialise the join queue for the new thread: */
_thread_queue_init(&(new_thread->join_queue));
/* Initialize the mutex queue: */
TAILQ_INIT(&new_thread->mutexq);
/* Initialise hooks in the thread structure: */
new_thread->specific_data = NULL;
new_thread->cleanup = NULL;
@ -200,6 +199,27 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Unlock the thread list: */
_unlock_thread_list();
/*
* Guard against preemption by a scheduling signal.
* A change of thread state modifies the waiting
* and priority queues.
*/
_thread_kern_sched_defer();
if (pattr->suspend == PTHREAD_CREATE_SUSPENDED) {
new_thread->state = PS_SUSPENDED;
PTHREAD_WAITQ_INSERT(new_thread);
} else {
new_thread->state = PS_RUNNING;
PTHREAD_PRIOQ_INSERT_TAIL(new_thread);
}
/*
* Reenable preemption and yield if a scheduling
* signal occurred while in the critical region.
*/
_thread_kern_sched_undefer();
/* Return a pointer to the thread structure: */
(*thread) = new_thread;

View file

@ -52,11 +52,24 @@ pthread_detach(pthread_t pthread)
/* Flag the thread as detached: */
pthread->attr.flags |= PTHREAD_DETACHED;
/*
* Guard against preemption by a scheduling signal.
* A change of thread state modifies the waiting
* and priority queues.
*/
_thread_kern_sched_defer();
/* Enter a loop to bring all threads off the join queue: */
while ((next_thread = _thread_queue_deq(&pthread->join_queue)) != NULL) {
/* Make the thread run: */
PTHREAD_NEW_STATE(next_thread,PS_RUNNING);
}
/*
* Reenable preemption and yield if a scheduling signal
* occurred while in the critical region.
*/
_thread_kern_sched_undefer();
} else
/* Return an error: */
rval = EINVAL;

View file

@ -52,7 +52,7 @@ execve(const char *name, char *const * argv, char *const * envp)
itimer.it_interval.tv_usec = 0;
itimer.it_value.tv_sec = 0;
itimer.it_value.tv_usec = 0;
setitimer(ITIMER_VIRTUAL, &itimer, NULL);
setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL);
/* Close the pthread kernel pipe: */
_thread_sys_close(_thread_kern_pipe[0]);

View file

@ -49,7 +49,7 @@ void _exit(int status)
itimer.it_interval.tv_usec = 0;
itimer.it_value.tv_sec = 0;
itimer.it_value.tv_usec = 0;
setitimer(ITIMER_VIRTUAL, &itimer, NULL);
setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL);
/* Close the pthread kernel pipe: */
_thread_sys_close(_thread_kern_pipe[0]);
@ -127,12 +127,25 @@ pthread_exit(void *status)
/* Run the thread-specific data destructors: */
_thread_cleanupspecific();
}
/*
* Guard against preemption by a scheduling signal. A change of
* thread state modifies the waiting and priority queues.
*/
_thread_kern_sched_defer();
/* Check if there are any threads joined to this one: */
while ((pthread = _thread_queue_deq(&(_thread_run->join_queue))) != NULL) {
/* Wake the joined thread and let it detach this thread: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
}
/*
* Reenable preemption and yield if a scheduling signal
* occurred while in the critical region.
*/
_thread_kern_sched_undefer();
/*
* Lock the garbage collector mutex to ensure that the garbage
* collector is not using the dead thread list.

View file

@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: uthread_fd.c,v 1.8 1998/06/09 23:16:53 jb Exp $
* $Id: uthread_fd.c,v 1.9 1998/09/13 15:33:42 dt Exp $
*
*/
#include <errno.h>
@ -199,7 +199,7 @@ _thread_fd_unlock(int fd, int lock_type)
} else {
/*
* Set the state of the new owner of
* the thread to running:
* the thread to running:
*/
PTHREAD_NEW_STATE(_thread_fd_table[fd]->r_owner,PS_RUNNING);

View file

@ -41,7 +41,7 @@
pid_t
fork(void)
{
int flags;
int i, flags;
pid_t ret;
pthread_t pthread;
pthread_t pthread_next;
@ -88,6 +88,11 @@ fork(void)
else if (_thread_sys_fcntl(_thread_kern_pipe[1], F_SETFL, flags | O_NONBLOCK) == -1) {
/* Abort this application: */
abort();
/* Initialize the ready queue: */
} else if (_pq_init(&_readyq, PTHREAD_MIN_PRIORITY,
PTHREAD_MAX_PRIORITY) != 0) {
/* Abort this application: */
PANIC("Cannot allocate priority ready queue.");
} else {
/* Point to the first thread in the list: */
pthread = _thread_link_list;
@ -119,6 +124,33 @@ fork(void)
/* Point to the next thread: */
pthread = pthread_next;
}
/* Re-init the waiting queues. */
TAILQ_INIT(&_waitingq);
/* Initialize the scheduling switch hook routine: */
_sched_switch_hook = NULL;
/* Clear out any locks in the file descriptor table: */
for (i = 0; i < _thread_dtablesize; i++) {
if (_thread_fd_table[i] != NULL) {
/* Initialise the file locks: */
memset(&_thread_fd_table[i]->lock, 0,
sizeof(_thread_fd_table[i]->lock));
_thread_fd_table[i]->r_owner = NULL;
_thread_fd_table[i]->w_owner = NULL;
_thread_fd_table[i]->r_fname = NULL;
_thread_fd_table[i]->w_fname = NULL;
_thread_fd_table[i]->r_lineno = 0;;
_thread_fd_table[i]->w_lineno = 0;;
_thread_fd_table[i]->r_lockcount = 0;;
_thread_fd_table[i]->w_lockcount = 0;;
/* Initialise the read/write queues: */
_thread_queue_init(&_thread_fd_table[i]->r_queue);
_thread_queue_init(&_thread_fd_table[i]->w_queue);
}
}
}
}

View file

@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: uthread_gc.c,v 1.1 1998/09/30 06:36:56 jb Exp $
* $Id: uthread_gc.c,v 1.2 1998/09/30 19:17:51 dt Exp $
*
* Garbage collector thread. Frees memory allocated for dead threads.
*
@ -47,6 +47,7 @@ _thread_gc(pthread_addr_t arg)
int f_debug;
int f_done = 0;
int ret;
sigset_t mask;
pthread_t pthread;
pthread_t pthread_cln;
pthread_t pthread_nxt;
@ -54,6 +55,13 @@ _thread_gc(pthread_addr_t arg)
struct timespec abstime;
void *p_stack;
/* Block all signals */
sigfillset (&mask);
sigprocmask (SIG_BLOCK, &mask, NULL);
/* Mark this thread as a library thread (not a user thread). */
_thread_run->flags |= PTHREAD_FLAGS_PRIVATE;
/* Set a debug flag based on an environment variable. */
f_debug = (getenv("LIBC_R_DEBUG") != NULL);

View file

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -38,12 +38,11 @@
int
pthread_getprio(pthread_t pthread)
{
int ret;
int policy, ret;
struct sched_param param;
/* Find the thread in the list of active threads: */
if ((ret = _find_thread(pthread)) == 0)
/* Get the thread priority: */
ret = pthread->pthread_priority;
if ((ret = pthread_getschedparam(pthread, &policy, &param)) == 0)
ret = param.sched_priority;
else {
/* Invalid thread: */
errno = ret;

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_getschedparam(pthread_t pthread, int *policy, struct sched_param *param)
{
int ret;
if ((param == NULL) || (policy == NULL))
/* Return an invalid argument error: */
ret = EINVAL;
/* 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;
*policy = pthread->attr.sched_policy;
}
return(ret);
}
#endif

View file

@ -60,9 +60,11 @@ static const struct s_thread_info thread_info[] = {
{PS_WAIT_WAIT , "Waiting process"},
{PS_SIGSUSPEND , "Suspended, waiting for a signal"},
{PS_SIGWAIT , "Waiting for a signal"},
{PS_SPINBLOCK , "Waiting for a spinlock"},
{PS_JOIN , "Waiting to join"},
{PS_SUSPENDED , "Suspended"},
{PS_DEAD , "Dead"},
{PS_DEADLOCK , "Deadlocked"},
{PS_STATE_MAX , "Not a real state!"}
};
@ -75,6 +77,7 @@ _thread_dump_info(void)
int j;
pthread_t pthread;
char tmpfile[128];
pq_list_t *pq_list;
for (i = 0; i < 100000; i++) {
snprintf(tmpfile, sizeof(tmpfile), "/tmp/uthread.dump.%u.%i",
@ -116,7 +119,7 @@ _thread_dump_info(void)
snprintf(s, sizeof(s),
"--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
pthread, (pthread->name == NULL) ?
"":pthread->name, pthread->pthread_priority,
"":pthread->name, pthread->base_priority,
thread_info[j].name,
pthread->fname,pthread->lineno);
_thread_sys_write(fd, s, strlen(s));
@ -167,6 +170,50 @@ _thread_dump_info(void)
}
}
/* Output a header for ready threads: */
strcpy(s, "\n\n=============\nREADY THREADS\n\n");
_thread_sys_write(fd, s, strlen(s));
/* Enter a loop to report each thread in the ready queue: */
TAILQ_FOREACH (pq_list, &_readyq.pq_queue, pl_link) {
TAILQ_FOREACH(pthread, &pq_list->pl_head, pqe) {
/* Find the state: */
for (j = 0; j < (sizeof(thread_info) /
sizeof(struct s_thread_info)) - 1; j++)
if (thread_info[j].state == pthread->state)
break;
/* Output a record for the current thread: */
snprintf(s, sizeof(s),
"--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
pthread, (pthread->name == NULL) ?
"":pthread->name, pthread->base_priority,
thread_info[j].name,
pthread->fname,pthread->lineno);
_thread_sys_write(fd, s, strlen(s));
}
}
/* Output a header for waiting threads: */
strcpy(s, "\n\n=============\nWAITING THREADS\n\n");
_thread_sys_write(fd, s, strlen(s));
/* Enter a loop to report each thread in the waiting queue: */
TAILQ_FOREACH (pthread, &_waitingq, pqe) {
/* Find the state: */
for (j = 0; j < (sizeof(thread_info) /
sizeof(struct s_thread_info)) - 1; j++)
if (thread_info[j].state == pthread->state)
break;
/* Output a record for the current thread: */
snprintf(s, sizeof(s),
"--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
pthread, (pthread->name == NULL) ?
"":pthread->name, pthread->base_priority,
thread_info[j].name,
pthread->fname,pthread->lineno);
_thread_sys_write(fd, s, strlen(s));
}
/* Check if there are no dead threads: */
if (_thread_dead == NULL) {
/* Output a record: */
@ -186,7 +233,7 @@ _thread_dump_info(void)
/* Output a record for the current thread: */
snprintf(s, sizeof(s),
"Thread %p prio %3d [%s:%d]\n",
pthread, pthread->pthread_priority,
pthread, pthread->base_priority,
pthread->fname,pthread->lineno);
_thread_sys_write(fd, s, strlen(s));
}

View file

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -147,6 +147,11 @@ _thread_init(void)
/* Abort this application: */
PANIC("Cannot get kernel write pipe flags");
}
/* Initialize the ready queue: */
else if (_pq_init(&_readyq, PTHREAD_MIN_PRIORITY, PTHREAD_MAX_PRIORITY) != 0) {
/* Abort this application: */
PANIC("Cannot allocate priority ready queue.");
}
/* Allocate memory for the thread structure of the initial thread: */
else if ((_thread_initial = (pthread_t) malloc(sizeof(struct pthread))) == NULL) {
/*
@ -157,10 +162,25 @@ _thread_init(void)
} else {
/* Zero the global kernel thread structure: */
memset(&_thread_kern_thread, 0, sizeof(struct pthread));
_thread_kern_thread.flags = PTHREAD_FLAGS_PRIVATE;
memset(_thread_initial, 0, sizeof(struct pthread));
/* Initialize the waiting queue: */
TAILQ_INIT(&_waitingq);
/* Initialize the scheduling switch hook routine: */
_sched_switch_hook = NULL;
/*
* Write a magic value to the thread structure
* to help identify valid ones:
*/
_thread_initial->magic = PTHREAD_MAGIC;
/* Default the priority of the initial thread: */
_thread_initial->pthread_priority = PTHREAD_DEFAULT_PRIORITY;
_thread_initial->base_priority = PTHREAD_DEFAULT_PRIORITY;
_thread_initial->active_priority = PTHREAD_DEFAULT_PRIORITY;
_thread_initial->inherited_priority = 0;
/* Initialise the state of the initial thread: */
_thread_initial->state = PS_RUNNING;
@ -168,7 +188,13 @@ _thread_init(void)
/* Initialise the queue: */
_thread_queue_init(&(_thread_initial->join_queue));
/* Initialize the owned mutex queue and count: */
TAILQ_INIT(&(_thread_initial->mutexq));
_thread_initial->priority_mutex_count = 0;
/* Initialise the rest of the fields: */
_thread_initial->sched_defer_count = 0;
_thread_initial->yield_on_sched_undefer = 0;
_thread_initial->specific_data = NULL;
_thread_initial->cleanup = NULL;
_thread_initial->queue = NULL;
@ -206,9 +232,9 @@ _thread_init(void)
* signals that the user-thread kernel needs. Actually
* SIGINFO isn't really needed, but it is nice to have.
*/
if (_thread_sys_sigaction(SIGVTALRM, &act, NULL) != 0 ||
_thread_sys_sigaction(SIGINFO , &act, NULL) != 0 ||
_thread_sys_sigaction(SIGCHLD , &act, NULL) != 0) {
if (_thread_sys_sigaction(_SCHED_SIGNAL, &act, NULL) != 0 ||
_thread_sys_sigaction(SIGINFO, &act, NULL) != 0 ||
_thread_sys_sigaction(SIGCHLD, &act, NULL) != 0) {
/*
* Abort this process if signal initialisation fails:
*/
@ -256,6 +282,8 @@ _thread_init(void)
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

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -53,16 +53,18 @@
static void
_thread_kern_select(int wait_reqd);
static inline void
thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in);
void
_thread_kern_sched(struct sigcontext * scp)
{
#ifndef __alpha__
char *fdata;
#endif
int prio = -1;
pthread_t pthread;
pthread_t pthread_h = NULL;
pthread_t pthread_s = NULL;
pthread_t last_thread = NULL;
struct itimerval itimer;
struct timespec ts;
struct timespec ts1;
@ -105,18 +107,21 @@ __asm__("fnsave %0": :"m"(*fdata));
*/
_thread_kern_in_sched = 0;
/*
* There might be pending signals for this thread, so
* dispatch any that aren't blocked:
*/
_dispatch_signals();
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;
/* If the currently running thread is a user thread, save it: */
if ((_thread_run->flags & PTHREAD_FLAGS_PRIVATE) == 0)
_last_user_thread = _thread_run;
/*
* Enter a the scheduling loop that finds the next thread that is
* Enter a scheduling loop that finds the next thread that is
* ready to run. This loop completes when there are no more threads
* in the global list or when a thread has its state restored by
* either a sigreturn (if the state was saved as a sigcontext) or a
@ -134,12 +139,48 @@ __asm__("fnsave %0": :"m"(*fdata));
_thread_kern_select(0);
/*
* Enter a loop to look for sleeping threads that are ready:
* Define the maximum time before a scheduling signal
* is required:
*/
for (pthread = _thread_link_list; pthread != NULL;
pthread = pthread->nxt) {
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;
/*
* Enter a loop to look for sleeping threads that are ready
* or timedout. While we're at it, also find the smallest
* timeout value for threads waiting for a time.
*/
_waitingq_check_reqd = 0; /* reset flag before loop */
TAILQ_FOREACH(pthread, &_waitingq, pqe) {
/* Check if this thread is ready: */
if (pthread->state == PS_RUNNING) {
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
/*
* Check if this thread is blocked by an
* atomic lock:
*/
else if (pthread->state == PS_SPINBLOCK) {
/*
* If the lock is available, let
* the thread run.
*/
if (pthread->data.spinlock->access_lock == 0) {
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
}
/* Check if this thread is to timeout: */
if (pthread->state == PS_COND_WAIT ||
} else if (pthread->state == PS_COND_WAIT ||
pthread->state == PS_SLEEP_WAIT ||
pthread->state == PS_FDR_WAIT ||
pthread->state == PS_FDW_WAIT ||
@ -163,9 +204,9 @@ __asm__("fnsave %0": :"m"(*fdata));
*/
if (pthread->state == PS_SELECT_WAIT) {
/*
* The select has timed out,
* so zero the file
* descriptor sets:
* The select has timed out, so
* zero the file descriptor
* sets:
*/
FD_ZERO(&pthread->data.select_data->readfds);
FD_ZERO(&pthread->data.select_data->writefds);
@ -189,12 +230,71 @@ __asm__("fnsave %0": :"m"(*fdata));
* it to be restarted:
*/
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
} else {
/*
* 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:
*/
if (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:
*/
if (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;
}
}
}
}
/* Check if there is a current thread: */
if (_thread_run != &_thread_kern_thread) {
/*
* This thread no longer needs to yield the CPU.
*/
_thread_run->yield_on_sched_undefer = 0;
/*
* Save the current time as the time that the thread
* became inactive:
@ -204,194 +304,64 @@ __asm__("fnsave %0": :"m"(*fdata));
/*
* Accumulate the number of microseconds that this
* thread has run for:
* thread has run for:
*/
if (_thread_run->slice_usec != -1) {
_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;
}
if ((_thread_run->slice_usec != -1) &&
(_thread_run->attr.sched_policy != SCHED_FIFO)) {
_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 if this thread has reached its allocated
* time slice period:
*/
if (_thread_run->slice_usec > TIMESLICE_USEC) {
/* Check for time quantum exceeded: */
if (_thread_run->slice_usec > TIMESLICE_USEC)
_thread_run->slice_usec = -1;
}
if (_thread_run->state == PS_RUNNING) {
if (_thread_run->slice_usec == -1) {
/*
* The thread exceeded its time
* quantum or it yielded the CPU;
* place it at the tail of the
* queue for its priority.
*/
PTHREAD_PRIOQ_INSERT_TAIL(_thread_run);
} else {
/*
* The thread hasn't exceeded its
* interval. Place it at the head
* of the queue for its priority.
*/
PTHREAD_PRIOQ_INSERT_HEAD(_thread_run);
}
}
else if (_thread_run->state == PS_DEAD) {
/*
* Flag the allocated time slice period as
* up:
* Don't add dead threads to the waiting
* queue, because when they're reaped, it
* will corrupt the queue.
*/
}
else {
/*
* This thread has changed state and needs
* to be placed in the waiting queue.
*/
PTHREAD_WAITQ_INSERT(_thread_run);
/* Restart the time slice: */
_thread_run->slice_usec = -1;
}
}
/* Check if an incremental priority update is required: */
if (((tv.tv_sec - kern_inc_prio_time.tv_sec) * 1000000 +
tv.tv_usec - kern_inc_prio_time.tv_usec) > INC_PRIO_USEC) {
/*
* Enter a loop to look for run-enabled threads that
* have not run since the last time that an
* incremental priority update was performed:
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
/* Check if this thread is unable to run: */
if (pthread->state != PS_RUNNING) {
}
/*
* Check if the last time that this thread
* was run (as indicated by the last time it
* became inactive) is before the time that
* the last incremental priority check was
* made:
*/
else if (timercmp(&pthread->last_inactive, &kern_inc_prio_time, <)) {
/*
* Increment the incremental priority
* for this thread in the hope that
* it will eventually get a chance to
* run:
*/
(pthread->inc_prio)++;
}
}
/* Save the new incremental priority update time: */
kern_inc_prio_time.tv_sec = tv.tv_sec;
kern_inc_prio_time.tv_usec = tv.tv_usec;
}
/*
* Enter a loop to look for the first thread of the highest
* priority that is ready to run:
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
/* Check if the current thread is unable to run: */
if (pthread->state != PS_RUNNING) {
}
/*
* Check if no run-enabled thread has been seen or if
* the current thread has a priority higher than the
* highest seen so far:
*/
else if (pthread_h == NULL || (pthread->pthread_priority + pthread->inc_prio) > prio) {
/*
* Save this thread as the highest priority
* thread seen so far:
*/
pthread_h = pthread;
prio = pthread->pthread_priority + pthread->inc_prio;
}
}
/*
* Enter a loop to look for a thread that: 1. Is run-enabled.
* 2. Has the required agregate priority. 3. Has not been
* allocated its allocated time slice. 4. Became inactive
* least recently.
* Get the highest priority thread in the ready queue.
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
/* Check if the current thread is unable to run: */
if (pthread->state != PS_RUNNING) {
/* Ignore threads that are not ready to run. */
}
pthread_h = PTHREAD_PRIOQ_FIRST;
/*
* Check if the current thread as an agregate
* priority not equal to the highest priority found
* above:
*/
else if ((pthread->pthread_priority + pthread->inc_prio) != prio) {
/*
* Ignore threads which have lower agregate
* priority.
*/
}
/*
* Check if the current thread reached its time slice
* allocation last time it ran (or if it has not run
* yet):
*/
else if (pthread->slice_usec == -1) {
}
/*
* Check if an eligible thread has not been found
* yet, or if the current thread has an inactive time
* earlier than the last one seen:
*/
else if (pthread_s == NULL || timercmp(&pthread->last_inactive, &tv1, <)) {
/*
* Save the pointer to the current thread as
* the most eligible thread seen so far:
*/
pthread_s = pthread;
/*
* Save the time that the selected thread
* became inactive:
*/
tv1.tv_sec = pthread->last_inactive.tv_sec;
tv1.tv_usec = pthread->last_inactive.tv_usec;
}
}
/*
* Check if no thread was selected according to incomplete
* time slice allocation:
*/
if (pthread_s == NULL) {
/*
* Enter a loop to look for any other thread that: 1.
* Is run-enabled. 2. Has the required agregate
* priority. 3. Became inactive least recently.
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
/*
* Check if the current thread is unable to
* run:
*/
if (pthread->state != PS_RUNNING) {
/*
* Ignore threads that are not ready
* to run.
*/
}
/*
* Check if the current thread as an agregate
* priority not equal to the highest priority
* found above:
*/
else if ((pthread->pthread_priority + pthread->inc_prio) != prio) {
/*
* Ignore threads which have lower
* agregate priority.
*/
}
/*
* Check if an eligible thread has not been
* found yet, or if the current thread has an
* inactive time earlier than the last one
* seen:
*/
else if (pthread_s == NULL || timercmp(&pthread->last_inactive, &tv1, <)) {
/*
* Save the pointer to the current
* thread as the most eligible thread
* seen so far:
*/
pthread_s = pthread;
/*
* Save the time that the selected
* thread became inactive:
*/
tv1.tv_sec = pthread->last_inactive.tv_sec;
tv1.tv_usec = pthread->last_inactive.tv_usec;
}
}
}
/* Check if there are no threads ready to run: */
if (pthread_s == NULL) {
if (pthread_h == NULL) {
/*
* Lock the pthread kernel by changing the pointer to
* the running thread to point to the global kernel
@ -406,7 +376,10 @@ __asm__("fnsave %0": :"m"(*fdata));
_thread_kern_select(1);
} else {
/* Make the selected thread the current thread: */
_thread_run = pthread_s;
_thread_run = pthread_h;
/* Remove the thread from the ready queue. */
PTHREAD_PRIOQ_REMOVE(_thread_run);
/*
* Save the current time as the time that the thread
@ -424,149 +397,22 @@ __asm__("fnsave %0": :"m"(*fdata));
/* Reset the accumulated time slice period: */
_thread_run->slice_usec = 0;
}
/*
* Reset the incremental priority now that this
* thread has been given the chance to run:
*/
_thread_run->inc_prio = 0;
/* Check if there is more than one thread: */
if (_thread_run != _thread_link_list || _thread_run->nxt != NULL) {
/*
* Define the maximum time before a SIGVTALRM
* 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;
/*
* Enter a loop to look for threads waiting
* for a time:
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
/*
* Check if this thread is to
* timeout:
*/
if (pthread->state == PS_COND_WAIT ||
pthread->state == PS_SLEEP_WAIT ||
pthread->state == PS_FDR_WAIT ||
pthread->state == PS_FDW_WAIT ||
pthread->state == PS_SELECT_WAIT) {
/*
* Check if this thread is to
* wait forever:
*/
if (pthread->wakeup_time.tv_sec == -1) {
}
/*
* Check if this thread is to
* wakeup immediately:
*/
else if (pthread->wakeup_time.tv_sec == 0 &&
pthread->wakeup_time.tv_nsec == 0) {
}
/*
* Check if the current time
* is after the wakeup time:
*/
else if ((ts.tv_sec > pthread->wakeup_time.tv_sec) ||
((ts.tv_sec == pthread->wakeup_time.tv_sec) &&
(ts.tv_nsec > pthread->wakeup_time.tv_nsec))) {
} else {
/*
* 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:
*/
if (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:
*/
if (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(&tv, &ts1);
/*
* Check if the
* thread will be
* ready sooner than
* the earliest one
* found so far:
*/
if (timercmp(&tv, &itimer.it_value, <)) {
/*
* Update the
* time
* value:
*/
itimer.it_value.tv_sec = tv.tv_sec;
itimer.it_value.tv_usec = tv.tv_usec;
}
}
}
}
/*
* Start the interval timer for the
* calculated time interval:
*/
if (setitimer(ITIMER_VIRTUAL, &itimer, NULL) != 0) {
if (setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL) != 0) {
/*
* Cannot initialise the timer, so
* abort this process:
*/
PANIC("Cannot set virtual timer");
PANIC("Cannot set scheduling timer");
}
}
/* Check if a signal context was saved: */
if (_thread_run->sig_saved == 1) {
#ifndef __alpha__
@ -579,20 +425,30 @@ __asm__("fnsave %0": :"m"(*fdata));
/* 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;
_thread_kern_in_sched = 0;
/*
* 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);
}
_thread_sys_sigreturn(&_thread_run->saved_sigcontext);
} else
} 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);
}
/* This point should not be reached. */
PANIC("Thread has returned from sigreturn or longjmp");
@ -679,7 +535,8 @@ _thread_kern_select(int wait_reqd)
* Enter a loop to process threads waiting on either file descriptors
* or times:
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
_waitingq_check_reqd = 0; /* reset flag before loop */
TAILQ_FOREACH (pthread, &_waitingq, pqe) {
/* Assume that this state does not time out: */
settimeout = 0;
@ -690,12 +547,12 @@ _thread_kern_select(int wait_reqd)
* operations or timeouts:
*/
case PS_DEAD:
case PS_DEADLOCK:
case PS_FDLR_WAIT:
case PS_FDLW_WAIT:
case PS_FILE_WAIT:
case PS_JOIN:
case PS_MUTEX_WAIT:
case PS_RUNNING:
case PS_SIGTHREAD:
case PS_SIGWAIT:
case PS_STATE_MAX:
@ -704,6 +561,16 @@ _thread_kern_select(int wait_reqd)
/* Nothing to do here. */
break;
case PS_RUNNING:
/*
* A signal occurred and made this thread ready
* while in the scheduler or while the scheduling
* queues were protected.
*/
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
break;
/* File descriptor read wait: */
case PS_FDR_WAIT:
/* Add the file descriptor to the read set: */
@ -1010,16 +877,16 @@ _thread_kern_select(int wait_reqd)
* descriptors that are flagged as available by the
* _select syscall:
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
TAILQ_FOREACH (pthread, &_waitingq, pqe) {
/* Process according to thread state: */
switch (pthread->state) {
/*
* States which do not depend on file
* descriptor I/O operations:
*/
case PS_RUNNING:
case PS_COND_WAIT:
case PS_DEAD:
case PS_DEADLOCK:
case PS_FDLR_WAIT:
case PS_FDLW_WAIT:
case PS_FILE_WAIT:
@ -1034,6 +901,15 @@ _thread_kern_select(int wait_reqd)
/* Nothing to do here. */
break;
case PS_RUNNING:
/*
* A signal occurred and made this thread
* ready while in the scheduler.
*/
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
break;
/* File descriptor read wait: */
case PS_FDR_WAIT:
/*
@ -1047,6 +923,13 @@ _thread_kern_select(int wait_reqd)
* is scheduled next:
*/
pthread->state = PS_RUNNING;
/*
* Remove it from the waiting queue
* and add it to the ready queue:
*/
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
break;
@ -1063,6 +946,13 @@ _thread_kern_select(int wait_reqd)
* scheduled next:
*/
pthread->state = PS_RUNNING;
/*
* Remove it from the waiting queue
* and add it to the ready queue:
*/
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
break;
@ -1269,6 +1159,13 @@ _thread_kern_select(int wait_reqd)
* thread to run:
*/
pthread->state = PS_RUNNING;
/*
* Remove it from the waiting queue
* and add it to the ready queue:
*/
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
break;
}
@ -1320,4 +1217,80 @@ _thread_kern_set_timeout(struct timespec * timeout)
}
return;
}
void
_thread_kern_sched_defer(void)
{
/* Allow scheduling deferral to be recursive. */
_thread_run->sched_defer_count++;
}
void
_thread_kern_sched_undefer(void)
{
pthread_t pthread;
int need_resched = 0;
/*
* Perform checks to yield only if we are about to undefer
* scheduling.
*/
if (_thread_run->sched_defer_count == 1) {
/*
* Check if the waiting queue needs to be examined for
* threads that are now ready:
*/
while (_waitingq_check_reqd != 0) {
/* Clear the flag before checking the waiting queue: */
_waitingq_check_reqd = 0;
TAILQ_FOREACH(pthread, &_waitingq, pqe) {
if (pthread->state == PS_RUNNING) {
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
}
}
/*
* We need to yield if a thread change of state caused a
* higher priority thread to become ready, or if a
* scheduling signal occurred while preemption was disabled.
*/
if ((((pthread = PTHREAD_PRIOQ_FIRST) != NULL) &&
(pthread->active_priority > _thread_run->active_priority)) ||
(_thread_run->yield_on_sched_undefer != 0)) {
_thread_run->yield_on_sched_undefer = 0;
need_resched = 1;
}
}
if (_thread_run->sched_defer_count > 0) {
/* Decrement the scheduling deferral count. */
_thread_run->sched_defer_count--;
/* Yield the CPU if necessary: */
if (need_resched)
_thread_kern_sched(NULL);
}
}
static inline void
thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in)
{
pthread_t tid_out = thread_out;
pthread_t tid_in = thread_in;
if ((tid_out != NULL) &&
(tid_out->flags & PTHREAD_FLAGS_PRIVATE != 0))
tid_out = NULL;
if ((tid_in != NULL) &&
(tid_in->flags & PTHREAD_FLAGS_PRIVATE != 0))
tid_in = NULL;
if ((_sched_switch_hook != NULL) && (tid_out != tid_in)) {
/* Run the scheduler switch hook: */
_sched_switch_hook(tid_out, tid_in);
}
}
#endif

View file

@ -52,6 +52,13 @@ pthread_kill(pthread_t pthread, int sig)
/* Find the thread in the list of active threads: */
else if ((ret = _find_thread(pthread)) == 0) {
/*
* Guard against preemption by a scheduling signal.
* A change of thread state modifies the waiting
* and priority queues.
*/
_thread_kern_sched_defer();
switch (pthread->state) {
case PS_SIGSUSPEND:
/*
@ -108,6 +115,12 @@ pthread_kill(pthread_t pthread, int sig)
sigaddset(&pthread->sigpend,sig);
break;
}
/*
* Reenable preemption and yield if a scheduling signal
* occurred while in the critical region.
*/
_thread_kern_sched_undefer();
}
/* Return the completion status: */

View file

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_mutexattr_getprioceiling(pthread_mutexattr_t *mattr, int *prioceiling)
{
int ret = 0;
if ((mattr == NULL) || (*mattr == NULL))
ret = EINVAL;
else if ((*mattr)->m_protocol != PTHREAD_PRIO_PROTECT)
ret = EINVAL;
else
*prioceiling = (*mattr)->m_ceiling;
return(ret);
}
int
pthread_mutexattr_setprioceiling(pthread_mutexattr_t *mattr, int prioceiling)
{
int ret = 0;
if ((mattr == NULL) || (*mattr == NULL))
ret = EINVAL;
else if ((*mattr)->m_protocol != PTHREAD_PRIO_PROTECT)
ret = EINVAL;
else
(*mattr)->m_ceiling = prioceiling;
return(ret);
}
int
pthread_mutex_getprioceiling(pthread_mutex_t *mutex,
int *prioceiling)
{
int ret;
if ((mutex == NULL) || (*mutex == NULL))
ret = EINVAL;
else if ((*mutex)->m_protocol != PTHREAD_PRIO_PROTECT)
ret = EINVAL;
else
ret = (*mutex)->m_prio;
return(ret);
}
int
pthread_mutex_setprioceiling(pthread_mutex_t *mutex,
int prioceiling, int *old_ceiling)
{
int ret = 0;
if ((mutex == NULL) || (*mutex == NULL))
ret = EINVAL;
else if ((*mutex)->m_protocol != PTHREAD_PRIO_PROTECT)
ret = EINVAL;
else {
/* Lock the mutex: */
if ((ret = pthread_mutex_lock(mutex)) == 0) {
/* Return the old ceiling and set the new ceiling: */
*old_ceiling = (*mutex)->m_prio;
(*mutex)->m_prio = prioceiling;
/* Unlock the mutex: */
ret = pthread_mutex_unlock(mutex);
}
}
return(ret);
}
#endif

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_mutexattr_getprotocol(pthread_mutexattr_t *mattr, int *protocol)
{
int ret = 0;
if ((mattr == NULL) || (*mattr == NULL))
ret = EINVAL;
else
*protocol = (*mattr)->m_protocol;
return(ret);
}
int
pthread_mutexattr_setprotocol(pthread_mutexattr_t *mattr, int protocol)
{
int ret = 0;
if ((mattr == NULL) || (*mattr == NULL) ||
(protocol < PTHREAD_PRIO_NONE) || (protocol > PTHREAD_PRIO_PROTECT))
ret = EINVAL;
else {
(*mattr)->m_protocol = protocol;
(*mattr)->m_ceiling = PTHREAD_MAX_PRIORITY;
}
return(ret);
}
#endif

View file

@ -0,0 +1,155 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <stdlib.h>
#include <sys/queue.h>
#include <string.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
/* Prototypes: */
static void pq_insert_prio_list(pq_queue_t *pq, int prio);
int
_pq_init(pq_queue_t *pq, int minprio, int maxprio)
{
int i, ret = 0;
int prioslots = maxprio - minprio + 1;
if (pq == NULL)
ret = -1;
/* Create the priority queue with (maxprio - minprio + 1) slots: */
else if ((pq->pq_lists =
(pq_list_t *) malloc(sizeof(pq_list_t) * prioslots)) == NULL)
ret = -1;
else {
/* Initialize the queue for each priority slot: */
for (i = 0; i < prioslots; i++) {
TAILQ_INIT(&pq->pq_lists[i].pl_head);
pq->pq_lists[i].pl_prio = i;
pq->pq_lists[i].pl_queued = 0;
}
/* Initialize the priority queue: */
TAILQ_INIT(&pq->pq_queue);
/* Remember the queue size: */
pq->pq_size = prioslots;
}
return (ret);
}
void
_pq_remove(pq_queue_t *pq, pthread_t pthread)
{
int prio = pthread->active_priority;
TAILQ_REMOVE(&pq->pq_lists[prio].pl_head, pthread, pqe);
}
void
_pq_insert_head(pq_queue_t *pq, pthread_t pthread)
{
int prio = pthread->active_priority;
TAILQ_INSERT_HEAD(&pq->pq_lists[prio].pl_head, pthread, pqe);
if (pq->pq_lists[prio].pl_queued == 0)
/* Insert the list into the priority queue: */
pq_insert_prio_list(pq, prio);
}
void
_pq_insert_tail(pq_queue_t *pq, pthread_t pthread)
{
int prio = pthread->active_priority;
TAILQ_INSERT_TAIL(&pq->pq_lists[prio].pl_head, pthread, pqe);
if (pq->pq_lists[prio].pl_queued == 0)
/* Insert the list into the priority queue: */
pq_insert_prio_list(pq, prio);
}
pthread_t
_pq_first(pq_queue_t *pq)
{
pq_list_t *pql;
pthread_t pthread = NULL;
while (((pql = TAILQ_FIRST(&pq->pq_queue)) != NULL) &&
(pthread == NULL)) {
if ((pthread = TAILQ_FIRST(&pql->pl_head)) == NULL) {
/*
* The priority list is empty; remove the list
* from the queue.
*/
TAILQ_REMOVE(&pq->pq_queue, pql, pl_link);
/* Mark the list as not being in the queue: */
pql->pl_queued = 0;
}
}
return (pthread);
}
static void
pq_insert_prio_list(pq_queue_t *pq, int prio)
{
pq_list_t *pql;
/*
* The priority queue is in descending priority order. Start at
* the beginning of the queue and find the list before which the
* new list should to be inserted.
*/
pql = TAILQ_FIRST(&pq->pq_queue);
while ((pql != NULL) && (pql->pl_prio > prio))
pql = TAILQ_NEXT(pql, pl_link);
/* Insert the list: */
if (pql == NULL)
TAILQ_INSERT_TAIL(&pq->pq_queue, &pq->pq_lists[prio], pl_link);
else
TAILQ_INSERT_BEFORE(pql, &pq->pq_lists[prio], pl_link);
/* Mark this list as being in the queue: */
pq->pq_lists[prio].pl_queued = 1;
}
#endif

View file

@ -45,8 +45,21 @@ pthread_resume_np(pthread_t thread)
if ((ret = _find_thread(thread)) == 0) {
/* The thread exists. Is it suspended? */
if (thread->state != PS_SUSPENDED) {
/*
* Guard against preemption by a scheduling signal.
* A change of thread state modifies the waiting
* and priority queues.
*/
_thread_kern_sched_defer();
/* Allow the thread to run. */
PTHREAD_NEW_STATE(thread,PS_RUNNING);
/*
* Reenable preemption and yield if a scheduling
* signal occurred while in the critical region.
*/
_thread_kern_sched_undefer();
}
}
return(ret);

View file

@ -35,6 +35,7 @@
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/fcntl.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
@ -47,6 +48,7 @@ select(int numfds, fd_set * readfds, fd_set * writefds,
struct timespec ts;
struct timeval zero_timeout = {0, 0};
int i, ret = 0, got_all_locks = 1;
int f_wait = 1;
struct pthread_select_data data;
if (numfds > _thread_dtablesize) {
@ -59,6 +61,8 @@ select(int numfds, fd_set * readfds, fd_set * writefds,
/* Set the wake up time: */
_thread_kern_set_timeout(&ts);
if (ts.tv_sec == 0 && ts.tv_nsec == 0)
f_wait = 0;
} else {
/* Wait for ever: */
_thread_kern_set_timeout(NULL);
@ -110,7 +114,7 @@ select(int numfds, fd_set * readfds, fd_set * writefds,
if (exceptfds != NULL) {
memcpy(&data.exceptfds, exceptfds, sizeof(data.exceptfds));
}
if ((ret = _thread_sys_select(data.nfds, &data.readfds, &data.writefds, &data.exceptfds, &zero_timeout)) == 0) {
if ((ret = _thread_sys_select(data.nfds, &data.readfds, &data.writefds, &data.exceptfds, &zero_timeout)) == 0 && f_wait) {
data.nfds = numfds;
FD_ZERO(&data.readfds);
FD_ZERO(&data.writefds);

View file

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -38,17 +38,13 @@
int
pthread_setprio(pthread_t pthread, int prio)
{
int ret;
int ret, policy;
struct sched_param param;
/* Check if the priority is invalid: */
if (prio < PTHREAD_MIN_PRIORITY || prio > PTHREAD_MAX_PRIORITY)
/* Return an invalid argument error: */
ret = EINVAL;
/* Find the thread in the list of active threads: */
else if ((ret = _find_thread(pthread)) == 0)
/* Set the thread priority: */
pthread->pthread_priority = prio;
if ((ret = pthread_getschedparam(pthread, &policy, &param)) == 0) {
param.sched_priority = prio;
ret = pthread_setschedparam(pthread, policy, &param);
}
/* Return the error status: */
return (ret);

View file

@ -0,0 +1,113 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#include <sys/param.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_setschedparam(pthread_t pthread, int policy, struct sched_param *param)
{
int old_prio, in_readyq = 0, ret = 0;
if ((param == NULL) || (param->sched_priority < PTHREAD_MIN_PRIORITY) ||
(param->sched_priority > PTHREAD_MAX_PRIORITY) ||
(policy < SCHED_FIFO) || (policy > SCHED_RR))
/* Return an invalid argument error: */
ret = EINVAL;
/* Find the thread in the list of active threads: */
else if ((ret = _find_thread(pthread)) == 0) {
/*
* Guard against being preempted by a scheduling
* signal:
*/
_thread_kern_sched_defer();
if (param->sched_priority != pthread->base_priority) {
/*
* Remove the thread from its current priority
* queue before any adjustments are made to its
* active priority:
*/
if ((pthread != _thread_run) &&
(pthread->state == PS_RUNNING)) {
in_readyq = 1;
old_prio = pthread->active_priority;
PTHREAD_PRIOQ_REMOVE(pthread);
}
/* Set the thread base priority: */
pthread->base_priority = param->sched_priority;
/* Recalculate the active priority: */
pthread->active_priority = MAX(pthread->base_priority,
pthread->inherited_priority);
if (in_readyq) {
if ((pthread->priority_mutex_count > 0) &&
(old_prio > pthread->active_priority)) {
/*
* POSIX states that if the priority is
* being lowered, the thread must be
* inserted at the head of the queue for
* its priority if it owns any priority
* protection or inheritence mutexes.
*/
PTHREAD_PRIOQ_INSERT_HEAD(pthread);
}
else
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
/*
* Check for any mutex priority adjustments. This
* includes checking for a priority mutex on which
* this thread is waiting.
*/
_mutex_notify_priochange(pthread);
}
/* Set the scheduling policy: */
pthread->attr.sched_policy = policy;
/*
* Renable preemption and yield if a scheduling signal
* arrived while in the critical region:
*/
_thread_kern_sched_undefer();
}
return(ret);
}
#endif

View file

@ -38,6 +38,19 @@
#include <pthread.h>
#include "pthread_private.h"
/*
* State change macro for signal handler:
*/
#define PTHREAD_SIG_NEW_STATE(thrd, newstate) { \
if ((_thread_run->sched_defer_count == 0) && \
(_thread_kern_in_sched == 0)) { \
PTHREAD_NEW_STATE(thrd, newstate); \
} else { \
_waitingq_check_reqd = 1; \
PTHREAD_SET_STATE(thrd, newstate); \
} \
}
/* Static variables: */
static int volatile yield_on_unlock_thread = 0;
static spinlock_t thread_link_list_lock = _SPINLOCK_INITIALIZER;
@ -94,14 +107,13 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp)
*/
_thread_sys_write(_thread_kern_pipe[1], &c, 1);
}
/* Check if the signal requires a dump of thread information: */
if (sig == SIGINFO)
/* Dump thread information to file: */
_thread_dump_info();
/* Check if an interval timer signal: */
else if (sig == SIGVTALRM) {
else if (sig == _SCHED_SIGNAL) {
/* Check if the scheduler interrupt has come at an
* unfortunate time which one of the threads is
* modifying the thread list:
@ -114,6 +126,14 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp)
*/
yield_on_unlock_thread = 1;
/*
* Check if the scheduler interrupt has come when
* the currently running thread has deferred thread
* scheduling.
*/
else if (_thread_run->sched_defer_count)
_thread_run->yield_on_sched_undefer = 1;
/*
* Check if the kernel has not been interrupted while
* executing scheduler code:
@ -170,18 +190,17 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp)
}
/*
* Enter a loop to process each thread in the linked
* Enter a loop to process each thread in the waiting
* list that is sigwait-ing on a signal. Since POSIX
* doesn't specify which thread will get the signal
* if there are multiple waiters, we'll give it to the
* first one we find.
*/
for (pthread = _thread_link_list; pthread != NULL;
pthread = pthread->nxt) {
TAILQ_FOREACH(pthread, &_waitingq, pqe) {
if ((pthread->state == PS_SIGWAIT) &&
sigismember(pthread->data.sigwait, sig)) {
/* Change the state of the thread to run: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
PTHREAD_SIG_NEW_STATE(pthread,PS_RUNNING);
/* Return the signal number: */
pthread->signo = sig;
@ -201,11 +220,19 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp)
* list:
*/
for (pthread = _thread_link_list; pthread != NULL;
pthread = pthread->nxt)
pthread = pthread->nxt) {
pthread_t pthread_saved = _thread_run;
_thread_run = pthread;
_thread_signal(pthread,sig);
/* Dispatch pending signals to the running thread: */
_dispatch_signals();
/*
* Dispatch pending signals to the
* running thread:
*/
_dispatch_signals();
_thread_run = pthread_saved;
}
}
/* Returns nothing. */
@ -257,7 +284,7 @@ _thread_signal(pthread_t pthread, int sig)
pthread->interrupted = 1;
/* Change the state of the thread to run: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
PTHREAD_SIG_NEW_STATE(pthread,PS_RUNNING);
/* Return the signal number: */
pthread->signo = sig;
@ -277,7 +304,7 @@ _thread_signal(pthread_t pthread, int sig)
pthread->interrupted = 1;
/* Change the state of the thread to run: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
PTHREAD_SIG_NEW_STATE(pthread,PS_RUNNING);
/* Return the signal number: */
pthread->signo = sig;
@ -292,7 +319,7 @@ _thread_signal(pthread_t pthread, int sig)
if (!sigismember(&pthread->sigmask, sig) &&
_thread_sigact[sig - 1].sa_handler != SIG_DFL) {
/* Change the state of the thread to run: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
PTHREAD_SIG_NEW_STATE(pthread,PS_RUNNING);
/* Return the signal number: */
pthread->signo = sig;

View file

@ -71,7 +71,7 @@ sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
* Check if the kernel needs to be advised of a change
* in signal action:
*/
if (act != NULL && sig != SIGVTALRM && sig != SIGCHLD &&
if (act != NULL && sig != _SCHED_SIGNAL && sig != SIGCHLD &&
sig != SIGINFO) {
/* Initialise the global signal action structure: */
gact.sa_mask = act->sa_mask;

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 1999 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by John Birrell.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <signal.h>
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
sigpending(sigset_t * set)
{
int ret = 0;
/* Check for a null signal set pointer: */
if (set == NULL) {
/* Return an invalid argument: */
ret = EINVAL;
}
else {
*set = _thread_run->sigpend;
}
/* Return the completion status: */
return (ret);
}
#endif

View file

@ -56,7 +56,7 @@ sigwait(const sigset_t * set, int *sig)
*/
sigdelset(&act.sa_mask, SIGKILL);
sigdelset(&act.sa_mask, SIGSTOP);
sigdelset(&act.sa_mask, SIGVTALRM);
sigdelset(&act.sa_mask, _SCHED_SIGNAL);
sigdelset(&act.sa_mask, SIGCHLD);
sigdelset(&act.sa_mask, SIGINFO);

View file

@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: uthread_spinlock.c,v 1.3 1998/06/06 07:27:06 jb Exp $
* $Id: uthread_spinlock.c,v 1.4 1998/06/09 23:13:10 jb Exp $
*
*/
@ -56,12 +56,9 @@ _spinlock(spinlock_t *lck)
* it before we do.
*/
while(_atomic_lock(&lck->access_lock)) {
/* Give up the time slice: */
sched_yield();
/* Check if already locked by the running thread: */
if (lck->lock_owner == (long) _thread_run)
return;
/* Block the thread until the lock. */
_thread_run->data.spinlock = lck;
_thread_kern_sched_state(PS_SPINBLOCK, __FILE__, __LINE__);
}
/* The running thread now owns the lock: */
@ -81,24 +78,25 @@ _spinlock(spinlock_t *lck)
void
_spinlock_debug(spinlock_t *lck, char *fname, int lineno)
{
int cnt = 0;
/*
* Try to grab the lock and loop if another thread grabs
* it before we do.
*/
while(_atomic_lock(&lck->access_lock)) {
/* Give up the time slice: */
sched_yield();
/* Check if already locked by the running thread: */
if (lck->lock_owner == (long) _thread_run) {
cnt++;
if (cnt > 100) {
char str[256];
snprintf(str, sizeof(str), "%s - Warning: Thread %p attempted to lock %p from %s (%d) which it had already locked in %s (%d)\n", __progname, _thread_run, lck, fname, lineno, lck->fname, lck->lineno);
snprintf(str, sizeof(str), "%s - Warning: Thread %p attempted to lock %p from %s (%d) was left locked from %s (%d)\n", __progname, _thread_run, lck, fname, lineno, lck->fname, lck->lineno);
_thread_sys_write(2,str,strlen(str));
/* Create a thread dump to help debug this problem: */
_thread_dump_info();
return;
sleep(1);
cnt = 0;
}
/* Block the thread until the lock. */
_thread_run->data.spinlock = lck;
_thread_kern_sched_state(PS_SPINBLOCK, fname, lineno);
}
/* The running thread now owns the lock: */

View file

@ -51,8 +51,21 @@ pthread_suspend_np(pthread_t thread)
thread->interrupted = 1;
}
/*
* Guard against preemption by a scheduling signal.
* A change of thread state modifies the waiting
* and priority queues.
*/
_thread_kern_sched_defer();
/* Suspend the thread. */
PTHREAD_NEW_STATE(thread,PS_SUSPENDED);
/*
* Reenable preemption and yield if a scheduling signal
* occurred while in the critical region.
*/
_thread_kern_sched_undefer();
}
return(ret);
}

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include <pthread_np.h>
#include "pthread_private.h"
int
pthread_switch_add_np(pthread_switch_routine_t routine)
{
int ret = 0;
if (routine == NULL)
/* Return an invalid argument error: */
ret = EINVAL;
else
/* Shouldn't need a lock to protect this assigment. */
_sched_switch_hook = routine;
return(ret);
}
int
pthread_switch_delete_np(pthread_switch_routine_t routine)
{
int ret = 0;
if (routine != _sched_switch_hook)
/* Return an invalid argument error: */
ret = EINVAL;
else
/* Shouldn't need a lock to protect this assigment. */
_sched_switch_hook = NULL;
return(ret);
}
#endif

View file

@ -1,4 +1,4 @@
# $Id: Makefile.inc,v 1.15 1998/09/12 22:03:20 dt Exp $
# $Id: Makefile.inc,v 1.16 1998/09/30 06:36:55 jb Exp $
# uthread sources
.PATH: ${.CURDIR}/uthread
@ -8,10 +8,18 @@ SRCS+= \
uthread_attr_destroy.c \
uthread_attr_init.c \
uthread_attr_getdetachstate.c \
uthread_attr_getinheritsched.c \
uthread_attr_getschedparam.c \
uthread_attr_getschedpolicy.c \
uthread_attr_getscope.c \
uthread_attr_getstackaddr.c \
uthread_attr_getstacksize.c \
uthread_attr_setcreatesuspend_np.c \
uthread_attr_setdetachstate.c \
uthread_attr_setinheritsched.c \
uthread_attr_setschedparam.c \
uthread_attr_setschedpolicy.c \
uthread_attr_setscope.c \
uthread_attr_setstackaddr.c \
uthread_attr_setstacksize.c \
uthread_autoinit.cc \
@ -44,6 +52,7 @@ SRCS+= \
uthread_getdirentries.c \
uthread_getpeername.c \
uthread_getprio.c \
uthread_getschedparam.c \
uthread_getsockname.c \
uthread_getsockopt.c \
uthread_info.c \
@ -57,11 +66,14 @@ SRCS+= \
uthread_mattr_kind_np.c \
uthread_multi_np.c \
uthread_mutex.c \
uthread_mutex_prioceiling.c \
uthread_mutex_protocol.c \
uthread_mutexattr_destroy.c \
uthread_nanosleep.c \
uthread_once.c \
uthread_open.c \
uthread_pipe.c \
uthread_priority_queue.c \
uthread_queue.c \
uthread_read.c \
uthread_readv.c \
@ -76,12 +88,14 @@ SRCS+= \
uthread_sendto.c \
uthread_seterrno.c \
uthread_setprio.c \
uthread_setschedparam.c \
uthread_setsockopt.c \
uthread_shutdown.c \
uthread_sig.c \
uthread_sigaction.c \
uthread_sigblock.c \
uthread_sigmask.c \
uthread_sigpending.c \
uthread_sigprocmask.c \
uthread_sigsetmask.c \
uthread_sigsuspend.c \
@ -92,6 +106,7 @@ SRCS+= \
uthread_spec.c \
uthread_spinlock.c \
uthread_suspend_np.c \
uthread_switch_np.c \
uthread_vfork.c \
uthread_wait4.c \
uthread_write.c \

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_getinheritsched(pthread_attr_t *attr, int *sched_inherit)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL))
ret = EINVAL;
else
*sched_inherit = (*attr)->sched_inherit;
return(ret);
}
#endif

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_getschedparam(pthread_attr_t *attr, struct sched_param *param)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) || (param == NULL))
ret = EINVAL;
else
param->sched_priority = (*attr)->prio;
return(ret);
}
#endif

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_getschedpolicy(pthread_attr_t *attr, int *policy)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) || (policy == NULL))
ret = EINVAL;
else
*policy = (*attr)->sched_policy;
return(ret);
}
#endif

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_getscope(pthread_attr_t *attr, int *contentionscope)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) || (contentionscope == NULL))
/* Return an invalid argument: */
ret = EINVAL;
else
*contentionscope = (*attr)->flags & PTHREAD_SCOPE_SYSTEM ?
PTHREAD_SCOPE_SYSTEM : PTHREAD_SCOPE_PROCESS;
return(ret);
}
#endif

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_setinheritsched(pthread_attr_t *attr, int sched_inherit)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL))
ret = EINVAL;
else
(*attr)->sched_inherit = sched_inherit;
return(ret);
}
#endif

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_setschedparam(pthread_attr_t *attr, struct sched_param *param)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) || (param == NULL))
ret = EINVAL;
else
(*attr)->prio = param->sched_priority;
return(ret);
}
#endif

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) || (policy < SCHED_FIFO) ||
(policy > SCHED_RR))
ret = EINVAL;
else
(*attr)->sched_policy = policy;
return(ret);
}
#endif

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_setscope(pthread_attr_t *attr, int contentionscope)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) ||
(contentionscope != PTHREAD_SCOPE_PROCESS) ||
(contentionscope != PTHREAD_SCOPE_SYSTEM))
/* Return an invalid argument: */
ret = EINVAL;
else if (contentionscope == PTHREAD_SCOPE_SYSTEM)
/* We don't support system wide contention: */
#ifdef NOT_YET
ret = ENOTSUP;
#else
ret = EOPNOTSUPP;
#endif
else
(*attr)->flags |= contentionscope;
return(ret);
}
#endif

View file

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -37,6 +37,14 @@
#include <pthread.h>
#include "pthread_private.h"
/*
* Prototypes
*/
static inline pthread_t cond_queue_deq(pthread_cond_t);
static inline void cond_queue_remove(pthread_cond_t, pthread_t);
static inline void cond_queue_enq(pthread_cond_t, pthread_t);
int
pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
{
@ -83,9 +91,10 @@ pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
* Initialise the condition variable
* structure:
*/
_thread_queue_init(&pcond->c_queue);
TAILQ_INIT(&pcond->c_queue);
pcond->c_flags |= COND_FLAGS_INITED;
pcond->c_type = type;
pcond->c_mutex = NULL;
memset(&pcond->lock,0,sizeof(pcond->lock));
*cond = pcond;
}
@ -144,33 +153,57 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
switch ((*cond)->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
/* Wait forever: */
_thread_run->wakeup_time.tv_sec = -1;
/*
* Queue the running thread for the condition
* variable:
*/
_thread_queue_enq(&(*cond)->c_queue, _thread_run);
/* Unlock the mutex: */
if ((rval = pthread_mutex_unlock(mutex)) != 0) {
/*
* Cannot unlock the mutex, so remove the
* running thread from the condition
* variable queue:
*/
_thread_queue_deq(&(*cond)->c_queue);
if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
((*cond)->c_mutex != *mutex))) {
/* Unlock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
} else {
/* Schedule the next thread: */
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
/* Lock the mutex: */
rval = pthread_mutex_lock(mutex);
/* Return invalid argument error: */
rval = EINVAL;
} else {
/* Reset the timeout flag: */
_thread_run->timeout = 0;
/*
* Queue the running thread for the condition
* variable:
*/
cond_queue_enq(*cond, _thread_run);
/* Remember the mutex that is being used: */
(*cond)->c_mutex = *mutex;
/* Wait forever: */
_thread_run->wakeup_time.tv_sec = -1;
/* Unlock the mutex: */
if ((rval = _mutex_cv_unlock(mutex)) != 0) {
/*
* Cannot unlock the mutex, so remove
* the running thread from the condition
* variable queue:
*/
cond_queue_remove(*cond, _thread_run);
/* Check for no more waiters: */
if (TAILQ_FIRST(&(*cond)->c_queue) ==
NULL)
(*cond)->c_mutex = NULL;
/* Unlock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
}
else {
/*
* Schedule the next thread and unlock
* the condition variable structure:
*/
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
/* Lock the mutex: */
rval = _mutex_cv_lock(mutex);
}
}
break;
@ -183,7 +216,6 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
rval = EINVAL;
break;
}
}
/* Return the completion status: */
@ -213,42 +245,88 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
switch ((*cond)->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
/* Set the wakeup time: */
_thread_run->wakeup_time.tv_sec = abstime->tv_sec;
_thread_run->wakeup_time.tv_nsec = abstime->tv_nsec;
/* Reset the timeout flag: */
_thread_run->timeout = 0;
/*
* Queue the running thread for the condition
* variable:
*/
_thread_queue_enq(&(*cond)->c_queue, _thread_run);
/* Unlock the mutex: */
if ((rval = pthread_mutex_unlock(mutex)) != 0) {
/*
* Cannot unlock the mutex, so remove the
* running thread from the condition
* variable queue:
*/
_thread_queue_deq(&(*cond)->c_queue);
if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
((*cond)->c_mutex != *mutex))) {
/* Return invalid argument error: */
rval = EINVAL;
/* Unlock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
} else {
/* Schedule the next thread: */
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
/* Set the wakeup time: */
_thread_run->wakeup_time.tv_sec =
abstime->tv_sec;
_thread_run->wakeup_time.tv_nsec =
abstime->tv_nsec;
/* Lock the mutex: */
if ((rval = pthread_mutex_lock(mutex)) != 0) {
}
/* Check if the wait timed out: */
else if (_thread_run->timeout) {
/* Return a timeout error: */
rval = ETIMEDOUT;
/* Reset the timeout flag: */
_thread_run->timeout = 0;
/*
* Queue the running thread for the condition
* variable:
*/
cond_queue_enq(*cond, _thread_run);
/* Remember the mutex that is being used: */
(*cond)->c_mutex = *mutex;
/* Unlock the mutex: */
if ((rval = _mutex_cv_unlock(mutex)) != 0) {
/*
* Cannot unlock the mutex, so remove
* the running thread from the condition
* variable queue:
*/
cond_queue_remove(*cond, _thread_run);
/* Check for no more waiters: */
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
(*cond)->c_mutex = NULL;
/* Unlock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
} else {
/*
* Schedule the next thread and unlock
* the condition variable structure:
*/
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
/* Check if the wait timedout: */
if (_thread_run->timeout == 0) {
/* Lock the mutex: */
rval = _mutex_cv_lock(mutex);
}
else {
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
/*
* The wait timed out; remove
* the thread from the condition
* variable queue:
*/
cond_queue_remove(*cond,
_thread_run);
/* Check for no more waiters: */
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
(*cond)->c_mutex = NULL;
/* Unock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
/* Return a timeout error: */
rval = ETIMEDOUT;
/*
* Lock the mutex and ignore
* any errors:
*/
(void)_mutex_cv_lock(mutex);
}
}
}
break;
@ -273,7 +351,6 @@ int
pthread_cond_signal(pthread_cond_t * cond)
{
int rval = 0;
int status;
pthread_t pthread;
if (cond == NULL || *cond == NULL)
@ -286,11 +363,22 @@ pthread_cond_signal(pthread_cond_t * cond)
switch ((*cond)->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
/* Bring the next thread off the condition queue: */
if ((pthread = _thread_queue_deq(&(*cond)->c_queue)) != NULL) {
/*
* Enter a loop to dequeue threads from the condition
* queue until we find one that hasn't previously
* timed out.
*/
while (((pthread = cond_queue_deq(*cond)) != NULL) &&
(pthread->timeout != 0)) {
}
if (pthread != NULL)
/* Allow the thread to run: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
}
/* Check for no more waiters: */
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
(*cond)->c_mutex = NULL;
break;
/* Trap invalid condition variable types: */
@ -312,12 +400,21 @@ int
pthread_cond_broadcast(pthread_cond_t * cond)
{
int rval = 0;
int status;
pthread_t pthread;
if (cond == NULL || *cond == NULL)
rval = EINVAL;
else {
/*
* Guard against preemption by a scheduling signal.
* A change of thread state modifies the waiting
* and priority queues. In addition, we must assure
* that all threads currently waiting on the condition
* variable are signaled and are not timedout by a
* scheduling signal that causes a preemption.
*/
_thread_kern_sched_defer();
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
@ -329,11 +426,17 @@ pthread_cond_broadcast(pthread_cond_t * cond)
* Enter a loop to bring all threads off the
* condition queue:
*/
while ((pthread =
_thread_queue_deq(&(*cond)->c_queue)) != NULL) {
/* Allow the thread to run: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
while ((pthread = cond_queue_deq(*cond)) != NULL) {
/*
* The thread is already running if the
* timeout flag is set.
*/
if (pthread->timeout == 0)
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
}
/* There are no more waiting threads: */
(*cond)->c_mutex = NULL;
break;
/* Trap invalid condition variable types: */
@ -345,9 +448,74 @@ pthread_cond_broadcast(pthread_cond_t * cond)
/* Unlock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
/* Reenable preemption and yield if necessary.
*/
_thread_kern_sched_undefer();
}
/* Return the completion status: */
return (rval);
}
/*
* Dequeue a waiting thread from the head of a condition queue in
* descending priority order.
*/
static inline pthread_t
cond_queue_deq(pthread_cond_t cond)
{
pthread_t pthread;
if ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
TAILQ_REMOVE(&cond->c_queue, pthread, qe);
pthread->flags &= ~PTHREAD_FLAGS_QUEUED;
}
return(pthread);
}
/*
* Remove a waiting thread from a condition queue in descending priority
* order.
*/
static inline void
cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
{
/*
* Because pthread_cond_timedwait() can timeout as well
* as be signaled by another thread, it is necessary to
* guard against removing the thread from the queue if
* it isn't in the queue.
*/
if (pthread->flags & PTHREAD_FLAGS_QUEUED) {
TAILQ_REMOVE(&cond->c_queue, pthread, qe);
pthread->flags &= ~PTHREAD_FLAGS_QUEUED;
}
}
/*
* Enqueue a waiting thread to a condition queue in descending priority
* order.
*/
static inline void
cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
{
pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
/*
* 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);
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);
}
pthread->flags |= PTHREAD_FLAGS_QUEUED;
}
#endif

View file

@ -99,12 +99,6 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
*/
new_thread->magic = PTHREAD_MAGIC;
if (pattr->suspend == PTHREAD_CREATE_SUSPENDED) {
PTHREAD_NEW_STATE(new_thread,PS_SUSPENDED);
} else {
PTHREAD_NEW_STATE(new_thread,PS_RUNNING);
}
/* Initialise the thread for signals: */
new_thread->sigmask = _thread_run->sigmask;
@ -162,21 +156,26 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
*/
if (new_thread->attr.flags & PTHREAD_INHERIT_SCHED) {
/* Copy the scheduling attributes: */
new_thread->pthread_priority = _thread_run->pthread_priority;
new_thread->attr.prio = _thread_run->pthread_priority;
new_thread->attr.schedparam_policy = _thread_run->attr.schedparam_policy;
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;
} else {
/*
* Use just the thread priority, leaving the
* other scheduling attributes as their
* default values:
*/
new_thread->pthread_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;
/* Initialise the join queue for the new thread: */
_thread_queue_init(&(new_thread->join_queue));
/* Initialize the mutex queue: */
TAILQ_INIT(&new_thread->mutexq);
/* Initialise hooks in the thread structure: */
new_thread->specific_data = NULL;
new_thread->cleanup = NULL;
@ -200,6 +199,27 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Unlock the thread list: */
_unlock_thread_list();
/*
* Guard against preemption by a scheduling signal.
* A change of thread state modifies the waiting
* and priority queues.
*/
_thread_kern_sched_defer();
if (pattr->suspend == PTHREAD_CREATE_SUSPENDED) {
new_thread->state = PS_SUSPENDED;
PTHREAD_WAITQ_INSERT(new_thread);
} else {
new_thread->state = PS_RUNNING;
PTHREAD_PRIOQ_INSERT_TAIL(new_thread);
}
/*
* Reenable preemption and yield if a scheduling
* signal occurred while in the critical region.
*/
_thread_kern_sched_undefer();
/* Return a pointer to the thread structure: */
(*thread) = new_thread;

View file

@ -52,11 +52,24 @@ pthread_detach(pthread_t pthread)
/* Flag the thread as detached: */
pthread->attr.flags |= PTHREAD_DETACHED;
/*
* Guard against preemption by a scheduling signal.
* A change of thread state modifies the waiting
* and priority queues.
*/
_thread_kern_sched_defer();
/* Enter a loop to bring all threads off the join queue: */
while ((next_thread = _thread_queue_deq(&pthread->join_queue)) != NULL) {
/* Make the thread run: */
PTHREAD_NEW_STATE(next_thread,PS_RUNNING);
}
/*
* Reenable preemption and yield if a scheduling signal
* occurred while in the critical region.
*/
_thread_kern_sched_undefer();
} else
/* Return an error: */
rval = EINVAL;

View file

@ -49,7 +49,7 @@ void _exit(int status)
itimer.it_interval.tv_usec = 0;
itimer.it_value.tv_sec = 0;
itimer.it_value.tv_usec = 0;
setitimer(ITIMER_VIRTUAL, &itimer, NULL);
setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL);
/* Close the pthread kernel pipe: */
_thread_sys_close(_thread_kern_pipe[0]);
@ -127,12 +127,25 @@ pthread_exit(void *status)
/* Run the thread-specific data destructors: */
_thread_cleanupspecific();
}
/*
* Guard against preemption by a scheduling signal. A change of
* thread state modifies the waiting and priority queues.
*/
_thread_kern_sched_defer();
/* Check if there are any threads joined to this one: */
while ((pthread = _thread_queue_deq(&(_thread_run->join_queue))) != NULL) {
/* Wake the joined thread and let it detach this thread: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
}
/*
* Reenable preemption and yield if a scheduling signal
* occurred while in the critical region.
*/
_thread_kern_sched_undefer();
/*
* Lock the garbage collector mutex to ensure that the garbage
* collector is not using the dead thread list.

View file

@ -41,7 +41,7 @@
pid_t
fork(void)
{
int flags;
int i, flags;
pid_t ret;
pthread_t pthread;
pthread_t pthread_next;
@ -88,6 +88,11 @@ fork(void)
else if (_thread_sys_fcntl(_thread_kern_pipe[1], F_SETFL, flags | O_NONBLOCK) == -1) {
/* Abort this application: */
abort();
/* Initialize the ready queue: */
} else if (_pq_init(&_readyq, PTHREAD_MIN_PRIORITY,
PTHREAD_MAX_PRIORITY) != 0) {
/* Abort this application: */
PANIC("Cannot allocate priority ready queue.");
} else {
/* Point to the first thread in the list: */
pthread = _thread_link_list;
@ -119,6 +124,33 @@ fork(void)
/* Point to the next thread: */
pthread = pthread_next;
}
/* Re-init the waiting queues. */
TAILQ_INIT(&_waitingq);
/* Initialize the scheduling switch hook routine: */
_sched_switch_hook = NULL;
/* Clear out any locks in the file descriptor table: */
for (i = 0; i < _thread_dtablesize; i++) {
if (_thread_fd_table[i] != NULL) {
/* Initialise the file locks: */
memset(&_thread_fd_table[i]->lock, 0,
sizeof(_thread_fd_table[i]->lock));
_thread_fd_table[i]->r_owner = NULL;
_thread_fd_table[i]->w_owner = NULL;
_thread_fd_table[i]->r_fname = NULL;
_thread_fd_table[i]->w_fname = NULL;
_thread_fd_table[i]->r_lineno = 0;;
_thread_fd_table[i]->w_lineno = 0;;
_thread_fd_table[i]->r_lockcount = 0;;
_thread_fd_table[i]->w_lockcount = 0;;
/* Initialise the read/write queues: */
_thread_queue_init(&_thread_fd_table[i]->r_queue);
_thread_queue_init(&_thread_fd_table[i]->w_queue);
}
}
}
}

View file

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -38,12 +38,11 @@
int
pthread_getprio(pthread_t pthread)
{
int ret;
int policy, ret;
struct sched_param param;
/* Find the thread in the list of active threads: */
if ((ret = _find_thread(pthread)) == 0)
/* Get the thread priority: */
ret = pthread->pthread_priority;
if ((ret = pthread_getschedparam(pthread, &policy, &param)) == 0)
ret = param.sched_priority;
else {
/* Invalid thread: */
errno = ret;

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_getschedparam(pthread_t pthread, int *policy, struct sched_param *param)
{
int ret;
if ((param == NULL) || (policy == NULL))
/* Return an invalid argument error: */
ret = EINVAL;
/* 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;
*policy = pthread->attr.sched_policy;
}
return(ret);
}
#endif

View file

@ -60,9 +60,11 @@ static const struct s_thread_info thread_info[] = {
{PS_WAIT_WAIT , "Waiting process"},
{PS_SIGSUSPEND , "Suspended, waiting for a signal"},
{PS_SIGWAIT , "Waiting for a signal"},
{PS_SPINBLOCK , "Waiting for a spinlock"},
{PS_JOIN , "Waiting to join"},
{PS_SUSPENDED , "Suspended"},
{PS_DEAD , "Dead"},
{PS_DEADLOCK , "Deadlocked"},
{PS_STATE_MAX , "Not a real state!"}
};
@ -75,6 +77,7 @@ _thread_dump_info(void)
int j;
pthread_t pthread;
char tmpfile[128];
pq_list_t *pq_list;
for (i = 0; i < 100000; i++) {
snprintf(tmpfile, sizeof(tmpfile), "/tmp/uthread.dump.%u.%i",
@ -116,7 +119,7 @@ _thread_dump_info(void)
snprintf(s, sizeof(s),
"--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
pthread, (pthread->name == NULL) ?
"":pthread->name, pthread->pthread_priority,
"":pthread->name, pthread->base_priority,
thread_info[j].name,
pthread->fname,pthread->lineno);
_thread_sys_write(fd, s, strlen(s));
@ -167,6 +170,50 @@ _thread_dump_info(void)
}
}
/* Output a header for ready threads: */
strcpy(s, "\n\n=============\nREADY THREADS\n\n");
_thread_sys_write(fd, s, strlen(s));
/* Enter a loop to report each thread in the ready queue: */
TAILQ_FOREACH (pq_list, &_readyq.pq_queue, pl_link) {
TAILQ_FOREACH(pthread, &pq_list->pl_head, pqe) {
/* Find the state: */
for (j = 0; j < (sizeof(thread_info) /
sizeof(struct s_thread_info)) - 1; j++)
if (thread_info[j].state == pthread->state)
break;
/* Output a record for the current thread: */
snprintf(s, sizeof(s),
"--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
pthread, (pthread->name == NULL) ?
"":pthread->name, pthread->base_priority,
thread_info[j].name,
pthread->fname,pthread->lineno);
_thread_sys_write(fd, s, strlen(s));
}
}
/* Output a header for waiting threads: */
strcpy(s, "\n\n=============\nWAITING THREADS\n\n");
_thread_sys_write(fd, s, strlen(s));
/* Enter a loop to report each thread in the waiting queue: */
TAILQ_FOREACH (pthread, &_waitingq, pqe) {
/* Find the state: */
for (j = 0; j < (sizeof(thread_info) /
sizeof(struct s_thread_info)) - 1; j++)
if (thread_info[j].state == pthread->state)
break;
/* Output a record for the current thread: */
snprintf(s, sizeof(s),
"--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
pthread, (pthread->name == NULL) ?
"":pthread->name, pthread->base_priority,
thread_info[j].name,
pthread->fname,pthread->lineno);
_thread_sys_write(fd, s, strlen(s));
}
/* Check if there are no dead threads: */
if (_thread_dead == NULL) {
/* Output a record: */
@ -186,7 +233,7 @@ _thread_dump_info(void)
/* Output a record for the current thread: */
snprintf(s, sizeof(s),
"Thread %p prio %3d [%s:%d]\n",
pthread, pthread->pthread_priority,
pthread, pthread->base_priority,
pthread->fname,pthread->lineno);
_thread_sys_write(fd, s, strlen(s));
}

View file

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -147,6 +147,11 @@ _thread_init(void)
/* Abort this application: */
PANIC("Cannot get kernel write pipe flags");
}
/* Initialize the ready queue: */
else if (_pq_init(&_readyq, PTHREAD_MIN_PRIORITY, PTHREAD_MAX_PRIORITY) != 0) {
/* Abort this application: */
PANIC("Cannot allocate priority ready queue.");
}
/* Allocate memory for the thread structure of the initial thread: */
else if ((_thread_initial = (pthread_t) malloc(sizeof(struct pthread))) == NULL) {
/*
@ -157,10 +162,25 @@ _thread_init(void)
} else {
/* Zero the global kernel thread structure: */
memset(&_thread_kern_thread, 0, sizeof(struct pthread));
_thread_kern_thread.flags = PTHREAD_FLAGS_PRIVATE;
memset(_thread_initial, 0, sizeof(struct pthread));
/* Initialize the waiting queue: */
TAILQ_INIT(&_waitingq);
/* Initialize the scheduling switch hook routine: */
_sched_switch_hook = NULL;
/*
* Write a magic value to the thread structure
* to help identify valid ones:
*/
_thread_initial->magic = PTHREAD_MAGIC;
/* Default the priority of the initial thread: */
_thread_initial->pthread_priority = PTHREAD_DEFAULT_PRIORITY;
_thread_initial->base_priority = PTHREAD_DEFAULT_PRIORITY;
_thread_initial->active_priority = PTHREAD_DEFAULT_PRIORITY;
_thread_initial->inherited_priority = 0;
/* Initialise the state of the initial thread: */
_thread_initial->state = PS_RUNNING;
@ -168,7 +188,13 @@ _thread_init(void)
/* Initialise the queue: */
_thread_queue_init(&(_thread_initial->join_queue));
/* Initialize the owned mutex queue and count: */
TAILQ_INIT(&(_thread_initial->mutexq));
_thread_initial->priority_mutex_count = 0;
/* Initialise the rest of the fields: */
_thread_initial->sched_defer_count = 0;
_thread_initial->yield_on_sched_undefer = 0;
_thread_initial->specific_data = NULL;
_thread_initial->cleanup = NULL;
_thread_initial->queue = NULL;
@ -206,9 +232,9 @@ _thread_init(void)
* signals that the user-thread kernel needs. Actually
* SIGINFO isn't really needed, but it is nice to have.
*/
if (_thread_sys_sigaction(SIGVTALRM, &act, NULL) != 0 ||
_thread_sys_sigaction(SIGINFO , &act, NULL) != 0 ||
_thread_sys_sigaction(SIGCHLD , &act, NULL) != 0) {
if (_thread_sys_sigaction(_SCHED_SIGNAL, &act, NULL) != 0 ||
_thread_sys_sigaction(SIGINFO, &act, NULL) != 0 ||
_thread_sys_sigaction(SIGCHLD, &act, NULL) != 0) {
/*
* Abort this process if signal initialisation fails:
*/
@ -256,6 +282,8 @@ _thread_init(void)
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

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -53,16 +53,18 @@
static void
_thread_kern_select(int wait_reqd);
static inline void
thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in);
void
_thread_kern_sched(struct sigcontext * scp)
{
#ifndef __alpha__
char *fdata;
#endif
int prio = -1;
pthread_t pthread;
pthread_t pthread_h = NULL;
pthread_t pthread_s = NULL;
pthread_t last_thread = NULL;
struct itimerval itimer;
struct timespec ts;
struct timespec ts1;
@ -105,18 +107,21 @@ __asm__("fnsave %0": :"m"(*fdata));
*/
_thread_kern_in_sched = 0;
/*
* There might be pending signals for this thread, so
* dispatch any that aren't blocked:
*/
_dispatch_signals();
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;
/* If the currently running thread is a user thread, save it: */
if ((_thread_run->flags & PTHREAD_FLAGS_PRIVATE) == 0)
_last_user_thread = _thread_run;
/*
* Enter a the scheduling loop that finds the next thread that is
* Enter a scheduling loop that finds the next thread that is
* ready to run. This loop completes when there are no more threads
* in the global list or when a thread has its state restored by
* either a sigreturn (if the state was saved as a sigcontext) or a
@ -134,12 +139,48 @@ __asm__("fnsave %0": :"m"(*fdata));
_thread_kern_select(0);
/*
* Enter a loop to look for sleeping threads that are ready:
* Define the maximum time before a scheduling signal
* is required:
*/
for (pthread = _thread_link_list; pthread != NULL;
pthread = pthread->nxt) {
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;
/*
* Enter a loop to look for sleeping threads that are ready
* or timedout. While we're at it, also find the smallest
* timeout value for threads waiting for a time.
*/
_waitingq_check_reqd = 0; /* reset flag before loop */
TAILQ_FOREACH(pthread, &_waitingq, pqe) {
/* Check if this thread is ready: */
if (pthread->state == PS_RUNNING) {
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
/*
* Check if this thread is blocked by an
* atomic lock:
*/
else if (pthread->state == PS_SPINBLOCK) {
/*
* If the lock is available, let
* the thread run.
*/
if (pthread->data.spinlock->access_lock == 0) {
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
}
/* Check if this thread is to timeout: */
if (pthread->state == PS_COND_WAIT ||
} else if (pthread->state == PS_COND_WAIT ||
pthread->state == PS_SLEEP_WAIT ||
pthread->state == PS_FDR_WAIT ||
pthread->state == PS_FDW_WAIT ||
@ -163,9 +204,9 @@ __asm__("fnsave %0": :"m"(*fdata));
*/
if (pthread->state == PS_SELECT_WAIT) {
/*
* The select has timed out,
* so zero the file
* descriptor sets:
* The select has timed out, so
* zero the file descriptor
* sets:
*/
FD_ZERO(&pthread->data.select_data->readfds);
FD_ZERO(&pthread->data.select_data->writefds);
@ -189,12 +230,71 @@ __asm__("fnsave %0": :"m"(*fdata));
* it to be restarted:
*/
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
} else {
/*
* 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:
*/
if (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:
*/
if (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;
}
}
}
}
/* Check if there is a current thread: */
if (_thread_run != &_thread_kern_thread) {
/*
* This thread no longer needs to yield the CPU.
*/
_thread_run->yield_on_sched_undefer = 0;
/*
* Save the current time as the time that the thread
* became inactive:
@ -204,194 +304,64 @@ __asm__("fnsave %0": :"m"(*fdata));
/*
* Accumulate the number of microseconds that this
* thread has run for:
* thread has run for:
*/
if (_thread_run->slice_usec != -1) {
_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;
}
if ((_thread_run->slice_usec != -1) &&
(_thread_run->attr.sched_policy != SCHED_FIFO)) {
_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 if this thread has reached its allocated
* time slice period:
*/
if (_thread_run->slice_usec > TIMESLICE_USEC) {
/* Check for time quantum exceeded: */
if (_thread_run->slice_usec > TIMESLICE_USEC)
_thread_run->slice_usec = -1;
}
if (_thread_run->state == PS_RUNNING) {
if (_thread_run->slice_usec == -1) {
/*
* The thread exceeded its time
* quantum or it yielded the CPU;
* place it at the tail of the
* queue for its priority.
*/
PTHREAD_PRIOQ_INSERT_TAIL(_thread_run);
} else {
/*
* The thread hasn't exceeded its
* interval. Place it at the head
* of the queue for its priority.
*/
PTHREAD_PRIOQ_INSERT_HEAD(_thread_run);
}
}
else if (_thread_run->state == PS_DEAD) {
/*
* Flag the allocated time slice period as
* up:
* Don't add dead threads to the waiting
* queue, because when they're reaped, it
* will corrupt the queue.
*/
}
else {
/*
* This thread has changed state and needs
* to be placed in the waiting queue.
*/
PTHREAD_WAITQ_INSERT(_thread_run);
/* Restart the time slice: */
_thread_run->slice_usec = -1;
}
}
/* Check if an incremental priority update is required: */
if (((tv.tv_sec - kern_inc_prio_time.tv_sec) * 1000000 +
tv.tv_usec - kern_inc_prio_time.tv_usec) > INC_PRIO_USEC) {
/*
* Enter a loop to look for run-enabled threads that
* have not run since the last time that an
* incremental priority update was performed:
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
/* Check if this thread is unable to run: */
if (pthread->state != PS_RUNNING) {
}
/*
* Check if the last time that this thread
* was run (as indicated by the last time it
* became inactive) is before the time that
* the last incremental priority check was
* made:
*/
else if (timercmp(&pthread->last_inactive, &kern_inc_prio_time, <)) {
/*
* Increment the incremental priority
* for this thread in the hope that
* it will eventually get a chance to
* run:
*/
(pthread->inc_prio)++;
}
}
/* Save the new incremental priority update time: */
kern_inc_prio_time.tv_sec = tv.tv_sec;
kern_inc_prio_time.tv_usec = tv.tv_usec;
}
/*
* Enter a loop to look for the first thread of the highest
* priority that is ready to run:
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
/* Check if the current thread is unable to run: */
if (pthread->state != PS_RUNNING) {
}
/*
* Check if no run-enabled thread has been seen or if
* the current thread has a priority higher than the
* highest seen so far:
*/
else if (pthread_h == NULL || (pthread->pthread_priority + pthread->inc_prio) > prio) {
/*
* Save this thread as the highest priority
* thread seen so far:
*/
pthread_h = pthread;
prio = pthread->pthread_priority + pthread->inc_prio;
}
}
/*
* Enter a loop to look for a thread that: 1. Is run-enabled.
* 2. Has the required agregate priority. 3. Has not been
* allocated its allocated time slice. 4. Became inactive
* least recently.
* Get the highest priority thread in the ready queue.
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
/* Check if the current thread is unable to run: */
if (pthread->state != PS_RUNNING) {
/* Ignore threads that are not ready to run. */
}
pthread_h = PTHREAD_PRIOQ_FIRST;
/*
* Check if the current thread as an agregate
* priority not equal to the highest priority found
* above:
*/
else if ((pthread->pthread_priority + pthread->inc_prio) != prio) {
/*
* Ignore threads which have lower agregate
* priority.
*/
}
/*
* Check if the current thread reached its time slice
* allocation last time it ran (or if it has not run
* yet):
*/
else if (pthread->slice_usec == -1) {
}
/*
* Check if an eligible thread has not been found
* yet, or if the current thread has an inactive time
* earlier than the last one seen:
*/
else if (pthread_s == NULL || timercmp(&pthread->last_inactive, &tv1, <)) {
/*
* Save the pointer to the current thread as
* the most eligible thread seen so far:
*/
pthread_s = pthread;
/*
* Save the time that the selected thread
* became inactive:
*/
tv1.tv_sec = pthread->last_inactive.tv_sec;
tv1.tv_usec = pthread->last_inactive.tv_usec;
}
}
/*
* Check if no thread was selected according to incomplete
* time slice allocation:
*/
if (pthread_s == NULL) {
/*
* Enter a loop to look for any other thread that: 1.
* Is run-enabled. 2. Has the required agregate
* priority. 3. Became inactive least recently.
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
/*
* Check if the current thread is unable to
* run:
*/
if (pthread->state != PS_RUNNING) {
/*
* Ignore threads that are not ready
* to run.
*/
}
/*
* Check if the current thread as an agregate
* priority not equal to the highest priority
* found above:
*/
else if ((pthread->pthread_priority + pthread->inc_prio) != prio) {
/*
* Ignore threads which have lower
* agregate priority.
*/
}
/*
* Check if an eligible thread has not been
* found yet, or if the current thread has an
* inactive time earlier than the last one
* seen:
*/
else if (pthread_s == NULL || timercmp(&pthread->last_inactive, &tv1, <)) {
/*
* Save the pointer to the current
* thread as the most eligible thread
* seen so far:
*/
pthread_s = pthread;
/*
* Save the time that the selected
* thread became inactive:
*/
tv1.tv_sec = pthread->last_inactive.tv_sec;
tv1.tv_usec = pthread->last_inactive.tv_usec;
}
}
}
/* Check if there are no threads ready to run: */
if (pthread_s == NULL) {
if (pthread_h == NULL) {
/*
* Lock the pthread kernel by changing the pointer to
* the running thread to point to the global kernel
@ -406,7 +376,10 @@ __asm__("fnsave %0": :"m"(*fdata));
_thread_kern_select(1);
} else {
/* Make the selected thread the current thread: */
_thread_run = pthread_s;
_thread_run = pthread_h;
/* Remove the thread from the ready queue. */
PTHREAD_PRIOQ_REMOVE(_thread_run);
/*
* Save the current time as the time that the thread
@ -424,149 +397,22 @@ __asm__("fnsave %0": :"m"(*fdata));
/* Reset the accumulated time slice period: */
_thread_run->slice_usec = 0;
}
/*
* Reset the incremental priority now that this
* thread has been given the chance to run:
*/
_thread_run->inc_prio = 0;
/* Check if there is more than one thread: */
if (_thread_run != _thread_link_list || _thread_run->nxt != NULL) {
/*
* Define the maximum time before a SIGVTALRM
* 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;
/*
* Enter a loop to look for threads waiting
* for a time:
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
/*
* Check if this thread is to
* timeout:
*/
if (pthread->state == PS_COND_WAIT ||
pthread->state == PS_SLEEP_WAIT ||
pthread->state == PS_FDR_WAIT ||
pthread->state == PS_FDW_WAIT ||
pthread->state == PS_SELECT_WAIT) {
/*
* Check if this thread is to
* wait forever:
*/
if (pthread->wakeup_time.tv_sec == -1) {
}
/*
* Check if this thread is to
* wakeup immediately:
*/
else if (pthread->wakeup_time.tv_sec == 0 &&
pthread->wakeup_time.tv_nsec == 0) {
}
/*
* Check if the current time
* is after the wakeup time:
*/
else if ((ts.tv_sec > pthread->wakeup_time.tv_sec) ||
((ts.tv_sec == pthread->wakeup_time.tv_sec) &&
(ts.tv_nsec > pthread->wakeup_time.tv_nsec))) {
} else {
/*
* 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:
*/
if (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:
*/
if (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(&tv, &ts1);
/*
* Check if the
* thread will be
* ready sooner than
* the earliest one
* found so far:
*/
if (timercmp(&tv, &itimer.it_value, <)) {
/*
* Update the
* time
* value:
*/
itimer.it_value.tv_sec = tv.tv_sec;
itimer.it_value.tv_usec = tv.tv_usec;
}
}
}
}
/*
* Start the interval timer for the
* calculated time interval:
*/
if (setitimer(ITIMER_VIRTUAL, &itimer, NULL) != 0) {
if (setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL) != 0) {
/*
* Cannot initialise the timer, so
* abort this process:
*/
PANIC("Cannot set virtual timer");
PANIC("Cannot set scheduling timer");
}
}
/* Check if a signal context was saved: */
if (_thread_run->sig_saved == 1) {
#ifndef __alpha__
@ -579,20 +425,30 @@ __asm__("fnsave %0": :"m"(*fdata));
/* 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;
_thread_kern_in_sched = 0;
/*
* 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);
}
_thread_sys_sigreturn(&_thread_run->saved_sigcontext);
} else
} 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);
}
/* This point should not be reached. */
PANIC("Thread has returned from sigreturn or longjmp");
@ -679,7 +535,8 @@ _thread_kern_select(int wait_reqd)
* Enter a loop to process threads waiting on either file descriptors
* or times:
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
_waitingq_check_reqd = 0; /* reset flag before loop */
TAILQ_FOREACH (pthread, &_waitingq, pqe) {
/* Assume that this state does not time out: */
settimeout = 0;
@ -690,12 +547,12 @@ _thread_kern_select(int wait_reqd)
* operations or timeouts:
*/
case PS_DEAD:
case PS_DEADLOCK:
case PS_FDLR_WAIT:
case PS_FDLW_WAIT:
case PS_FILE_WAIT:
case PS_JOIN:
case PS_MUTEX_WAIT:
case PS_RUNNING:
case PS_SIGTHREAD:
case PS_SIGWAIT:
case PS_STATE_MAX:
@ -704,6 +561,16 @@ _thread_kern_select(int wait_reqd)
/* Nothing to do here. */
break;
case PS_RUNNING:
/*
* A signal occurred and made this thread ready
* while in the scheduler or while the scheduling
* queues were protected.
*/
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
break;
/* File descriptor read wait: */
case PS_FDR_WAIT:
/* Add the file descriptor to the read set: */
@ -1010,16 +877,16 @@ _thread_kern_select(int wait_reqd)
* descriptors that are flagged as available by the
* _select syscall:
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
TAILQ_FOREACH (pthread, &_waitingq, pqe) {
/* Process according to thread state: */
switch (pthread->state) {
/*
* States which do not depend on file
* descriptor I/O operations:
*/
case PS_RUNNING:
case PS_COND_WAIT:
case PS_DEAD:
case PS_DEADLOCK:
case PS_FDLR_WAIT:
case PS_FDLW_WAIT:
case PS_FILE_WAIT:
@ -1034,6 +901,15 @@ _thread_kern_select(int wait_reqd)
/* Nothing to do here. */
break;
case PS_RUNNING:
/*
* A signal occurred and made this thread
* ready while in the scheduler.
*/
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
break;
/* File descriptor read wait: */
case PS_FDR_WAIT:
/*
@ -1047,6 +923,13 @@ _thread_kern_select(int wait_reqd)
* is scheduled next:
*/
pthread->state = PS_RUNNING;
/*
* Remove it from the waiting queue
* and add it to the ready queue:
*/
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
break;
@ -1063,6 +946,13 @@ _thread_kern_select(int wait_reqd)
* scheduled next:
*/
pthread->state = PS_RUNNING;
/*
* Remove it from the waiting queue
* and add it to the ready queue:
*/
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
break;
@ -1269,6 +1159,13 @@ _thread_kern_select(int wait_reqd)
* thread to run:
*/
pthread->state = PS_RUNNING;
/*
* Remove it from the waiting queue
* and add it to the ready queue:
*/
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
break;
}
@ -1320,4 +1217,80 @@ _thread_kern_set_timeout(struct timespec * timeout)
}
return;
}
void
_thread_kern_sched_defer(void)
{
/* Allow scheduling deferral to be recursive. */
_thread_run->sched_defer_count++;
}
void
_thread_kern_sched_undefer(void)
{
pthread_t pthread;
int need_resched = 0;
/*
* Perform checks to yield only if we are about to undefer
* scheduling.
*/
if (_thread_run->sched_defer_count == 1) {
/*
* Check if the waiting queue needs to be examined for
* threads that are now ready:
*/
while (_waitingq_check_reqd != 0) {
/* Clear the flag before checking the waiting queue: */
_waitingq_check_reqd = 0;
TAILQ_FOREACH(pthread, &_waitingq, pqe) {
if (pthread->state == PS_RUNNING) {
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
}
}
/*
* We need to yield if a thread change of state caused a
* higher priority thread to become ready, or if a
* scheduling signal occurred while preemption was disabled.
*/
if ((((pthread = PTHREAD_PRIOQ_FIRST) != NULL) &&
(pthread->active_priority > _thread_run->active_priority)) ||
(_thread_run->yield_on_sched_undefer != 0)) {
_thread_run->yield_on_sched_undefer = 0;
need_resched = 1;
}
}
if (_thread_run->sched_defer_count > 0) {
/* Decrement the scheduling deferral count. */
_thread_run->sched_defer_count--;
/* Yield the CPU if necessary: */
if (need_resched)
_thread_kern_sched(NULL);
}
}
static inline void
thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in)
{
pthread_t tid_out = thread_out;
pthread_t tid_in = thread_in;
if ((tid_out != NULL) &&
(tid_out->flags & PTHREAD_FLAGS_PRIVATE != 0))
tid_out = NULL;
if ((tid_in != NULL) &&
(tid_in->flags & PTHREAD_FLAGS_PRIVATE != 0))
tid_in = NULL;
if ((_sched_switch_hook != NULL) && (tid_out != tid_in)) {
/* Run the scheduler switch hook: */
_sched_switch_hook(tid_out, tid_in);
}
}
#endif

View file

@ -52,6 +52,13 @@ pthread_kill(pthread_t pthread, int sig)
/* Find the thread in the list of active threads: */
else if ((ret = _find_thread(pthread)) == 0) {
/*
* Guard against preemption by a scheduling signal.
* A change of thread state modifies the waiting
* and priority queues.
*/
_thread_kern_sched_defer();
switch (pthread->state) {
case PS_SIGSUSPEND:
/*
@ -108,6 +115,12 @@ pthread_kill(pthread_t pthread, int sig)
sigaddset(&pthread->sigpend,sig);
break;
}
/*
* Reenable preemption and yield if a scheduling signal
* occurred while in the critical region.
*/
_thread_kern_sched_undefer();
}
/* Return the completion status: */

View file

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_mutexattr_getprioceiling(pthread_mutexattr_t *mattr, int *prioceiling)
{
int ret = 0;
if ((mattr == NULL) || (*mattr == NULL))
ret = EINVAL;
else if ((*mattr)->m_protocol != PTHREAD_PRIO_PROTECT)
ret = EINVAL;
else
*prioceiling = (*mattr)->m_ceiling;
return(ret);
}
int
pthread_mutexattr_setprioceiling(pthread_mutexattr_t *mattr, int prioceiling)
{
int ret = 0;
if ((mattr == NULL) || (*mattr == NULL))
ret = EINVAL;
else if ((*mattr)->m_protocol != PTHREAD_PRIO_PROTECT)
ret = EINVAL;
else
(*mattr)->m_ceiling = prioceiling;
return(ret);
}
int
pthread_mutex_getprioceiling(pthread_mutex_t *mutex,
int *prioceiling)
{
int ret;
if ((mutex == NULL) || (*mutex == NULL))
ret = EINVAL;
else if ((*mutex)->m_protocol != PTHREAD_PRIO_PROTECT)
ret = EINVAL;
else
ret = (*mutex)->m_prio;
return(ret);
}
int
pthread_mutex_setprioceiling(pthread_mutex_t *mutex,
int prioceiling, int *old_ceiling)
{
int ret = 0;
if ((mutex == NULL) || (*mutex == NULL))
ret = EINVAL;
else if ((*mutex)->m_protocol != PTHREAD_PRIO_PROTECT)
ret = EINVAL;
else {
/* Lock the mutex: */
if ((ret = pthread_mutex_lock(mutex)) == 0) {
/* Return the old ceiling and set the new ceiling: */
*old_ceiling = (*mutex)->m_prio;
(*mutex)->m_prio = prioceiling;
/* Unlock the mutex: */
ret = pthread_mutex_unlock(mutex);
}
}
return(ret);
}
#endif

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_mutexattr_getprotocol(pthread_mutexattr_t *mattr, int *protocol)
{
int ret = 0;
if ((mattr == NULL) || (*mattr == NULL))
ret = EINVAL;
else
*protocol = (*mattr)->m_protocol;
return(ret);
}
int
pthread_mutexattr_setprotocol(pthread_mutexattr_t *mattr, int protocol)
{
int ret = 0;
if ((mattr == NULL) || (*mattr == NULL) ||
(protocol < PTHREAD_PRIO_NONE) || (protocol > PTHREAD_PRIO_PROTECT))
ret = EINVAL;
else {
(*mattr)->m_protocol = protocol;
(*mattr)->m_ceiling = PTHREAD_MAX_PRIORITY;
}
return(ret);
}
#endif

View file

@ -0,0 +1,155 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <stdlib.h>
#include <sys/queue.h>
#include <string.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
/* Prototypes: */
static void pq_insert_prio_list(pq_queue_t *pq, int prio);
int
_pq_init(pq_queue_t *pq, int minprio, int maxprio)
{
int i, ret = 0;
int prioslots = maxprio - minprio + 1;
if (pq == NULL)
ret = -1;
/* Create the priority queue with (maxprio - minprio + 1) slots: */
else if ((pq->pq_lists =
(pq_list_t *) malloc(sizeof(pq_list_t) * prioslots)) == NULL)
ret = -1;
else {
/* Initialize the queue for each priority slot: */
for (i = 0; i < prioslots; i++) {
TAILQ_INIT(&pq->pq_lists[i].pl_head);
pq->pq_lists[i].pl_prio = i;
pq->pq_lists[i].pl_queued = 0;
}
/* Initialize the priority queue: */
TAILQ_INIT(&pq->pq_queue);
/* Remember the queue size: */
pq->pq_size = prioslots;
}
return (ret);
}
void
_pq_remove(pq_queue_t *pq, pthread_t pthread)
{
int prio = pthread->active_priority;
TAILQ_REMOVE(&pq->pq_lists[prio].pl_head, pthread, pqe);
}
void
_pq_insert_head(pq_queue_t *pq, pthread_t pthread)
{
int prio = pthread->active_priority;
TAILQ_INSERT_HEAD(&pq->pq_lists[prio].pl_head, pthread, pqe);
if (pq->pq_lists[prio].pl_queued == 0)
/* Insert the list into the priority queue: */
pq_insert_prio_list(pq, prio);
}
void
_pq_insert_tail(pq_queue_t *pq, pthread_t pthread)
{
int prio = pthread->active_priority;
TAILQ_INSERT_TAIL(&pq->pq_lists[prio].pl_head, pthread, pqe);
if (pq->pq_lists[prio].pl_queued == 0)
/* Insert the list into the priority queue: */
pq_insert_prio_list(pq, prio);
}
pthread_t
_pq_first(pq_queue_t *pq)
{
pq_list_t *pql;
pthread_t pthread = NULL;
while (((pql = TAILQ_FIRST(&pq->pq_queue)) != NULL) &&
(pthread == NULL)) {
if ((pthread = TAILQ_FIRST(&pql->pl_head)) == NULL) {
/*
* The priority list is empty; remove the list
* from the queue.
*/
TAILQ_REMOVE(&pq->pq_queue, pql, pl_link);
/* Mark the list as not being in the queue: */
pql->pl_queued = 0;
}
}
return (pthread);
}
static void
pq_insert_prio_list(pq_queue_t *pq, int prio)
{
pq_list_t *pql;
/*
* The priority queue is in descending priority order. Start at
* the beginning of the queue and find the list before which the
* new list should to be inserted.
*/
pql = TAILQ_FIRST(&pq->pq_queue);
while ((pql != NULL) && (pql->pl_prio > prio))
pql = TAILQ_NEXT(pql, pl_link);
/* Insert the list: */
if (pql == NULL)
TAILQ_INSERT_TAIL(&pq->pq_queue, &pq->pq_lists[prio], pl_link);
else
TAILQ_INSERT_BEFORE(pql, &pq->pq_lists[prio], pl_link);
/* Mark this list as being in the queue: */
pq->pq_lists[prio].pl_queued = 1;
}
#endif

View file

@ -55,6 +55,7 @@
#include <sys/time.h>
#include <sched.h>
#include <spinlock.h>
#include <pthread_np.h>
/*
* Kernel fatal error handler macro.
@ -65,15 +66,58 @@
#define stdout_debug(_x) _thread_sys_write(1,_x,strlen(_x));
#define stderr_debug(_x) _thread_sys_write(2,_x,strlen(_x));
/*
* State change macro:
* Priority queue manipulation macros:
*/
#define PTHREAD_NEW_STATE(thrd, newstate) { \
#define PTHREAD_PRIOQ_INSERT_HEAD(thrd) _pq_insert_head(&_readyq,thrd)
#define PTHREAD_PRIOQ_INSERT_TAIL(thrd) _pq_insert_tail(&_readyq,thrd)
#define PTHREAD_PRIOQ_REMOVE(thrd) _pq_remove(&_readyq,thrd)
#define PTHREAD_PRIOQ_FIRST _pq_first(&_readyq)
/*
* Waiting queue manipulation macros:
*/
#define PTHREAD_WAITQ_INSERT(thrd) TAILQ_INSERT_TAIL(&_waitingq,thrd,pqe)
#define PTHREAD_WAITQ_REMOVE(thrd) TAILQ_REMOVE(&_waitingq,thrd,pqe)
/*
* State change macro without scheduling queue change:
*/
#define PTHREAD_SET_STATE(thrd, newstate) { \
(thrd)->state = newstate; \
(thrd)->fname = __FILE__; \
(thrd)->lineno = __LINE__; \
}
/*
* State change macro with scheduling queue change - This must be
* called with preemption deferred (see thread_kern_sched_[un]defer).
*/
#define PTHREAD_NEW_STATE(thrd, newstate) { \
if ((thrd)->state != newstate) { \
if ((thrd)->state == PS_RUNNING) { \
PTHREAD_PRIOQ_REMOVE(thrd); \
PTHREAD_WAITQ_INSERT(thrd); \
} else if (newstate == PS_RUNNING) { \
PTHREAD_WAITQ_REMOVE(thrd); \
PTHREAD_PRIOQ_INSERT_TAIL(thrd); \
} \
} \
PTHREAD_SET_STATE(thrd, newstate); \
}
/*
* Define the signals to be used for scheduling.
*/
#if defined(_PTHREADS_COMPAT_SCHED)
#define _ITIMER_SCHED_TIMER ITIMER_VIRTUAL
#define _SCHED_SIGNAL SIGVTALRM
#else
#define _ITIMER_SCHED_TIMER ITIMER_PROF
#define _SCHED_SIGNAL SIGPROF
#endif
/*
* Queue definitions.
*/
@ -83,11 +127,35 @@ struct pthread_queue {
void *q_data;
};
/*
* Priority queues.
*
* XXX It'd be nice if these were contained in uthread_priority_queue.[ch].
*/
typedef struct pq_list {
TAILQ_HEAD(, pthread) pl_head; /* list of threads at this priority */
TAILQ_ENTRY(pq_list) pl_link; /* link for queue of priority lists */
int pl_prio; /* the priority of this list */
int pl_queued; /* is this in the priority queue */
} pq_list_t;
typedef struct pq_queue {
TAILQ_HEAD(, pq_list) pq_queue; /* queue of priority lists */
pq_list_t *pq_lists; /* array of all priority lists */
int pq_size; /* number of priority lists */
} pq_queue_t;
/*
* Static queue initialization values.
*/
#define PTHREAD_QUEUE_INITIALIZER { NULL, NULL, NULL }
/*
* TailQ initialization values.
*/
#define TAILQ_INITIALIZER { NULL, NULL }
/*
* Mutex definitions.
*/
@ -98,10 +166,31 @@ union pthread_mutex_data {
struct pthread_mutex {
enum pthread_mutextype m_type;
struct pthread_queue m_queue;
int m_protocol;
TAILQ_HEAD(mutex_head, pthread) m_queue;
struct pthread *m_owner;
union pthread_mutex_data m_data;
long m_flags;
int m_refcount;
/*
* Used for priority inheritence and protection.
*
* m_prio - For priority inheritence, the highest active
* priority (threads locking the mutex inherit
* this priority). For priority protection, the
* ceiling priority of this mutex.
* m_saved_prio - mutex owners inherited priority before
* taking the mutex, restored when the owner
* unlocks the mutex.
*/
int m_prio;
int m_saved_prio;
/*
* Link for list of all mutexes a thread currently owns.
*/
TAILQ_ENTRY(pthread_mutex) m_qe;
/*
* Lock for accesses to this structure.
@ -120,11 +209,13 @@ struct pthread_mutex {
* Static mutex initialization values.
*/
#define PTHREAD_MUTEX_STATIC_INITIALIZER \
{ MUTEX_TYPE_FAST, PTHREAD_QUEUE_INITIALIZER, \
NULL, { NULL }, MUTEX_FLAGS_INITED }
{ PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, TAILQ_INITIALIZER, \
NULL, { NULL }, MUTEX_FLAGS_INITED, 0, 0, 0, TAILQ_INITIALIZER }
struct pthread_mutex_attr {
enum pthread_mutextype m_type;
int m_protocol;
int m_ceiling;
long m_flags;
};
@ -137,15 +228,16 @@ enum pthread_cond_type {
};
struct pthread_cond {
enum pthread_cond_type c_type;
struct pthread_queue c_queue;
void *c_data;
long c_flags;
enum pthread_cond_type c_type;
TAILQ_HEAD(cond_head, pthread) c_queue;
pthread_mutex_t c_mutex;
void *c_data;
long c_flags;
/*
* Lock for accesses to this structure.
*/
spinlock_t lock;
spinlock_t lock;
};
struct pthread_cond_attr {
@ -164,7 +256,8 @@ struct pthread_cond_attr {
* Static cond initialization values.
*/
#define PTHREAD_COND_STATIC_INITIALIZER \
{ COND_TYPE_FAST, PTHREAD_QUEUE_INITIALIZER, NULL, COND_FLAGS_INITED }
{ COND_TYPE_FAST, PTHREAD_QUEUE_INITIALIZER, NULL, NULL \
COND_FLAGS_INITED }
/*
* Cleanup definitions.
@ -176,7 +269,9 @@ struct pthread_cleanup {
};
struct pthread_attr {
int schedparam_policy;
int sched_policy;
int sched_inherit;
int sched_interval;
int prio;
int suspend;
int flags;
@ -254,9 +349,11 @@ enum pthread_state {
PS_WAIT_WAIT,
PS_SIGSUSPEND,
PS_SIGWAIT,
PS_SPINBLOCK,
PS_JOIN,
PS_SUSPENDED,
PS_DEAD,
PS_DEADLOCK,
PS_STATE_MAX
};
@ -300,8 +397,8 @@ struct pthread_select_data {
};
union pthread_wait_data {
pthread_mutex_t *mutex;
pthread_cond_t *cond;
pthread_mutex_t mutex;
pthread_cond_t cond;
const sigset_t *sigwait; /* Waiting on a signal in sigwait */
struct {
short fd; /* Used when thread waiting on fd */
@ -309,6 +406,7 @@ union pthread_wait_data {
char *fname; /* Source file name for debugging.*/
} fd;
struct pthread_select_data * select_data;
spinlock_t *spinlock;
};
/*
@ -419,7 +517,11 @@ struct pthread {
struct pthread_queue join_queue;
/*
* The current thread can belong to only one queue at a time.
* The current thread can belong to only one scheduling queue
* at a time (ready or waiting queue). It can also belong to
* a queue of threads waiting on mutexes or condition variables.
* Use pqe for the scheduling queue link (both ready and waiting),
* and qe for other links (mutexes and condition variables).
*
* Pointer to queue (if any) on which the current thread is waiting.
*
@ -431,8 +533,11 @@ struct pthread {
/* Pointer to next element in queue. */
struct pthread *qnxt;
/* Priority queue entry for this thread: */
TAILQ_ENTRY(pthread) pqe;
/* Queue entry for this thread: */
TAILQ_ENTRY(pthread) qe;
TAILQ_ENTRY(pthread) qe;
/* Wait data. */
union pthread_wait_data data;
@ -446,10 +551,59 @@ struct pthread {
/* Signal number when in state PS_SIGWAIT: */
int signo;
/*
* Set to non-zero when this thread has deferred thread
* scheduling. We allow for recursive deferral.
*/
int sched_defer_count;
/*
* Set to TRUE if this thread should yield after undeferring
* thread scheduling.
*/
int yield_on_sched_undefer;
/* Miscellaneous data. */
int flags;
#define PTHREAD_EXITING 0x0100
char pthread_priority;
int flags;
#define PTHREAD_FLAGS_PRIVATE 0x0001
#define PTHREAD_EXITING 0x0002
#define PTHREAD_FLAGS_QUEUED 0x0004 /* in queue (qe is used) */
#define PTHREAD_FLAGS_TRACE 0x0008
/*
* Base priority is the user setable and retrievable priority
* of the thread. It is only affected by explicit calls to
* set thread priority and upon thread creation via a thread
* attribute or default priority.
*/
char base_priority;
/*
* Inherited priority is the priority a thread inherits by
* taking a priority inheritence or protection mutex. It
* is not affected by base priority changes. Inherited
* priority defaults to and remains 0 until a mutex is taken
* that is being waited on by any other thread whose priority
* is non-zero.
*/
char inherited_priority;
/*
* Active priority is always the maximum of the threads base
* priority and inherited priority. When there is a change
* in either the real or inherited priority, the active
* priority must be recalculated.
*/
char active_priority;
/* Number of priority ceiling or protection mutexes owned. */
int priority_mutex_count;
/*
* Queue of currently owned mutexes.
*/
TAILQ_HEAD(, pthread_mutex) mutexq;
void *ret;
const void **specific_data;
int specific_data_count;
@ -475,6 +629,14 @@ SCLASS struct pthread * volatile _thread_run
;
#endif
/* Ptr to the thread structure for the last user thread to run: */
SCLASS struct pthread * volatile _last_user_thread
#ifdef GLOBAL_PTHREAD_PRIVATE
= &_thread_kern_thread;
#else
;
#endif
/*
* Ptr to the thread running in single-threaded mode or NULL if
* running multi-threaded (default POSIX behaviour).
@ -547,7 +709,7 @@ SCLASS struct pthread *_thread_initial
/* Default thread attributes: */
SCLASS struct pthread_attr pthread_attr_default
#ifdef GLOBAL_PTHREAD_PRIVATE
= { SCHED_RR, PTHREAD_DEFAULT_PRIORITY, PTHREAD_CREATE_RUNNING,
= { SCHED_RR, 0, TIMESLICE_USEC, PTHREAD_DEFAULT_PRIORITY, PTHREAD_CREATE_RUNNING,
PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL, PTHREAD_STACK_DEFAULT };
#else
;
@ -556,7 +718,7 @@ SCLASS struct pthread_attr pthread_attr_default
/* Default mutex attributes: */
SCLASS struct pthread_mutex_attr pthread_mutexattr_default
#ifdef GLOBAL_PTHREAD_PRIVATE
= { MUTEX_TYPE_FAST, 0 };
= { PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, 0, 0 };
#else
;
#endif
@ -614,6 +776,27 @@ SCLASS pthread_cond_t _gc_cond
*/
struct sigaction _thread_sigact[NSIG];
/*
* Scheduling queues:
*/
SCLASS pq_queue_t _readyq;
SCLASS TAILQ_HEAD(, pthread) _waitingq;
/* Indicates that the waitingq now has threads ready to run. */
SCLASS volatile int _waitingq_check_reqd
#ifdef GLOBAL_PTHREAD_PRIVATE
= 0
#endif
;
/* Thread switch hook. */
SCLASS pthread_switch_routine_t _sched_switch_hook
#ifdef GLOBAL_PTHREAD_PRIVATE
= NULL
#endif
;
/* Undefine the storage class specifier: */
#undef SCLASS
@ -645,6 +828,14 @@ void _lock_thread(void);
void _lock_thread_list(void);
void _unlock_thread(void);
void _unlock_thread_list(void);
int _mutex_cv_lock(pthread_mutex_t *);
int _mutex_cv_unlock(pthread_mutex_t *);
void _mutex_notify_priochange(struct pthread *);
int _pq_init(struct pq_queue *pq, int, int);
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);
void _thread_exit(char *, int, char *);
void _thread_fd_unlock(int, int);
void _thread_fd_unlock_debug(int, int, char *, int);
@ -657,6 +848,8 @@ 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(struct timespec *);
void _thread_kern_sched_defer(void);
void _thread_kern_sched_undefer(void);
void _thread_sig_handler(int, int, struct sigcontext *);
void _thread_start(void);
void _thread_start_sig_handler(void);

View file

@ -45,8 +45,21 @@ pthread_resume_np(pthread_t thread)
if ((ret = _find_thread(thread)) == 0) {
/* The thread exists. Is it suspended? */
if (thread->state != PS_SUSPENDED) {
/*
* Guard against preemption by a scheduling signal.
* A change of thread state modifies the waiting
* and priority queues.
*/
_thread_kern_sched_defer();
/* Allow the thread to run. */
PTHREAD_NEW_STATE(thread,PS_RUNNING);
/*
* Reenable preemption and yield if a scheduling
* signal occurred while in the critical region.
*/
_thread_kern_sched_undefer();
}
}
return(ret);

View file

@ -35,6 +35,7 @@
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/fcntl.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
@ -47,6 +48,7 @@ select(int numfds, fd_set * readfds, fd_set * writefds,
struct timespec ts;
struct timeval zero_timeout = {0, 0};
int i, ret = 0, got_all_locks = 1;
int f_wait = 1;
struct pthread_select_data data;
if (numfds > _thread_dtablesize) {
@ -59,6 +61,8 @@ select(int numfds, fd_set * readfds, fd_set * writefds,
/* Set the wake up time: */
_thread_kern_set_timeout(&ts);
if (ts.tv_sec == 0 && ts.tv_nsec == 0)
f_wait = 0;
} else {
/* Wait for ever: */
_thread_kern_set_timeout(NULL);
@ -110,7 +114,7 @@ select(int numfds, fd_set * readfds, fd_set * writefds,
if (exceptfds != NULL) {
memcpy(&data.exceptfds, exceptfds, sizeof(data.exceptfds));
}
if ((ret = _thread_sys_select(data.nfds, &data.readfds, &data.writefds, &data.exceptfds, &zero_timeout)) == 0) {
if ((ret = _thread_sys_select(data.nfds, &data.readfds, &data.writefds, &data.exceptfds, &zero_timeout)) == 0 && f_wait) {
data.nfds = numfds;
FD_ZERO(&data.readfds);
FD_ZERO(&data.writefds);

View file

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -38,17 +38,13 @@
int
pthread_setprio(pthread_t pthread, int prio)
{
int ret;
int ret, policy;
struct sched_param param;
/* Check if the priority is invalid: */
if (prio < PTHREAD_MIN_PRIORITY || prio > PTHREAD_MAX_PRIORITY)
/* Return an invalid argument error: */
ret = EINVAL;
/* Find the thread in the list of active threads: */
else if ((ret = _find_thread(pthread)) == 0)
/* Set the thread priority: */
pthread->pthread_priority = prio;
if ((ret = pthread_getschedparam(pthread, &policy, &param)) == 0) {
param.sched_priority = prio;
ret = pthread_setschedparam(pthread, policy, &param);
}
/* Return the error status: */
return (ret);

View file

@ -0,0 +1,113 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#include <sys/param.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_setschedparam(pthread_t pthread, int policy, struct sched_param *param)
{
int old_prio, in_readyq = 0, ret = 0;
if ((param == NULL) || (param->sched_priority < PTHREAD_MIN_PRIORITY) ||
(param->sched_priority > PTHREAD_MAX_PRIORITY) ||
(policy < SCHED_FIFO) || (policy > SCHED_RR))
/* Return an invalid argument error: */
ret = EINVAL;
/* Find the thread in the list of active threads: */
else if ((ret = _find_thread(pthread)) == 0) {
/*
* Guard against being preempted by a scheduling
* signal:
*/
_thread_kern_sched_defer();
if (param->sched_priority != pthread->base_priority) {
/*
* Remove the thread from its current priority
* queue before any adjustments are made to its
* active priority:
*/
if ((pthread != _thread_run) &&
(pthread->state == PS_RUNNING)) {
in_readyq = 1;
old_prio = pthread->active_priority;
PTHREAD_PRIOQ_REMOVE(pthread);
}
/* Set the thread base priority: */
pthread->base_priority = param->sched_priority;
/* Recalculate the active priority: */
pthread->active_priority = MAX(pthread->base_priority,
pthread->inherited_priority);
if (in_readyq) {
if ((pthread->priority_mutex_count > 0) &&
(old_prio > pthread->active_priority)) {
/*
* POSIX states that if the priority is
* being lowered, the thread must be
* inserted at the head of the queue for
* its priority if it owns any priority
* protection or inheritence mutexes.
*/
PTHREAD_PRIOQ_INSERT_HEAD(pthread);
}
else
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
/*
* Check for any mutex priority adjustments. This
* includes checking for a priority mutex on which
* this thread is waiting.
*/
_mutex_notify_priochange(pthread);
}
/* Set the scheduling policy: */
pthread->attr.sched_policy = policy;
/*
* Renable preemption and yield if a scheduling signal
* arrived while in the critical region:
*/
_thread_kern_sched_undefer();
}
return(ret);
}
#endif

View file

@ -38,6 +38,19 @@
#include <pthread.h>
#include "pthread_private.h"
/*
* State change macro for signal handler:
*/
#define PTHREAD_SIG_NEW_STATE(thrd, newstate) { \
if ((_thread_run->sched_defer_count == 0) && \
(_thread_kern_in_sched == 0)) { \
PTHREAD_NEW_STATE(thrd, newstate); \
} else { \
_waitingq_check_reqd = 1; \
PTHREAD_SET_STATE(thrd, newstate); \
} \
}
/* Static variables: */
static int volatile yield_on_unlock_thread = 0;
static spinlock_t thread_link_list_lock = _SPINLOCK_INITIALIZER;
@ -94,14 +107,13 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp)
*/
_thread_sys_write(_thread_kern_pipe[1], &c, 1);
}
/* Check if the signal requires a dump of thread information: */
if (sig == SIGINFO)
/* Dump thread information to file: */
_thread_dump_info();
/* Check if an interval timer signal: */
else if (sig == SIGVTALRM) {
else if (sig == _SCHED_SIGNAL) {
/* Check if the scheduler interrupt has come at an
* unfortunate time which one of the threads is
* modifying the thread list:
@ -114,6 +126,14 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp)
*/
yield_on_unlock_thread = 1;
/*
* Check if the scheduler interrupt has come when
* the currently running thread has deferred thread
* scheduling.
*/
else if (_thread_run->sched_defer_count)
_thread_run->yield_on_sched_undefer = 1;
/*
* Check if the kernel has not been interrupted while
* executing scheduler code:
@ -170,18 +190,17 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp)
}
/*
* Enter a loop to process each thread in the linked
* Enter a loop to process each thread in the waiting
* list that is sigwait-ing on a signal. Since POSIX
* doesn't specify which thread will get the signal
* if there are multiple waiters, we'll give it to the
* first one we find.
*/
for (pthread = _thread_link_list; pthread != NULL;
pthread = pthread->nxt) {
TAILQ_FOREACH(pthread, &_waitingq, pqe) {
if ((pthread->state == PS_SIGWAIT) &&
sigismember(pthread->data.sigwait, sig)) {
/* Change the state of the thread to run: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
PTHREAD_SIG_NEW_STATE(pthread,PS_RUNNING);
/* Return the signal number: */
pthread->signo = sig;
@ -201,11 +220,19 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp)
* list:
*/
for (pthread = _thread_link_list; pthread != NULL;
pthread = pthread->nxt)
pthread = pthread->nxt) {
pthread_t pthread_saved = _thread_run;
_thread_run = pthread;
_thread_signal(pthread,sig);
/* Dispatch pending signals to the running thread: */
_dispatch_signals();
/*
* Dispatch pending signals to the
* running thread:
*/
_dispatch_signals();
_thread_run = pthread_saved;
}
}
/* Returns nothing. */
@ -257,7 +284,7 @@ _thread_signal(pthread_t pthread, int sig)
pthread->interrupted = 1;
/* Change the state of the thread to run: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
PTHREAD_SIG_NEW_STATE(pthread,PS_RUNNING);
/* Return the signal number: */
pthread->signo = sig;
@ -277,7 +304,7 @@ _thread_signal(pthread_t pthread, int sig)
pthread->interrupted = 1;
/* Change the state of the thread to run: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
PTHREAD_SIG_NEW_STATE(pthread,PS_RUNNING);
/* Return the signal number: */
pthread->signo = sig;
@ -292,7 +319,7 @@ _thread_signal(pthread_t pthread, int sig)
if (!sigismember(&pthread->sigmask, sig) &&
_thread_sigact[sig - 1].sa_handler != SIG_DFL) {
/* Change the state of the thread to run: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
PTHREAD_SIG_NEW_STATE(pthread,PS_RUNNING);
/* Return the signal number: */
pthread->signo = sig;

View file

@ -71,7 +71,7 @@ sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
* Check if the kernel needs to be advised of a change
* in signal action:
*/
if (act != NULL && sig != SIGVTALRM && sig != SIGCHLD &&
if (act != NULL && sig != _SCHED_SIGNAL && sig != SIGCHLD &&
sig != SIGINFO) {
/* Initialise the global signal action structure: */
gact.sa_mask = act->sa_mask;

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 1999 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by John Birrell.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <signal.h>
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
sigpending(sigset_t * set)
{
int ret = 0;
/* Check for a null signal set pointer: */
if (set == NULL) {
/* Return an invalid argument: */
ret = EINVAL;
}
else {
*set = _thread_run->sigpend;
}
/* Return the completion status: */
return (ret);
}
#endif

View file

@ -56,7 +56,7 @@ sigwait(const sigset_t * set, int *sig)
*/
sigdelset(&act.sa_mask, SIGKILL);
sigdelset(&act.sa_mask, SIGSTOP);
sigdelset(&act.sa_mask, SIGVTALRM);
sigdelset(&act.sa_mask, _SCHED_SIGNAL);
sigdelset(&act.sa_mask, SIGCHLD);
sigdelset(&act.sa_mask, SIGINFO);

View file

@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: uthread_spinlock.c,v 1.3 1998/06/06 07:27:06 jb Exp $
* $Id: uthread_spinlock.c,v 1.4 1998/06/09 23:13:10 jb Exp $
*
*/
@ -56,12 +56,9 @@ _spinlock(spinlock_t *lck)
* it before we do.
*/
while(_atomic_lock(&lck->access_lock)) {
/* Give up the time slice: */
sched_yield();
/* Check if already locked by the running thread: */
if (lck->lock_owner == (long) _thread_run)
return;
/* Block the thread until the lock. */
_thread_run->data.spinlock = lck;
_thread_kern_sched_state(PS_SPINBLOCK, __FILE__, __LINE__);
}
/* The running thread now owns the lock: */
@ -81,24 +78,25 @@ _spinlock(spinlock_t *lck)
void
_spinlock_debug(spinlock_t *lck, char *fname, int lineno)
{
int cnt = 0;
/*
* Try to grab the lock and loop if another thread grabs
* it before we do.
*/
while(_atomic_lock(&lck->access_lock)) {
/* Give up the time slice: */
sched_yield();
/* Check if already locked by the running thread: */
if (lck->lock_owner == (long) _thread_run) {
cnt++;
if (cnt > 100) {
char str[256];
snprintf(str, sizeof(str), "%s - Warning: Thread %p attempted to lock %p from %s (%d) which it had already locked in %s (%d)\n", __progname, _thread_run, lck, fname, lineno, lck->fname, lck->lineno);
snprintf(str, sizeof(str), "%s - Warning: Thread %p attempted to lock %p from %s (%d) was left locked from %s (%d)\n", __progname, _thread_run, lck, fname, lineno, lck->fname, lck->lineno);
_thread_sys_write(2,str,strlen(str));
/* Create a thread dump to help debug this problem: */
_thread_dump_info();
return;
sleep(1);
cnt = 0;
}
/* Block the thread until the lock. */
_thread_run->data.spinlock = lck;
_thread_kern_sched_state(PS_SPINBLOCK, fname, lineno);
}
/* The running thread now owns the lock: */

View file

@ -51,8 +51,21 @@ pthread_suspend_np(pthread_t thread)
thread->interrupted = 1;
}
/*
* Guard against preemption by a scheduling signal.
* A change of thread state modifies the waiting
* and priority queues.
*/
_thread_kern_sched_defer();
/* Suspend the thread. */
PTHREAD_NEW_STATE(thread,PS_SUSPENDED);
/*
* Reenable preemption and yield if a scheduling signal
* occurred while in the critical region.
*/
_thread_kern_sched_undefer();
}
return(ret);
}

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include <pthread_np.h>
#include "pthread_private.h"
int
pthread_switch_add_np(pthread_switch_routine_t routine)
{
int ret = 0;
if (routine == NULL)
/* Return an invalid argument error: */
ret = EINVAL;
else
/* Shouldn't need a lock to protect this assigment. */
_sched_switch_hook = routine;
return(ret);
}
int
pthread_switch_delete_np(pthread_switch_routine_t routine)
{
int ret = 0;
if (routine != _sched_switch_hook)
/* Return an invalid argument error: */
ret = EINVAL;
else
/* Shouldn't need a lock to protect this assigment. */
_sched_switch_hook = NULL;
return(ret);
}
#endif

View file

@ -1,4 +1,4 @@
# $Id: Makefile.inc,v 1.15 1998/09/12 22:03:20 dt Exp $
# $Id: Makefile.inc,v 1.16 1998/09/30 06:36:55 jb Exp $
# uthread sources
.PATH: ${.CURDIR}/uthread
@ -8,10 +8,18 @@ SRCS+= \
uthread_attr_destroy.c \
uthread_attr_init.c \
uthread_attr_getdetachstate.c \
uthread_attr_getinheritsched.c \
uthread_attr_getschedparam.c \
uthread_attr_getschedpolicy.c \
uthread_attr_getscope.c \
uthread_attr_getstackaddr.c \
uthread_attr_getstacksize.c \
uthread_attr_setcreatesuspend_np.c \
uthread_attr_setdetachstate.c \
uthread_attr_setinheritsched.c \
uthread_attr_setschedparam.c \
uthread_attr_setschedpolicy.c \
uthread_attr_setscope.c \
uthread_attr_setstackaddr.c \
uthread_attr_setstacksize.c \
uthread_autoinit.cc \
@ -44,6 +52,7 @@ SRCS+= \
uthread_getdirentries.c \
uthread_getpeername.c \
uthread_getprio.c \
uthread_getschedparam.c \
uthread_getsockname.c \
uthread_getsockopt.c \
uthread_info.c \
@ -57,11 +66,14 @@ SRCS+= \
uthread_mattr_kind_np.c \
uthread_multi_np.c \
uthread_mutex.c \
uthread_mutex_prioceiling.c \
uthread_mutex_protocol.c \
uthread_mutexattr_destroy.c \
uthread_nanosleep.c \
uthread_once.c \
uthread_open.c \
uthread_pipe.c \
uthread_priority_queue.c \
uthread_queue.c \
uthread_read.c \
uthread_readv.c \
@ -76,12 +88,14 @@ SRCS+= \
uthread_sendto.c \
uthread_seterrno.c \
uthread_setprio.c \
uthread_setschedparam.c \
uthread_setsockopt.c \
uthread_shutdown.c \
uthread_sig.c \
uthread_sigaction.c \
uthread_sigblock.c \
uthread_sigmask.c \
uthread_sigpending.c \
uthread_sigprocmask.c \
uthread_sigsetmask.c \
uthread_sigsuspend.c \
@ -92,6 +106,7 @@ SRCS+= \
uthread_spec.c \
uthread_spinlock.c \
uthread_suspend_np.c \
uthread_switch_np.c \
uthread_vfork.c \
uthread_wait4.c \
uthread_write.c \

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_getinheritsched(pthread_attr_t *attr, int *sched_inherit)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL))
ret = EINVAL;
else
*sched_inherit = (*attr)->sched_inherit;
return(ret);
}
#endif

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_getschedparam(pthread_attr_t *attr, struct sched_param *param)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) || (param == NULL))
ret = EINVAL;
else
param->sched_priority = (*attr)->prio;
return(ret);
}
#endif

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_getschedpolicy(pthread_attr_t *attr, int *policy)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) || (policy == NULL))
ret = EINVAL;
else
*policy = (*attr)->sched_policy;
return(ret);
}
#endif

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_getscope(pthread_attr_t *attr, int *contentionscope)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) || (contentionscope == NULL))
/* Return an invalid argument: */
ret = EINVAL;
else
*contentionscope = (*attr)->flags & PTHREAD_SCOPE_SYSTEM ?
PTHREAD_SCOPE_SYSTEM : PTHREAD_SCOPE_PROCESS;
return(ret);
}
#endif

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_setinheritsched(pthread_attr_t *attr, int sched_inherit)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL))
ret = EINVAL;
else
(*attr)->sched_inherit = sched_inherit;
return(ret);
}
#endif

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_setschedparam(pthread_attr_t *attr, struct sched_param *param)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) || (param == NULL))
ret = EINVAL;
else
(*attr)->prio = param->sched_priority;
return(ret);
}
#endif

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) || (policy < SCHED_FIFO) ||
(policy > SCHED_RR))
ret = EINVAL;
else
(*attr)->sched_policy = policy;
return(ret);
}
#endif

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_attr_setscope(pthread_attr_t *attr, int contentionscope)
{
int ret = 0;
if ((attr == NULL) || (*attr == NULL) ||
(contentionscope != PTHREAD_SCOPE_PROCESS) ||
(contentionscope != PTHREAD_SCOPE_SYSTEM))
/* Return an invalid argument: */
ret = EINVAL;
else if (contentionscope == PTHREAD_SCOPE_SYSTEM)
/* We don't support system wide contention: */
#ifdef NOT_YET
ret = ENOTSUP;
#else
ret = EOPNOTSUPP;
#endif
else
(*attr)->flags |= contentionscope;
return(ret);
}
#endif

View file

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -37,6 +37,14 @@
#include <pthread.h>
#include "pthread_private.h"
/*
* Prototypes
*/
static inline pthread_t cond_queue_deq(pthread_cond_t);
static inline void cond_queue_remove(pthread_cond_t, pthread_t);
static inline void cond_queue_enq(pthread_cond_t, pthread_t);
int
pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
{
@ -83,9 +91,10 @@ pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
* Initialise the condition variable
* structure:
*/
_thread_queue_init(&pcond->c_queue);
TAILQ_INIT(&pcond->c_queue);
pcond->c_flags |= COND_FLAGS_INITED;
pcond->c_type = type;
pcond->c_mutex = NULL;
memset(&pcond->lock,0,sizeof(pcond->lock));
*cond = pcond;
}
@ -144,33 +153,57 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
switch ((*cond)->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
/* Wait forever: */
_thread_run->wakeup_time.tv_sec = -1;
/*
* Queue the running thread for the condition
* variable:
*/
_thread_queue_enq(&(*cond)->c_queue, _thread_run);
/* Unlock the mutex: */
if ((rval = pthread_mutex_unlock(mutex)) != 0) {
/*
* Cannot unlock the mutex, so remove the
* running thread from the condition
* variable queue:
*/
_thread_queue_deq(&(*cond)->c_queue);
if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
((*cond)->c_mutex != *mutex))) {
/* Unlock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
} else {
/* Schedule the next thread: */
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
/* Lock the mutex: */
rval = pthread_mutex_lock(mutex);
/* Return invalid argument error: */
rval = EINVAL;
} else {
/* Reset the timeout flag: */
_thread_run->timeout = 0;
/*
* Queue the running thread for the condition
* variable:
*/
cond_queue_enq(*cond, _thread_run);
/* Remember the mutex that is being used: */
(*cond)->c_mutex = *mutex;
/* Wait forever: */
_thread_run->wakeup_time.tv_sec = -1;
/* Unlock the mutex: */
if ((rval = _mutex_cv_unlock(mutex)) != 0) {
/*
* Cannot unlock the mutex, so remove
* the running thread from the condition
* variable queue:
*/
cond_queue_remove(*cond, _thread_run);
/* Check for no more waiters: */
if (TAILQ_FIRST(&(*cond)->c_queue) ==
NULL)
(*cond)->c_mutex = NULL;
/* Unlock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
}
else {
/*
* Schedule the next thread and unlock
* the condition variable structure:
*/
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
/* Lock the mutex: */
rval = _mutex_cv_lock(mutex);
}
}
break;
@ -183,7 +216,6 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
rval = EINVAL;
break;
}
}
/* Return the completion status: */
@ -213,42 +245,88 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
switch ((*cond)->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
/* Set the wakeup time: */
_thread_run->wakeup_time.tv_sec = abstime->tv_sec;
_thread_run->wakeup_time.tv_nsec = abstime->tv_nsec;
/* Reset the timeout flag: */
_thread_run->timeout = 0;
/*
* Queue the running thread for the condition
* variable:
*/
_thread_queue_enq(&(*cond)->c_queue, _thread_run);
/* Unlock the mutex: */
if ((rval = pthread_mutex_unlock(mutex)) != 0) {
/*
* Cannot unlock the mutex, so remove the
* running thread from the condition
* variable queue:
*/
_thread_queue_deq(&(*cond)->c_queue);
if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
((*cond)->c_mutex != *mutex))) {
/* Return invalid argument error: */
rval = EINVAL;
/* Unlock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
} else {
/* Schedule the next thread: */
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
/* Set the wakeup time: */
_thread_run->wakeup_time.tv_sec =
abstime->tv_sec;
_thread_run->wakeup_time.tv_nsec =
abstime->tv_nsec;
/* Lock the mutex: */
if ((rval = pthread_mutex_lock(mutex)) != 0) {
}
/* Check if the wait timed out: */
else if (_thread_run->timeout) {
/* Return a timeout error: */
rval = ETIMEDOUT;
/* Reset the timeout flag: */
_thread_run->timeout = 0;
/*
* Queue the running thread for the condition
* variable:
*/
cond_queue_enq(*cond, _thread_run);
/* Remember the mutex that is being used: */
(*cond)->c_mutex = *mutex;
/* Unlock the mutex: */
if ((rval = _mutex_cv_unlock(mutex)) != 0) {
/*
* Cannot unlock the mutex, so remove
* the running thread from the condition
* variable queue:
*/
cond_queue_remove(*cond, _thread_run);
/* Check for no more waiters: */
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
(*cond)->c_mutex = NULL;
/* Unlock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
} else {
/*
* Schedule the next thread and unlock
* the condition variable structure:
*/
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
/* Check if the wait timedout: */
if (_thread_run->timeout == 0) {
/* Lock the mutex: */
rval = _mutex_cv_lock(mutex);
}
else {
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
/*
* The wait timed out; remove
* the thread from the condition
* variable queue:
*/
cond_queue_remove(*cond,
_thread_run);
/* Check for no more waiters: */
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
(*cond)->c_mutex = NULL;
/* Unock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
/* Return a timeout error: */
rval = ETIMEDOUT;
/*
* Lock the mutex and ignore
* any errors:
*/
(void)_mutex_cv_lock(mutex);
}
}
}
break;
@ -273,7 +351,6 @@ int
pthread_cond_signal(pthread_cond_t * cond)
{
int rval = 0;
int status;
pthread_t pthread;
if (cond == NULL || *cond == NULL)
@ -286,11 +363,22 @@ pthread_cond_signal(pthread_cond_t * cond)
switch ((*cond)->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
/* Bring the next thread off the condition queue: */
if ((pthread = _thread_queue_deq(&(*cond)->c_queue)) != NULL) {
/*
* Enter a loop to dequeue threads from the condition
* queue until we find one that hasn't previously
* timed out.
*/
while (((pthread = cond_queue_deq(*cond)) != NULL) &&
(pthread->timeout != 0)) {
}
if (pthread != NULL)
/* Allow the thread to run: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
}
/* Check for no more waiters: */
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
(*cond)->c_mutex = NULL;
break;
/* Trap invalid condition variable types: */
@ -312,12 +400,21 @@ int
pthread_cond_broadcast(pthread_cond_t * cond)
{
int rval = 0;
int status;
pthread_t pthread;
if (cond == NULL || *cond == NULL)
rval = EINVAL;
else {
/*
* Guard against preemption by a scheduling signal.
* A change of thread state modifies the waiting
* and priority queues. In addition, we must assure
* that all threads currently waiting on the condition
* variable are signaled and are not timedout by a
* scheduling signal that causes a preemption.
*/
_thread_kern_sched_defer();
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
@ -329,11 +426,17 @@ pthread_cond_broadcast(pthread_cond_t * cond)
* Enter a loop to bring all threads off the
* condition queue:
*/
while ((pthread =
_thread_queue_deq(&(*cond)->c_queue)) != NULL) {
/* Allow the thread to run: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
while ((pthread = cond_queue_deq(*cond)) != NULL) {
/*
* The thread is already running if the
* timeout flag is set.
*/
if (pthread->timeout == 0)
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
}
/* There are no more waiting threads: */
(*cond)->c_mutex = NULL;
break;
/* Trap invalid condition variable types: */
@ -345,9 +448,74 @@ pthread_cond_broadcast(pthread_cond_t * cond)
/* Unlock the condition variable structure: */
_SPINUNLOCK(&(*cond)->lock);
/* Reenable preemption and yield if necessary.
*/
_thread_kern_sched_undefer();
}
/* Return the completion status: */
return (rval);
}
/*
* Dequeue a waiting thread from the head of a condition queue in
* descending priority order.
*/
static inline pthread_t
cond_queue_deq(pthread_cond_t cond)
{
pthread_t pthread;
if ((pthread = TAILQ_FIRST(&cond->c_queue)) != NULL) {
TAILQ_REMOVE(&cond->c_queue, pthread, qe);
pthread->flags &= ~PTHREAD_FLAGS_QUEUED;
}
return(pthread);
}
/*
* Remove a waiting thread from a condition queue in descending priority
* order.
*/
static inline void
cond_queue_remove(pthread_cond_t cond, pthread_t pthread)
{
/*
* Because pthread_cond_timedwait() can timeout as well
* as be signaled by another thread, it is necessary to
* guard against removing the thread from the queue if
* it isn't in the queue.
*/
if (pthread->flags & PTHREAD_FLAGS_QUEUED) {
TAILQ_REMOVE(&cond->c_queue, pthread, qe);
pthread->flags &= ~PTHREAD_FLAGS_QUEUED;
}
}
/*
* Enqueue a waiting thread to a condition queue in descending priority
* order.
*/
static inline void
cond_queue_enq(pthread_cond_t cond, pthread_t pthread)
{
pthread_t tid = TAILQ_LAST(&cond->c_queue, cond_head);
/*
* 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);
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);
}
pthread->flags |= PTHREAD_FLAGS_QUEUED;
}
#endif

View file

@ -99,12 +99,6 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
*/
new_thread->magic = PTHREAD_MAGIC;
if (pattr->suspend == PTHREAD_CREATE_SUSPENDED) {
PTHREAD_NEW_STATE(new_thread,PS_SUSPENDED);
} else {
PTHREAD_NEW_STATE(new_thread,PS_RUNNING);
}
/* Initialise the thread for signals: */
new_thread->sigmask = _thread_run->sigmask;
@ -162,21 +156,26 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
*/
if (new_thread->attr.flags & PTHREAD_INHERIT_SCHED) {
/* Copy the scheduling attributes: */
new_thread->pthread_priority = _thread_run->pthread_priority;
new_thread->attr.prio = _thread_run->pthread_priority;
new_thread->attr.schedparam_policy = _thread_run->attr.schedparam_policy;
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;
} else {
/*
* Use just the thread priority, leaving the
* other scheduling attributes as their
* default values:
*/
new_thread->pthread_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;
/* Initialise the join queue for the new thread: */
_thread_queue_init(&(new_thread->join_queue));
/* Initialize the mutex queue: */
TAILQ_INIT(&new_thread->mutexq);
/* Initialise hooks in the thread structure: */
new_thread->specific_data = NULL;
new_thread->cleanup = NULL;
@ -200,6 +199,27 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Unlock the thread list: */
_unlock_thread_list();
/*
* Guard against preemption by a scheduling signal.
* A change of thread state modifies the waiting
* and priority queues.
*/
_thread_kern_sched_defer();
if (pattr->suspend == PTHREAD_CREATE_SUSPENDED) {
new_thread->state = PS_SUSPENDED;
PTHREAD_WAITQ_INSERT(new_thread);
} else {
new_thread->state = PS_RUNNING;
PTHREAD_PRIOQ_INSERT_TAIL(new_thread);
}
/*
* Reenable preemption and yield if a scheduling
* signal occurred while in the critical region.
*/
_thread_kern_sched_undefer();
/* Return a pointer to the thread structure: */
(*thread) = new_thread;

View file

@ -52,11 +52,24 @@ pthread_detach(pthread_t pthread)
/* Flag the thread as detached: */
pthread->attr.flags |= PTHREAD_DETACHED;
/*
* Guard against preemption by a scheduling signal.
* A change of thread state modifies the waiting
* and priority queues.
*/
_thread_kern_sched_defer();
/* Enter a loop to bring all threads off the join queue: */
while ((next_thread = _thread_queue_deq(&pthread->join_queue)) != NULL) {
/* Make the thread run: */
PTHREAD_NEW_STATE(next_thread,PS_RUNNING);
}
/*
* Reenable preemption and yield if a scheduling signal
* occurred while in the critical region.
*/
_thread_kern_sched_undefer();
} else
/* Return an error: */
rval = EINVAL;

View file

@ -49,7 +49,7 @@ void _exit(int status)
itimer.it_interval.tv_usec = 0;
itimer.it_value.tv_sec = 0;
itimer.it_value.tv_usec = 0;
setitimer(ITIMER_VIRTUAL, &itimer, NULL);
setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL);
/* Close the pthread kernel pipe: */
_thread_sys_close(_thread_kern_pipe[0]);
@ -127,12 +127,25 @@ pthread_exit(void *status)
/* Run the thread-specific data destructors: */
_thread_cleanupspecific();
}
/*
* Guard against preemption by a scheduling signal. A change of
* thread state modifies the waiting and priority queues.
*/
_thread_kern_sched_defer();
/* Check if there are any threads joined to this one: */
while ((pthread = _thread_queue_deq(&(_thread_run->join_queue))) != NULL) {
/* Wake the joined thread and let it detach this thread: */
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
}
/*
* Reenable preemption and yield if a scheduling signal
* occurred while in the critical region.
*/
_thread_kern_sched_undefer();
/*
* Lock the garbage collector mutex to ensure that the garbage
* collector is not using the dead thread list.

View file

@ -41,7 +41,7 @@
pid_t
fork(void)
{
int flags;
int i, flags;
pid_t ret;
pthread_t pthread;
pthread_t pthread_next;
@ -88,6 +88,11 @@ fork(void)
else if (_thread_sys_fcntl(_thread_kern_pipe[1], F_SETFL, flags | O_NONBLOCK) == -1) {
/* Abort this application: */
abort();
/* Initialize the ready queue: */
} else if (_pq_init(&_readyq, PTHREAD_MIN_PRIORITY,
PTHREAD_MAX_PRIORITY) != 0) {
/* Abort this application: */
PANIC("Cannot allocate priority ready queue.");
} else {
/* Point to the first thread in the list: */
pthread = _thread_link_list;
@ -119,6 +124,33 @@ fork(void)
/* Point to the next thread: */
pthread = pthread_next;
}
/* Re-init the waiting queues. */
TAILQ_INIT(&_waitingq);
/* Initialize the scheduling switch hook routine: */
_sched_switch_hook = NULL;
/* Clear out any locks in the file descriptor table: */
for (i = 0; i < _thread_dtablesize; i++) {
if (_thread_fd_table[i] != NULL) {
/* Initialise the file locks: */
memset(&_thread_fd_table[i]->lock, 0,
sizeof(_thread_fd_table[i]->lock));
_thread_fd_table[i]->r_owner = NULL;
_thread_fd_table[i]->w_owner = NULL;
_thread_fd_table[i]->r_fname = NULL;
_thread_fd_table[i]->w_fname = NULL;
_thread_fd_table[i]->r_lineno = 0;;
_thread_fd_table[i]->w_lineno = 0;;
_thread_fd_table[i]->r_lockcount = 0;;
_thread_fd_table[i]->w_lockcount = 0;;
/* Initialise the read/write queues: */
_thread_queue_init(&_thread_fd_table[i]->r_queue);
_thread_queue_init(&_thread_fd_table[i]->w_queue);
}
}
}
}

View file

@ -29,7 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: uthread_gc.c,v 1.1 1998/09/30 06:36:56 jb Exp $
* $Id: uthread_gc.c,v 1.2 1998/09/30 19:17:51 dt Exp $
*
* Garbage collector thread. Frees memory allocated for dead threads.
*
@ -47,6 +47,7 @@ _thread_gc(pthread_addr_t arg)
int f_debug;
int f_done = 0;
int ret;
sigset_t mask;
pthread_t pthread;
pthread_t pthread_cln;
pthread_t pthread_nxt;
@ -54,6 +55,13 @@ _thread_gc(pthread_addr_t arg)
struct timespec abstime;
void *p_stack;
/* Block all signals */
sigfillset (&mask);
sigprocmask (SIG_BLOCK, &mask, NULL);
/* Mark this thread as a library thread (not a user thread). */
_thread_run->flags |= PTHREAD_FLAGS_PRIVATE;
/* Set a debug flag based on an environment variable. */
f_debug = (getenv("LIBC_R_DEBUG") != NULL);

View file

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -38,12 +38,11 @@
int
pthread_getprio(pthread_t pthread)
{
int ret;
int policy, ret;
struct sched_param param;
/* Find the thread in the list of active threads: */
if ((ret = _find_thread(pthread)) == 0)
/* Get the thread priority: */
ret = pthread->pthread_priority;
if ((ret = pthread_getschedparam(pthread, &policy, &param)) == 0)
ret = param.sched_priority;
else {
/* Invalid thread: */
errno = ret;

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Daniel Eischen.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
int
pthread_getschedparam(pthread_t pthread, int *policy, struct sched_param *param)
{
int ret;
if ((param == NULL) || (policy == NULL))
/* Return an invalid argument error: */
ret = EINVAL;
/* 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;
*policy = pthread->attr.sched_policy;
}
return(ret);
}
#endif

View file

@ -60,9 +60,11 @@ static const struct s_thread_info thread_info[] = {
{PS_WAIT_WAIT , "Waiting process"},
{PS_SIGSUSPEND , "Suspended, waiting for a signal"},
{PS_SIGWAIT , "Waiting for a signal"},
{PS_SPINBLOCK , "Waiting for a spinlock"},
{PS_JOIN , "Waiting to join"},
{PS_SUSPENDED , "Suspended"},
{PS_DEAD , "Dead"},
{PS_DEADLOCK , "Deadlocked"},
{PS_STATE_MAX , "Not a real state!"}
};
@ -75,6 +77,7 @@ _thread_dump_info(void)
int j;
pthread_t pthread;
char tmpfile[128];
pq_list_t *pq_list;
for (i = 0; i < 100000; i++) {
snprintf(tmpfile, sizeof(tmpfile), "/tmp/uthread.dump.%u.%i",
@ -116,7 +119,7 @@ _thread_dump_info(void)
snprintf(s, sizeof(s),
"--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
pthread, (pthread->name == NULL) ?
"":pthread->name, pthread->pthread_priority,
"":pthread->name, pthread->base_priority,
thread_info[j].name,
pthread->fname,pthread->lineno);
_thread_sys_write(fd, s, strlen(s));
@ -167,6 +170,50 @@ _thread_dump_info(void)
}
}
/* Output a header for ready threads: */
strcpy(s, "\n\n=============\nREADY THREADS\n\n");
_thread_sys_write(fd, s, strlen(s));
/* Enter a loop to report each thread in the ready queue: */
TAILQ_FOREACH (pq_list, &_readyq.pq_queue, pl_link) {
TAILQ_FOREACH(pthread, &pq_list->pl_head, pqe) {
/* Find the state: */
for (j = 0; j < (sizeof(thread_info) /
sizeof(struct s_thread_info)) - 1; j++)
if (thread_info[j].state == pthread->state)
break;
/* Output a record for the current thread: */
snprintf(s, sizeof(s),
"--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
pthread, (pthread->name == NULL) ?
"":pthread->name, pthread->base_priority,
thread_info[j].name,
pthread->fname,pthread->lineno);
_thread_sys_write(fd, s, strlen(s));
}
}
/* Output a header for waiting threads: */
strcpy(s, "\n\n=============\nWAITING THREADS\n\n");
_thread_sys_write(fd, s, strlen(s));
/* Enter a loop to report each thread in the waiting queue: */
TAILQ_FOREACH (pthread, &_waitingq, pqe) {
/* Find the state: */
for (j = 0; j < (sizeof(thread_info) /
sizeof(struct s_thread_info)) - 1; j++)
if (thread_info[j].state == pthread->state)
break;
/* Output a record for the current thread: */
snprintf(s, sizeof(s),
"--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
pthread, (pthread->name == NULL) ?
"":pthread->name, pthread->base_priority,
thread_info[j].name,
pthread->fname,pthread->lineno);
_thread_sys_write(fd, s, strlen(s));
}
/* Check if there are no dead threads: */
if (_thread_dead == NULL) {
/* Output a record: */
@ -186,7 +233,7 @@ _thread_dump_info(void)
/* Output a record for the current thread: */
snprintf(s, sizeof(s),
"Thread %p prio %3d [%s:%d]\n",
pthread, pthread->pthread_priority,
pthread, pthread->base_priority,
pthread->fname,pthread->lineno);
_thread_sys_write(fd, s, strlen(s));
}

View file

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -147,6 +147,11 @@ _thread_init(void)
/* Abort this application: */
PANIC("Cannot get kernel write pipe flags");
}
/* Initialize the ready queue: */
else if (_pq_init(&_readyq, PTHREAD_MIN_PRIORITY, PTHREAD_MAX_PRIORITY) != 0) {
/* Abort this application: */
PANIC("Cannot allocate priority ready queue.");
}
/* Allocate memory for the thread structure of the initial thread: */
else if ((_thread_initial = (pthread_t) malloc(sizeof(struct pthread))) == NULL) {
/*
@ -157,10 +162,25 @@ _thread_init(void)
} else {
/* Zero the global kernel thread structure: */
memset(&_thread_kern_thread, 0, sizeof(struct pthread));
_thread_kern_thread.flags = PTHREAD_FLAGS_PRIVATE;
memset(_thread_initial, 0, sizeof(struct pthread));
/* Initialize the waiting queue: */
TAILQ_INIT(&_waitingq);
/* Initialize the scheduling switch hook routine: */
_sched_switch_hook = NULL;
/*
* Write a magic value to the thread structure
* to help identify valid ones:
*/
_thread_initial->magic = PTHREAD_MAGIC;
/* Default the priority of the initial thread: */
_thread_initial->pthread_priority = PTHREAD_DEFAULT_PRIORITY;
_thread_initial->base_priority = PTHREAD_DEFAULT_PRIORITY;
_thread_initial->active_priority = PTHREAD_DEFAULT_PRIORITY;
_thread_initial->inherited_priority = 0;
/* Initialise the state of the initial thread: */
_thread_initial->state = PS_RUNNING;
@ -168,7 +188,13 @@ _thread_init(void)
/* Initialise the queue: */
_thread_queue_init(&(_thread_initial->join_queue));
/* Initialize the owned mutex queue and count: */
TAILQ_INIT(&(_thread_initial->mutexq));
_thread_initial->priority_mutex_count = 0;
/* Initialise the rest of the fields: */
_thread_initial->sched_defer_count = 0;
_thread_initial->yield_on_sched_undefer = 0;
_thread_initial->specific_data = NULL;
_thread_initial->cleanup = NULL;
_thread_initial->queue = NULL;
@ -206,9 +232,9 @@ _thread_init(void)
* signals that the user-thread kernel needs. Actually
* SIGINFO isn't really needed, but it is nice to have.
*/
if (_thread_sys_sigaction(SIGVTALRM, &act, NULL) != 0 ||
_thread_sys_sigaction(SIGINFO , &act, NULL) != 0 ||
_thread_sys_sigaction(SIGCHLD , &act, NULL) != 0) {
if (_thread_sys_sigaction(_SCHED_SIGNAL, &act, NULL) != 0 ||
_thread_sys_sigaction(SIGINFO, &act, NULL) != 0 ||
_thread_sys_sigaction(SIGCHLD, &act, NULL) != 0) {
/*
* Abort this process if signal initialisation fails:
*/
@ -256,6 +282,8 @@ _thread_init(void)
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

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@ -53,16 +53,18 @@
static void
_thread_kern_select(int wait_reqd);
static inline void
thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in);
void
_thread_kern_sched(struct sigcontext * scp)
{
#ifndef __alpha__
char *fdata;
#endif
int prio = -1;
pthread_t pthread;
pthread_t pthread_h = NULL;
pthread_t pthread_s = NULL;
pthread_t last_thread = NULL;
struct itimerval itimer;
struct timespec ts;
struct timespec ts1;
@ -105,18 +107,21 @@ __asm__("fnsave %0": :"m"(*fdata));
*/
_thread_kern_in_sched = 0;
/*
* There might be pending signals for this thread, so
* dispatch any that aren't blocked:
*/
_dispatch_signals();
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;
/* If the currently running thread is a user thread, save it: */
if ((_thread_run->flags & PTHREAD_FLAGS_PRIVATE) == 0)
_last_user_thread = _thread_run;
/*
* Enter a the scheduling loop that finds the next thread that is
* Enter a scheduling loop that finds the next thread that is
* ready to run. This loop completes when there are no more threads
* in the global list or when a thread has its state restored by
* either a sigreturn (if the state was saved as a sigcontext) or a
@ -134,12 +139,48 @@ __asm__("fnsave %0": :"m"(*fdata));
_thread_kern_select(0);
/*
* Enter a loop to look for sleeping threads that are ready:
* Define the maximum time before a scheduling signal
* is required:
*/
for (pthread = _thread_link_list; pthread != NULL;
pthread = pthread->nxt) {
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;
/*
* Enter a loop to look for sleeping threads that are ready
* or timedout. While we're at it, also find the smallest
* timeout value for threads waiting for a time.
*/
_waitingq_check_reqd = 0; /* reset flag before loop */
TAILQ_FOREACH(pthread, &_waitingq, pqe) {
/* Check if this thread is ready: */
if (pthread->state == PS_RUNNING) {
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
/*
* Check if this thread is blocked by an
* atomic lock:
*/
else if (pthread->state == PS_SPINBLOCK) {
/*
* If the lock is available, let
* the thread run.
*/
if (pthread->data.spinlock->access_lock == 0) {
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
}
/* Check if this thread is to timeout: */
if (pthread->state == PS_COND_WAIT ||
} else if (pthread->state == PS_COND_WAIT ||
pthread->state == PS_SLEEP_WAIT ||
pthread->state == PS_FDR_WAIT ||
pthread->state == PS_FDW_WAIT ||
@ -163,9 +204,9 @@ __asm__("fnsave %0": :"m"(*fdata));
*/
if (pthread->state == PS_SELECT_WAIT) {
/*
* The select has timed out,
* so zero the file
* descriptor sets:
* The select has timed out, so
* zero the file descriptor
* sets:
*/
FD_ZERO(&pthread->data.select_data->readfds);
FD_ZERO(&pthread->data.select_data->writefds);
@ -189,12 +230,71 @@ __asm__("fnsave %0": :"m"(*fdata));
* it to be restarted:
*/
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
} else {
/*
* 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:
*/
if (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:
*/
if (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;
}
}
}
}
/* Check if there is a current thread: */
if (_thread_run != &_thread_kern_thread) {
/*
* This thread no longer needs to yield the CPU.
*/
_thread_run->yield_on_sched_undefer = 0;
/*
* Save the current time as the time that the thread
* became inactive:
@ -204,194 +304,64 @@ __asm__("fnsave %0": :"m"(*fdata));
/*
* Accumulate the number of microseconds that this
* thread has run for:
* thread has run for:
*/
if (_thread_run->slice_usec != -1) {
_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;
}
if ((_thread_run->slice_usec != -1) &&
(_thread_run->attr.sched_policy != SCHED_FIFO)) {
_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 if this thread has reached its allocated
* time slice period:
*/
if (_thread_run->slice_usec > TIMESLICE_USEC) {
/* Check for time quantum exceeded: */
if (_thread_run->slice_usec > TIMESLICE_USEC)
_thread_run->slice_usec = -1;
}
if (_thread_run->state == PS_RUNNING) {
if (_thread_run->slice_usec == -1) {
/*
* The thread exceeded its time
* quantum or it yielded the CPU;
* place it at the tail of the
* queue for its priority.
*/
PTHREAD_PRIOQ_INSERT_TAIL(_thread_run);
} else {
/*
* The thread hasn't exceeded its
* interval. Place it at the head
* of the queue for its priority.
*/
PTHREAD_PRIOQ_INSERT_HEAD(_thread_run);
}
}
else if (_thread_run->state == PS_DEAD) {
/*
* Flag the allocated time slice period as
* up:
* Don't add dead threads to the waiting
* queue, because when they're reaped, it
* will corrupt the queue.
*/
}
else {
/*
* This thread has changed state and needs
* to be placed in the waiting queue.
*/
PTHREAD_WAITQ_INSERT(_thread_run);
/* Restart the time slice: */
_thread_run->slice_usec = -1;
}
}
/* Check if an incremental priority update is required: */
if (((tv.tv_sec - kern_inc_prio_time.tv_sec) * 1000000 +
tv.tv_usec - kern_inc_prio_time.tv_usec) > INC_PRIO_USEC) {
/*
* Enter a loop to look for run-enabled threads that
* have not run since the last time that an
* incremental priority update was performed:
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
/* Check if this thread is unable to run: */
if (pthread->state != PS_RUNNING) {
}
/*
* Check if the last time that this thread
* was run (as indicated by the last time it
* became inactive) is before the time that
* the last incremental priority check was
* made:
*/
else if (timercmp(&pthread->last_inactive, &kern_inc_prio_time, <)) {
/*
* Increment the incremental priority
* for this thread in the hope that
* it will eventually get a chance to
* run:
*/
(pthread->inc_prio)++;
}
}
/* Save the new incremental priority update time: */
kern_inc_prio_time.tv_sec = tv.tv_sec;
kern_inc_prio_time.tv_usec = tv.tv_usec;
}
/*
* Enter a loop to look for the first thread of the highest
* priority that is ready to run:
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
/* Check if the current thread is unable to run: */
if (pthread->state != PS_RUNNING) {
}
/*
* Check if no run-enabled thread has been seen or if
* the current thread has a priority higher than the
* highest seen so far:
*/
else if (pthread_h == NULL || (pthread->pthread_priority + pthread->inc_prio) > prio) {
/*
* Save this thread as the highest priority
* thread seen so far:
*/
pthread_h = pthread;
prio = pthread->pthread_priority + pthread->inc_prio;
}
}
/*
* Enter a loop to look for a thread that: 1. Is run-enabled.
* 2. Has the required agregate priority. 3. Has not been
* allocated its allocated time slice. 4. Became inactive
* least recently.
* Get the highest priority thread in the ready queue.
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
/* Check if the current thread is unable to run: */
if (pthread->state != PS_RUNNING) {
/* Ignore threads that are not ready to run. */
}
pthread_h = PTHREAD_PRIOQ_FIRST;
/*
* Check if the current thread as an agregate
* priority not equal to the highest priority found
* above:
*/
else if ((pthread->pthread_priority + pthread->inc_prio) != prio) {
/*
* Ignore threads which have lower agregate
* priority.
*/
}
/*
* Check if the current thread reached its time slice
* allocation last time it ran (or if it has not run
* yet):
*/
else if (pthread->slice_usec == -1) {
}
/*
* Check if an eligible thread has not been found
* yet, or if the current thread has an inactive time
* earlier than the last one seen:
*/
else if (pthread_s == NULL || timercmp(&pthread->last_inactive, &tv1, <)) {
/*
* Save the pointer to the current thread as
* the most eligible thread seen so far:
*/
pthread_s = pthread;
/*
* Save the time that the selected thread
* became inactive:
*/
tv1.tv_sec = pthread->last_inactive.tv_sec;
tv1.tv_usec = pthread->last_inactive.tv_usec;
}
}
/*
* Check if no thread was selected according to incomplete
* time slice allocation:
*/
if (pthread_s == NULL) {
/*
* Enter a loop to look for any other thread that: 1.
* Is run-enabled. 2. Has the required agregate
* priority. 3. Became inactive least recently.
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
/*
* Check if the current thread is unable to
* run:
*/
if (pthread->state != PS_RUNNING) {
/*
* Ignore threads that are not ready
* to run.
*/
}
/*
* Check if the current thread as an agregate
* priority not equal to the highest priority
* found above:
*/
else if ((pthread->pthread_priority + pthread->inc_prio) != prio) {
/*
* Ignore threads which have lower
* agregate priority.
*/
}
/*
* Check if an eligible thread has not been
* found yet, or if the current thread has an
* inactive time earlier than the last one
* seen:
*/
else if (pthread_s == NULL || timercmp(&pthread->last_inactive, &tv1, <)) {
/*
* Save the pointer to the current
* thread as the most eligible thread
* seen so far:
*/
pthread_s = pthread;
/*
* Save the time that the selected
* thread became inactive:
*/
tv1.tv_sec = pthread->last_inactive.tv_sec;
tv1.tv_usec = pthread->last_inactive.tv_usec;
}
}
}
/* Check if there are no threads ready to run: */
if (pthread_s == NULL) {
if (pthread_h == NULL) {
/*
* Lock the pthread kernel by changing the pointer to
* the running thread to point to the global kernel
@ -406,7 +376,10 @@ __asm__("fnsave %0": :"m"(*fdata));
_thread_kern_select(1);
} else {
/* Make the selected thread the current thread: */
_thread_run = pthread_s;
_thread_run = pthread_h;
/* Remove the thread from the ready queue. */
PTHREAD_PRIOQ_REMOVE(_thread_run);
/*
* Save the current time as the time that the thread
@ -424,149 +397,22 @@ __asm__("fnsave %0": :"m"(*fdata));
/* Reset the accumulated time slice period: */
_thread_run->slice_usec = 0;
}
/*
* Reset the incremental priority now that this
* thread has been given the chance to run:
*/
_thread_run->inc_prio = 0;
/* Check if there is more than one thread: */
if (_thread_run != _thread_link_list || _thread_run->nxt != NULL) {
/*
* Define the maximum time before a SIGVTALRM
* 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;
/*
* Enter a loop to look for threads waiting
* for a time:
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
/*
* Check if this thread is to
* timeout:
*/
if (pthread->state == PS_COND_WAIT ||
pthread->state == PS_SLEEP_WAIT ||
pthread->state == PS_FDR_WAIT ||
pthread->state == PS_FDW_WAIT ||
pthread->state == PS_SELECT_WAIT) {
/*
* Check if this thread is to
* wait forever:
*/
if (pthread->wakeup_time.tv_sec == -1) {
}
/*
* Check if this thread is to
* wakeup immediately:
*/
else if (pthread->wakeup_time.tv_sec == 0 &&
pthread->wakeup_time.tv_nsec == 0) {
}
/*
* Check if the current time
* is after the wakeup time:
*/
else if ((ts.tv_sec > pthread->wakeup_time.tv_sec) ||
((ts.tv_sec == pthread->wakeup_time.tv_sec) &&
(ts.tv_nsec > pthread->wakeup_time.tv_nsec))) {
} else {
/*
* 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:
*/
if (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:
*/
if (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(&tv, &ts1);
/*
* Check if the
* thread will be
* ready sooner than
* the earliest one
* found so far:
*/
if (timercmp(&tv, &itimer.it_value, <)) {
/*
* Update the
* time
* value:
*/
itimer.it_value.tv_sec = tv.tv_sec;
itimer.it_value.tv_usec = tv.tv_usec;
}
}
}
}
/*
* Start the interval timer for the
* calculated time interval:
*/
if (setitimer(ITIMER_VIRTUAL, &itimer, NULL) != 0) {
if (setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL) != 0) {
/*
* Cannot initialise the timer, so
* abort this process:
*/
PANIC("Cannot set virtual timer");
PANIC("Cannot set scheduling timer");
}
}
/* Check if a signal context was saved: */
if (_thread_run->sig_saved == 1) {
#ifndef __alpha__
@ -579,20 +425,30 @@ __asm__("fnsave %0": :"m"(*fdata));
/* 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;
_thread_kern_in_sched = 0;
/*
* 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);
}
_thread_sys_sigreturn(&_thread_run->saved_sigcontext);
} else
} 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);
}
/* This point should not be reached. */
PANIC("Thread has returned from sigreturn or longjmp");
@ -679,7 +535,8 @@ _thread_kern_select(int wait_reqd)
* Enter a loop to process threads waiting on either file descriptors
* or times:
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
_waitingq_check_reqd = 0; /* reset flag before loop */
TAILQ_FOREACH (pthread, &_waitingq, pqe) {
/* Assume that this state does not time out: */
settimeout = 0;
@ -690,12 +547,12 @@ _thread_kern_select(int wait_reqd)
* operations or timeouts:
*/
case PS_DEAD:
case PS_DEADLOCK:
case PS_FDLR_WAIT:
case PS_FDLW_WAIT:
case PS_FILE_WAIT:
case PS_JOIN:
case PS_MUTEX_WAIT:
case PS_RUNNING:
case PS_SIGTHREAD:
case PS_SIGWAIT:
case PS_STATE_MAX:
@ -704,6 +561,16 @@ _thread_kern_select(int wait_reqd)
/* Nothing to do here. */
break;
case PS_RUNNING:
/*
* A signal occurred and made this thread ready
* while in the scheduler or while the scheduling
* queues were protected.
*/
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
break;
/* File descriptor read wait: */
case PS_FDR_WAIT:
/* Add the file descriptor to the read set: */
@ -1010,16 +877,16 @@ _thread_kern_select(int wait_reqd)
* descriptors that are flagged as available by the
* _select syscall:
*/
for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) {
TAILQ_FOREACH (pthread, &_waitingq, pqe) {
/* Process according to thread state: */
switch (pthread->state) {
/*
* States which do not depend on file
* descriptor I/O operations:
*/
case PS_RUNNING:
case PS_COND_WAIT:
case PS_DEAD:
case PS_DEADLOCK:
case PS_FDLR_WAIT:
case PS_FDLW_WAIT:
case PS_FILE_WAIT:
@ -1034,6 +901,15 @@ _thread_kern_select(int wait_reqd)
/* Nothing to do here. */
break;
case PS_RUNNING:
/*
* A signal occurred and made this thread
* ready while in the scheduler.
*/
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
break;
/* File descriptor read wait: */
case PS_FDR_WAIT:
/*
@ -1047,6 +923,13 @@ _thread_kern_select(int wait_reqd)
* is scheduled next:
*/
pthread->state = PS_RUNNING;
/*
* Remove it from the waiting queue
* and add it to the ready queue:
*/
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
break;
@ -1063,6 +946,13 @@ _thread_kern_select(int wait_reqd)
* scheduled next:
*/
pthread->state = PS_RUNNING;
/*
* Remove it from the waiting queue
* and add it to the ready queue:
*/
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
break;
@ -1269,6 +1159,13 @@ _thread_kern_select(int wait_reqd)
* thread to run:
*/
pthread->state = PS_RUNNING;
/*
* Remove it from the waiting queue
* and add it to the ready queue:
*/
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
break;
}
@ -1320,4 +1217,80 @@ _thread_kern_set_timeout(struct timespec * timeout)
}
return;
}
void
_thread_kern_sched_defer(void)
{
/* Allow scheduling deferral to be recursive. */
_thread_run->sched_defer_count++;
}
void
_thread_kern_sched_undefer(void)
{
pthread_t pthread;
int need_resched = 0;
/*
* Perform checks to yield only if we are about to undefer
* scheduling.
*/
if (_thread_run->sched_defer_count == 1) {
/*
* Check if the waiting queue needs to be examined for
* threads that are now ready:
*/
while (_waitingq_check_reqd != 0) {
/* Clear the flag before checking the waiting queue: */
_waitingq_check_reqd = 0;
TAILQ_FOREACH(pthread, &_waitingq, pqe) {
if (pthread->state == PS_RUNNING) {
PTHREAD_WAITQ_REMOVE(pthread);
PTHREAD_PRIOQ_INSERT_TAIL(pthread);
}
}
}
/*
* We need to yield if a thread change of state caused a
* higher priority thread to become ready, or if a
* scheduling signal occurred while preemption was disabled.
*/
if ((((pthread = PTHREAD_PRIOQ_FIRST) != NULL) &&
(pthread->active_priority > _thread_run->active_priority)) ||
(_thread_run->yield_on_sched_undefer != 0)) {
_thread_run->yield_on_sched_undefer = 0;
need_resched = 1;
}
}
if (_thread_run->sched_defer_count > 0) {
/* Decrement the scheduling deferral count. */
_thread_run->sched_defer_count--;
/* Yield the CPU if necessary: */
if (need_resched)
_thread_kern_sched(NULL);
}
}
static inline void
thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in)
{
pthread_t tid_out = thread_out;
pthread_t tid_in = thread_in;
if ((tid_out != NULL) &&
(tid_out->flags & PTHREAD_FLAGS_PRIVATE != 0))
tid_out = NULL;
if ((tid_in != NULL) &&
(tid_in->flags & PTHREAD_FLAGS_PRIVATE != 0))
tid_in = NULL;
if ((_sched_switch_hook != NULL) && (tid_out != tid_in)) {
/* Run the scheduler switch hook: */
_sched_switch_hook(tid_out, tid_in);
}
}
#endif

View file

@ -52,6 +52,13 @@ pthread_kill(pthread_t pthread, int sig)
/* Find the thread in the list of active threads: */
else if ((ret = _find_thread(pthread)) == 0) {
/*
* Guard against preemption by a scheduling signal.
* A change of thread state modifies the waiting
* and priority queues.
*/
_thread_kern_sched_defer();
switch (pthread->state) {
case PS_SIGSUSPEND:
/*
@ -108,6 +115,12 @@ pthread_kill(pthread_t pthread, int sig)
sigaddset(&pthread->sigpend,sig);
break;
}
/*
* Reenable preemption and yield if a scheduling signal
* occurred while in the critical region.
*/
_thread_kern_sched_undefer();
}
/* Return the completion status: */

View file

@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more