#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <pthread_np.h>
#include <sys/sched.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sysexits.h>
#include <sys/time.h>
#ifndef NELEMENTS
#define NELEMENTS(arr) (sizeof (arr) / sizeof (arr[0]))
#endif
#ifndef NUM_THREADS
#define NUM_THREADS 10
#endif
#define MAX_THREAD_CMDS 10
static void log_error(const char *, ...) __printflike(1, 2);
static void log_trace (const char *, ...) __printflike(1, 2);
static void log_info (const char *, ...) __printflike(1, 2);
typedef enum {
STAT_INITIAL,
STAT_WAITCONDVAR,
STAT_WAITMUTEX
} thread_status_t;
typedef enum {
FLAGS_REPORT_WAITCONDMUTEX = 0x01,
FLAGS_REPORT_WAITCONDVAR = 0x02,
FLAGS_REPORT_WAITMUTEX = 0x04,
FLAGS_REPORT_BUSY_LOOP = 0x08,
FLAGS_IS_BUSY = 0x10,
FLAGS_WAS_BUSY = 0x20
} thread_flags_t;
typedef enum {
CMD_NONE,
CMD_TAKE_MUTEX,
CMD_RELEASE_MUTEX,
CMD_WAIT_FOR_SIGNAL,
CMD_BUSY_LOOP,
CMD_PROTECTED_OP,
CMD_RELEASE_ALL
} thread_cmd_id_t;
typedef struct {
thread_cmd_id_t cmd_id;
pthread_mutex_t *mutex;
pthread_cond_t *cond;
} thread_cmd_t;
typedef struct {
pthread_cond_t cond_var;
thread_status_t status;
thread_cmd_t cmd;
int flags;
int priority;
int ret;
pthread_t tid;
u_int8_t id;
} thread_state_t;
typedef enum {
M_POSIX,
M_SS2_DEFAULT,
M_SS2_ERRORCHECK,
M_SS2_NORMAL,
M_SS2_RECURSIVE
} mutex_kind_t;
const char *protocol_strs[] = {
"PTHREAD_PRIO_NONE",
"PTHREAD_PRIO_INHERIT",
"PTHREAD_PRIO_PROTECT"
};
const int protocols[] = {
PTHREAD_PRIO_NONE,
PTHREAD_PRIO_INHERIT,
PTHREAD_PRIO_PROTECT
};
const char *mutextype_strs[] = {
"POSIX (type not specified)",
"SS2 PTHREAD_MUTEX_DEFAULT",
"SS2 PTHREAD_MUTEX_ERRORCHECK",
"SS2 PTHREAD_MUTEX_NORMAL",
"SS2 PTHREAD_MUTEX_RECURSIVE"
};
const int mutex_types[] = {
0,
PTHREAD_MUTEX_DEFAULT,
PTHREAD_MUTEX_ERRORCHECK,
PTHREAD_MUTEX_NORMAL,
PTHREAD_MUTEX_RECURSIVE
};
static int done = 0;
static int trace_enabled = 0;
static int use_global_condvar = 0;
static thread_state_t states[NUM_THREADS];
static int pipefd[2];
static pthread_mutex_t waiter_mutex;
static pthread_mutex_t cond_mutex;
static pthread_cond_t cond_var;
static FILE *logfile;
static int error_count = 0, pass_count = 0, total = 0;
extern char *strtok_r(char *str, const char *sep, char **last);
#ifdef DEBUG
static void
kern_switch (pthread_t pthread_out, pthread_t pthread_in)
{
if (pthread_out != NULL)
printf ("Swapping out thread 0x%x, ", (int) pthread_out);
else
printf ("Swapping out kernel thread, ");
if (pthread_in != NULL)
printf ("swapping in thread 0x%x\n", (int) pthread_in);
else
printf ("swapping in kernel thread.\n");
}
#endif
static void
log_error (const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
fprintf (logfile, "FAIL: ");
vfprintf (logfile, fmt, ap);
error_count = error_count + 1;
total = total + 1;
}
static void
log_pass (void)
{
fprintf (logfile, "PASS\n");
pass_count = pass_count + 1;
total = total + 1;
}
static void
log_trace (const char *fmt, ...)
{
va_list ap;
if (trace_enabled) {
va_start (ap, fmt);
vfprintf (logfile, fmt, ap);
}
}
static void
log_info (const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
vfprintf (logfile, fmt, ap);
}
static void
check_result (int expected, int actual)
{
if (expected != actual)
log_error ("expected %d, returned %d\n", expected, actual);
else
log_pass ();
}
static void
check_run_order (char *order)
{
const char *sep = ":,";
char *tok, *last, *idstr, *endptr;
int expected_id, bytes, count = 0, errors = 0;
u_int8_t id;
assert ((tok = (char *) malloc (strlen(order) + 1)) != NULL);
strcpy (tok, order);
assert (ioctl (pipefd[0], FIONREAD, &bytes) == 0);
log_trace ("%d bytes read from FIFO.\n", bytes);
for (idstr = strtok_r (tok, sep, &last);
(idstr != NULL) && (count < bytes);
idstr = strtok_r (NULL, sep, &last)) {
expected_id = (int) strtol (idstr, &endptr, 10);
assert ((endptr != NULL) && (*endptr == '\0'));
assert (read (pipefd[0], &id, sizeof (id)) == sizeof (id));
count = count + sizeof (id);
if (id != expected_id) {
log_trace ("Thread %d ran out of order.\n", id);
errors = errors + 1;
}
else {
log_trace ("Thread %d at priority %d reporting.\n",
(int) id, states[id].priority);
}
}
if (count < bytes) {
while (count < bytes) {
read (pipefd[0], &id, sizeof (id));
count = count + 1;
errors = errors + 1;
}
}
else if (bytes < count)
errors = errors + count - bytes;
if (errors == 0)
log_pass ();
else
log_error ("%d threads ran out of order\n", errors);
}
static void *
waiter (void *arg)
{
thread_state_t *statep = (thread_state_t *) arg;
pthread_mutex_t *held_mutex[MAX_THREAD_CMDS];
int held_mutex_owned[MAX_THREAD_CMDS];
sigset_t mask;
struct timeval tv1, tv2;
thread_cmd_t cmd;
int i, mutex_count = 0;
statep->status = STAT_INITIAL;
sigfillset (&mask);
sigdelset (&mask, SIGINT);
sigprocmask (SIG_BLOCK, &mask, NULL);
while (done == 0) {
statep->status = STAT_WAITMUTEX;
log_trace ("Thread %d: locking cond_mutex.\n",
(int) statep->id);
pthread_mutex_lock (&cond_mutex);
if (statep->flags & FLAGS_REPORT_WAITCONDMUTEX)
write (pipefd[1], &statep->id, sizeof (statep->id));
log_trace ("Thread %d: waiting for cond_var.\n",
(int) statep->id);
statep->status = STAT_WAITCONDVAR;
if (use_global_condvar != 0)
pthread_cond_wait (&cond_var, &cond_mutex);
else
pthread_cond_wait (&statep->cond_var, &cond_mutex);
if (statep->flags & FLAGS_REPORT_WAITCONDVAR) {
write (pipefd[1], &statep->id, sizeof (statep->id));
log_trace ("Thread %d: wrote to pipe.\n",
(int) statep->id);
}
log_trace ("Thread %d: received cond_var signal.\n",
(int) statep->id);
cmd = statep->cmd;
statep->cmd.cmd_id = CMD_NONE;
assert (pthread_mutex_unlock (&cond_mutex) == 0);
switch (cmd.cmd_id) {
case CMD_TAKE_MUTEX:
statep->ret = pthread_mutex_lock (cmd.mutex);
if (statep->ret == 0) {
assert (mutex_count < sizeof (held_mutex));
held_mutex[mutex_count] = cmd.mutex;
held_mutex_owned[mutex_count] = 1;
mutex_count++;
}
else {
held_mutex_owned[mutex_count] = 0;
log_trace ("Thread id %d unable to lock mutex, "
"error = %d\n", (int) statep->id,
statep->ret);
}
break;
case CMD_RELEASE_MUTEX:
assert ((mutex_count <= sizeof (held_mutex)) &&
(mutex_count > 0));
mutex_count--;
if (held_mutex_owned[mutex_count] != 0)
assert (pthread_mutex_unlock
(held_mutex[mutex_count]) == 0);
break;
case CMD_WAIT_FOR_SIGNAL:
assert (pthread_mutex_lock (cmd.mutex) == 0);
assert (pthread_cond_wait (cmd.cond, cmd.mutex) == 0);
assert (pthread_mutex_unlock (cmd.mutex) == 0);
break;
case CMD_BUSY_LOOP:
log_trace ("Thread %d: Entering busy loop.\n",
(int) statep->id);
assert (gettimeofday (&tv2, NULL) == 0);
tv1.tv_sec = tv2.tv_sec + 5;
tv1.tv_usec = tv2.tv_usec;
statep->flags |= FLAGS_IS_BUSY;
while (timercmp (&tv2, &tv1,<)) {
assert (gettimeofday (&tv2, NULL) == 0);
}
statep->flags &= ~FLAGS_IS_BUSY;
statep->flags |= FLAGS_WAS_BUSY;
if (statep->flags & FLAGS_REPORT_BUSY_LOOP)
write (pipefd[1], &statep->id,
sizeof (statep->id));
log_trace ("Thread %d: Leaving busy loop.\n",
(int) statep->id);
break;
case CMD_PROTECTED_OP:
assert (pthread_mutex_lock (cmd.mutex) == 0);
statep->flags |= FLAGS_WAS_BUSY;
if (statep->flags & FLAGS_REPORT_BUSY_LOOP)
write (pipefd[1], &statep->id,
sizeof (statep->id));
assert (pthread_mutex_unlock (cmd.mutex) == 0);
break;
case CMD_RELEASE_ALL:
assert ((mutex_count <= sizeof (held_mutex)) &&
(mutex_count > 0));
for (i = mutex_count - 1; i >= 0; i--) {
if (held_mutex_owned[i] != 0)
assert (pthread_mutex_unlock
(held_mutex[i]) == 0);
}
mutex_count = 0;
break;
case CMD_NONE:
default:
break;
}
statep->status = STAT_WAITMUTEX;
log_trace ("Thread %d: waiting for big giant lock.\n",
(int) statep->id);
pthread_mutex_lock (&waiter_mutex);
if (statep->flags & FLAGS_REPORT_WAITMUTEX)
write (pipefd[1], &statep->id, sizeof (statep->id));
log_trace ("Thread %d: got big giant lock.\n",
(int) statep->id);
statep->status = STAT_INITIAL;
pthread_mutex_unlock (&waiter_mutex);
}
log_trace ("Thread %d: Exiting thread 0x%p\n", (int) statep->id,
pthread_self());
pthread_exit (arg);
return (NULL);
}
static void *
lock_twice (void *arg)
{
thread_state_t *statep = (thread_state_t *) arg;
sigset_t mask;
statep->status = STAT_INITIAL;
sigfillset (&mask);
sigdelset (&mask, SIGINT);
sigprocmask (SIG_BLOCK, &mask, NULL);
log_trace ("Thread %d: locking cond_mutex.\n", (int) statep->id);
pthread_mutex_lock (&cond_mutex);
log_trace ("Thread %d: waiting for cond_var.\n", (int) statep->id);
statep->status = STAT_WAITCONDVAR;
pthread_cond_wait (&cond_var, &cond_mutex);
log_trace ("Thread %d: received cond_var signal.\n", (int) statep->id);
assert (pthread_mutex_unlock (&cond_mutex) == 0);
statep->status = STAT_WAITMUTEX;
assert (pthread_mutex_lock (statep->cmd.mutex) == 0);
statep->ret = pthread_mutex_lock (statep->cmd.mutex);
statep->status = 0;
assert (pthread_mutex_unlock (statep->cmd.mutex) == 0);
if (statep->ret == 0)
pthread_mutex_unlock (statep->cmd.mutex);
log_trace ("Thread %d: Exiting thread 0x%p\n", (int) statep->id,
pthread_self());
pthread_exit (arg);
return (NULL);
}
static void
sighandler (int signo)
{
log_info ("Signal handler caught signal %d, thread id 0x%p\n",
signo, pthread_self());
if (signo == SIGINT)
done = 1;
}
static void
send_cmd (int id, thread_cmd_id_t cmd)
{
assert (pthread_mutex_lock (&cond_mutex) == 0);
assert (states[id].status == STAT_WAITCONDVAR);
states[id].cmd.cmd_id = cmd;
states[id].cmd.mutex = NULL;
states[id].cmd.cond = NULL;
states[id].flags &= ~(FLAGS_WAS_BUSY | FLAGS_IS_BUSY);
assert (pthread_cond_signal (&states[id].cond_var) == 0);
assert (pthread_mutex_unlock (&cond_mutex) == 0);
}
static void
send_mutex_cmd (int id, thread_cmd_id_t cmd, pthread_mutex_t *m)
{
assert (pthread_mutex_lock (&cond_mutex) == 0);
assert (states[id].status == STAT_WAITCONDVAR);
states[id].cmd.cmd_id = cmd;
states[id].cmd.mutex = m;
states[id].cmd.cond = NULL;
states[id].flags &= ~(FLAGS_WAS_BUSY | FLAGS_IS_BUSY);
assert (pthread_cond_signal (&states[id].cond_var) == 0);
assert (pthread_mutex_unlock (&cond_mutex) == 0);
}
static void
send_mutex_cv_cmd (int id, thread_cmd_id_t cmd, pthread_mutex_t *m,
pthread_cond_t *cv)
{
assert (pthread_mutex_lock (&cond_mutex) == 0);
assert (states[id].status == STAT_WAITCONDVAR);
states[id].cmd.cmd_id = cmd;
states[id].cmd.mutex = m;
states[id].cmd.cond = cv;
states[id].flags &= ~(FLAGS_WAS_BUSY | FLAGS_IS_BUSY);
assert (pthread_cond_signal (&states[id].cond_var) == 0);
assert (pthread_mutex_unlock (&cond_mutex) == 0);
}
static void
mutex_init_test (void)
{
pthread_mutexattr_t mattr;
pthread_mutex_t mutex;
mutex_kind_t mkind;
int mproto, ret;
assert (pthread_mutexattr_init (&mattr) == 0);
log_info ("Testing pthread_mutex_init\n");
log_info ("--------------------------\n");
for (mproto = 0; mproto < NELEMENTS(protocols); mproto++) {
for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
assert (pthread_mutexattr_init (&mattr) == 0);
assert (pthread_mutexattr_setprotocol (&mattr,
protocols[mproto]) == 0);
if (mkind != M_POSIX) {
assert (pthread_mutexattr_settype (&mattr,
mutex_types[mkind]) == 0);
}
log_info (" Protocol %s, Type %s - ",
protocol_strs[mproto], mutextype_strs[mkind]);
ret = pthread_mutex_init (&mutex, &mattr);
check_result ( 0, ret);
assert (pthread_mutex_destroy (&mutex) == 0);
assert (pthread_mutexattr_destroy (&mattr) == 0);
}
}
}
static void
mutex_destroy_test (void)
{
pthread_mutexattr_t mattr;
pthread_mutex_t mutex;
pthread_condattr_t cattr;
pthread_cond_t cv;
pthread_attr_t pattr;
int mproto, ret;
mutex_kind_t mkind;
#if 0
thread_state_t state;
#endif
log_info ("Testing pthread_mutex_destroy\n");
log_info ("-----------------------------\n");
assert (pthread_attr_init (&pattr) == 0);
assert (pthread_attr_setdetachstate (&pattr,
PTHREAD_CREATE_DETACHED) == 0);
#if 0
state.flags = 0;
#endif
for (mproto = 0; mproto < NELEMENTS(protocols); mproto++) {
for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
assert (pthread_mutexattr_init (&mattr) == 0);
assert (pthread_mutexattr_setprotocol (&mattr,
protocols[mproto]) == 0);
if (mkind != M_POSIX) {
assert (pthread_mutexattr_settype (&mattr,
mutex_types[mkind]) == 0);
}
assert (pthread_mutex_init (&mutex, &mattr) == 0);
log_info (" Protocol %s, Type %s\n",
protocol_strs[mproto], mutextype_strs[mkind]);
log_info (" Destruction of unused mutex - ");
assert (pthread_mutex_init (&mutex, &mattr) == 0);
ret = pthread_mutex_destroy (&mutex);
check_result ( 0, ret);
log_info (" Destruction of mutex locked by self - ");
assert (pthread_mutex_init (&mutex, &mattr) == 0);
assert (pthread_mutex_lock (&mutex) == 0);
ret = pthread_mutex_destroy (&mutex);
check_result ( EBUSY, ret);
assert (pthread_mutex_unlock (&mutex) == 0);
assert (pthread_mutex_destroy (&mutex) == 0);
log_info (" Destruction of mutex locked by another "
"thread - ");
assert (pthread_mutex_init (&mutex, &mattr) == 0);
send_mutex_cmd (0, CMD_TAKE_MUTEX, &mutex);
sleep (1);
ret = pthread_mutex_destroy (&mutex);
check_result ( EBUSY, ret);
send_cmd (0, CMD_RELEASE_ALL);
sleep (1);
assert (pthread_mutex_destroy (&mutex) == 0);
log_info (" Destruction of mutex while being used in "
"cond_wait - ");
assert (pthread_mutex_init (&mutex, &mattr) == 0);
assert (pthread_condattr_init (&cattr) == 0);
assert (pthread_cond_init (&cv, &cattr) == 0);
send_mutex_cv_cmd (0, CMD_WAIT_FOR_SIGNAL, &mutex, &cv);
sleep (1);
ret = pthread_mutex_destroy (&mutex);
check_result ( EBUSY, ret);
pthread_cond_signal (&cv);
sleep (1);
assert (pthread_mutex_destroy (&mutex) == 0);
}
}
}
static void
mutex_lock_test (void)
{
pthread_mutexattr_t mattr;
pthread_mutex_t mutex;
pthread_attr_t pattr;
int mproto, ret;
mutex_kind_t mkind;
thread_state_t state;
log_info ("Testing pthread_mutex_lock\n");
log_info ("--------------------------\n");
assert (pthread_attr_init (&pattr) == 0);
assert (pthread_attr_setdetachstate (&pattr,
PTHREAD_CREATE_DETACHED) == 0);
state.flags = 0;
for (mproto = 0; mproto < NELEMENTS(protocols); mproto++) {
for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
assert (pthread_mutexattr_init (&mattr) == 0);
assert (pthread_mutexattr_setprotocol (&mattr,
protocols[mproto]) == 0);
if (mkind != M_POSIX) {
assert (pthread_mutexattr_settype (&mattr,
mutex_types[mkind]) == 0);
}
assert (pthread_mutex_init (&mutex, &mattr) == 0);
log_info (" Protocol %s, Type %s\n",
protocol_strs[mproto], mutextype_strs[mkind]);
log_info (" Lock on unlocked mutex - ");
ret = pthread_mutex_lock (&mutex);
check_result ( 0, ret);
pthread_mutex_unlock (&mutex);
log_info (" Lock on invalid mutex - ");
ret = pthread_mutex_lock (NULL);
check_result ( EINVAL, ret);
log_info (" Lock on mutex held by self - ");
assert (pthread_create (&state.tid, &pattr, lock_twice,
(void *) &state) == 0);
sleep (1);
state.cmd.mutex = &mutex;
state.ret = 0xdeadbeef;
assert (pthread_mutex_lock (&cond_mutex) == 0);
assert (pthread_cond_signal (&cond_var) == 0);
assert (pthread_mutex_unlock (&cond_mutex) == 0);
sleep (1);
switch (mkind) {
case M_POSIX:
check_result ( EDEADLK,
state.ret);
break;
case M_SS2_DEFAULT:
check_result ( EDEADLK,
state.ret);
break;
case M_SS2_ERRORCHECK:
check_result ( EDEADLK,
state.ret);
break;
case M_SS2_NORMAL:
check_result ( 0xdeadbeef,
state.ret);
break;
case M_SS2_RECURSIVE:
check_result ( 0, state.ret);
break;
}
pthread_mutex_destroy (&mutex);
pthread_mutexattr_destroy (&mattr);
}
}
}
static void
mutex_unlock_test (void)
{
const int test_thread_id = 0;
pthread_mutexattr_t mattr;
pthread_mutex_t mutex;
int mproto, ret;
mutex_kind_t mkind;
log_info ("Testing pthread_mutex_unlock\n");
log_info ("----------------------------\n");
for (mproto = 0; mproto < NELEMENTS(protocols); mproto++) {
for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
assert (pthread_mutexattr_init (&mattr) == 0);
assert (pthread_mutexattr_setprotocol (&mattr,
protocols[mproto]) == 0);
if (mkind != M_POSIX) {
assert (pthread_mutexattr_settype (&mattr,
mutex_types[mkind]) == 0);
}
assert (pthread_mutex_init (&mutex, &mattr) == 0);
log_info (" Protocol %s, Type %s\n",
protocol_strs[mproto], mutextype_strs[mkind]);
log_info (" Unlock on mutex held by self - ");
assert (pthread_mutex_lock (&mutex) == 0);
ret = pthread_mutex_unlock (&mutex);
check_result ( 0, ret);
log_info (" Unlock on invalid mutex - ");
ret = pthread_mutex_unlock (NULL);
check_result ( EINVAL, ret);
log_info (" Unlock on mutex locked by another thread - ");
send_mutex_cmd (test_thread_id, CMD_TAKE_MUTEX, &mutex);
sleep (1);
ret = pthread_mutex_unlock (&mutex);
switch (mkind) {
case M_POSIX:
check_result ( EPERM, ret);
break;
case M_SS2_DEFAULT:
check_result ( EPERM, ret);
break;
case M_SS2_ERRORCHECK:
check_result ( EPERM, ret);
break;
case M_SS2_NORMAL:
check_result ( EPERM, ret);
break;
case M_SS2_RECURSIVE:
check_result ( EPERM, ret);
break;
}
if (ret == 0) {
pthread_mutex_lock (&mutex);
}
send_cmd (test_thread_id, CMD_RELEASE_ALL);
sleep (1);
pthread_mutex_destroy (&mutex);
pthread_mutexattr_destroy (&mattr);
}
}
}
static void
queueing_order_test (void)
{
int i;
log_info ("Testing queueing order\n");
log_info ("----------------------\n");
assert (pthread_mutex_lock (&waiter_mutex) == 0);
assert (pthread_mutex_lock (&cond_mutex) == 0);
for (i = 0; i < NUM_THREADS; i++) {
states[i].flags = FLAGS_REPORT_WAITMUTEX;
assert (pthread_cond_signal (&states[i].cond_var) == 0);
}
assert (pthread_mutex_unlock (&cond_mutex) == 0);
sleep (1);
use_global_condvar = 1;
assert (pthread_mutex_unlock (&waiter_mutex) == 0);
sleep (1);
log_info (" Queueing order on a mutex - ");
check_run_order ("9,8,7,6,5,4,3,2,1,0");
for (i = 0; i < NUM_THREADS; i = i + 1) {
states[i].flags = FLAGS_REPORT_WAITCONDVAR;
}
assert (pthread_mutex_lock (&waiter_mutex) == 0);
log_info (" Queueing order on a condition variable - ");
assert (pthread_mutex_lock (&cond_mutex) == 0);
assert (pthread_cond_signal (&cond_var) == 0);
assert (pthread_mutex_unlock (&cond_mutex) == 0);
sleep (1);
if (states[NUM_THREADS - 1].status != STAT_WAITMUTEX)
log_error ("highest priority thread does not run.\n");
assert (pthread_mutex_lock (&cond_mutex) == 0);
assert (pthread_cond_broadcast (&cond_var) == 0);
assert (pthread_mutex_unlock (&cond_mutex) == 0);
sleep (1);
check_run_order ("9,8,7,6,5,4,3,2,1,0");
for (i = 0; i < NUM_THREADS; i = i + 1) {
states[i].flags = 0;
}
use_global_condvar = 0;
assert (pthread_mutex_unlock (&waiter_mutex) == 0);
sleep (1);
}
static void
mutex_prioceiling_test (void)
{
const int test_thread_id = 0;
pthread_mutexattr_t mattr;
struct sched_param param;
pthread_mutex_t m[3];
mutex_kind_t mkind;
int i, ret, policy, my_prio, old_ceiling;
log_info ("Testing priority ceilings\n");
log_info ("-------------------------\n");
for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
log_info (" Protype PTHREAD_PRIO_PROTECT, Type %s\n",
mutextype_strs[mkind]);
assert (pthread_mutexattr_init (&mattr) == 0);
assert (pthread_getschedparam (pthread_self(), &policy,
¶m) == 0);
my_prio = param.sched_priority;
log_trace ("Current scheduling policy %d, priority %d\n",
policy, my_prio);
assert (pthread_mutexattr_setprotocol(&mattr,
PTHREAD_PRIO_PROTECT) == 0);
if (mkind != M_POSIX) {
assert (pthread_mutexattr_settype (&mattr,
mutex_types[mkind]) == 0);
}
for (i = 0; i < 3; i++)
assert (pthread_mutex_init (&m[i], &mattr) == 0);
for (i = 0; i < 3; i++)
assert (pthread_mutex_setprioceiling (&m[i],
my_prio - 5 + 5*i, &old_ceiling) == 0);
log_info (" Lock with ceiling priority < thread priority - ");
ret = pthread_mutex_lock (&m[0]);
check_result ( EINVAL, ret);
if (ret == 0)
pthread_mutex_unlock (&m[0]);
log_info (" Lock with ceiling priority = thread priority - ");
ret = pthread_mutex_lock (&m[1]);
check_result ( 0, ret);
if (ret == 0)
pthread_mutex_unlock (&m[1]);
log_info (" Lock with ceiling priority > thread priority - ");
ret = pthread_mutex_lock (&m[2]);
check_result ( 0, ret);
if (ret == 0)
pthread_mutex_unlock (&m[2]);
log_info (" Preemption with ceiling priority < thread "
"priority - ");
send_mutex_cmd (test_thread_id, CMD_TAKE_MUTEX, &m[0]);
sleep (1);
log_trace ("Sending busy command.\n");
send_cmd (test_thread_id, CMD_BUSY_LOOP);
log_trace ("Busy sent, yielding\n");
pthread_yield ();
log_trace ("Returned from yield.\n");
if (states[test_thread_id].flags &
(FLAGS_IS_BUSY | FLAGS_WAS_BUSY))
log_error ("test thread inproperly preempted us.\n");
else {
sleep (6);
if ((states[test_thread_id].flags & FLAGS_WAS_BUSY) == 0)
log_error ("test thread never finished.\n");
else
log_pass ();
}
states[test_thread_id].flags &= ~FLAGS_WAS_BUSY;
send_cmd (test_thread_id, CMD_RELEASE_ALL);
sleep (1);
log_info (" Preemption with ceiling priority = thread "
"priority - ");
send_mutex_cmd (test_thread_id, CMD_TAKE_MUTEX, &m[1]);
sleep (1);
log_trace ("Sending busy\n");
send_cmd (test_thread_id, CMD_BUSY_LOOP);
log_trace ("Busy sent, yielding\n");
pthread_yield ();
log_trace ("Returned from yield.\n");
if ((states[test_thread_id].flags & FLAGS_IS_BUSY) == 0)
log_error ("test thread did not switch in on yield.\n");
else if (states[test_thread_id].flags & FLAGS_WAS_BUSY)
log_error ("test thread ran to completion.\n");
else {
sleep (6);
if ((states[test_thread_id].flags & FLAGS_WAS_BUSY) == 0)
log_error ("test thread never finished.\n");
else
log_pass ();
}
states[test_thread_id].flags &= ~FLAGS_WAS_BUSY;
send_cmd (test_thread_id, CMD_RELEASE_ALL);
sleep (1);
log_info (" SCHED_FIFO scheduling and ceiling priority = "
"thread priority - ");
param.sched_priority = states[test_thread_id].priority;
assert (pthread_setschedparam (states[test_thread_id].tid,
SCHED_FIFO, ¶m) == 0);
send_mutex_cmd (test_thread_id, CMD_TAKE_MUTEX, &m[1]);
sleep (1);
log_trace ("Sending busy\n");
send_cmd (test_thread_id, CMD_BUSY_LOOP);
log_trace ("Busy sent, yielding\n");
pthread_yield ();
log_trace ("Returned from yield.\n");
if ((states[test_thread_id].flags & FLAGS_WAS_BUSY) == 0) {
log_error ("test thread did not run to completion.\n");
sleep (6);
}
else
log_pass ();
states[test_thread_id].flags &= ~FLAGS_WAS_BUSY;
param.sched_priority = states[test_thread_id].priority;
assert (pthread_setschedparam (states[test_thread_id].tid,
SCHED_RR, ¶m) == 0);
send_cmd (test_thread_id, CMD_RELEASE_ALL);
sleep (1);
log_info (" SCHED_FIFO scheduling and ceiling priority > "
"thread priority - ");
send_mutex_cmd (test_thread_id, CMD_TAKE_MUTEX, &m[2]);
sleep (1);
log_trace ("Sending busy\n");
send_cmd (test_thread_id, CMD_BUSY_LOOP);
log_trace ("Busy sent, yielding\n");
pthread_yield ();
log_trace ("Returned from yield.\n");
if ((states[test_thread_id].flags & FLAGS_IS_BUSY) != 0) {
log_error ("test thread did not run to completion.\n");
sleep (6);
}
else if ((states[test_thread_id].flags & FLAGS_WAS_BUSY) == 0)
log_error ("test thread never finished.\n");
else
log_pass ();
states[test_thread_id].flags &= ~FLAGS_WAS_BUSY;
send_cmd (test_thread_id, CMD_RELEASE_ALL);
sleep (1);
for (i = 0; i < 3; i++)
assert (pthread_mutex_destroy (&m[i]) == 0);
}
}
static void
mutex_prioinherit_test (void)
{
pthread_mutexattr_t mattr;
struct sched_param param;
pthread_mutex_t m[3];
mutex_kind_t mkind;
int i, policy, my_prio;
assert (pthread_getschedparam (pthread_self(), &policy,
¶m) == 0);
my_prio = param.sched_priority;
log_trace ("Current scheduling policy %d, priority %d\n",
policy, my_prio);
log_info ("Testing priority inheritance\n");
log_info ("----------------------------\n");
for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
log_info (" Protype PTHREAD_PRIO_INHERIT, Type %s\n",
mutextype_strs[mkind]);
assert (pthread_mutexattr_init (&mattr) == 0);
assert (pthread_mutexattr_setprotocol(&mattr,
PTHREAD_PRIO_INHERIT) == 0);
if (mkind != M_POSIX) {
assert (pthread_mutexattr_settype (&mattr,
mutex_types[mkind]) == 0);
}
for (i = 0; i < 3; i++)
assert (pthread_mutex_init (&m[i], &mattr) == 0);
log_info (" Simple inheritance test - ");
send_mutex_cmd (4, CMD_TAKE_MUTEX, &m[0]);
sleep (1);
send_mutex_cmd (4, CMD_TAKE_MUTEX, &m[1]);
sleep (1);
for (i = 0; i < NUM_THREADS; i++)
states[i].flags |= FLAGS_REPORT_WAITMUTEX;
log_trace ("Commanding protected operation to thread 2.\n");
send_mutex_cmd (2, CMD_PROTECTED_OP, &m[0]);
log_trace ("Commanding protected operation to thread 3.\n");
send_mutex_cmd (3, CMD_PROTECTED_OP, &m[1]);
sleep (1);
log_trace ("Commanding protected operation to thread 4.\n");
send_mutex_cmd (4, CMD_PROTECTED_OP, &m[2]);
log_trace ("Commanding busy loop to thread 5.\n");
send_cmd (5, CMD_BUSY_LOOP);
sleep (1);
if ((states[5].flags & FLAGS_IS_BUSY) == 0)
log_error ("thread 5 is not running.\n");
log_trace ("Commanding protected operation thread 6.\n");
send_mutex_cmd (6, CMD_PROTECTED_OP, &m[0]);
sleep (1);
if ((states[4].flags & FLAGS_WAS_BUSY) == 0)
log_error ("thread 4 failed to inherit priority.\n");
states[4].flags = 0;
send_cmd (4, CMD_RELEASE_ALL);
sleep (5);
check_run_order ("4,6,5,3,2");
for (i = 0; i < NUM_THREADS; i++)
states[i].flags = 0;
log_info (" Inheritance test with change of priority - ");
param.sched_priority = states[2].priority;
assert (pthread_setschedparam (states[2].tid, SCHED_FIFO,
¶m) == 0);
param.sched_priority = states[4].priority;
assert (pthread_setschedparam (states[4].tid, SCHED_FIFO,
¶m) == 0);
send_mutex_cmd (4, CMD_TAKE_MUTEX, &m[0]);
sleep (1);
send_cmd (2, CMD_BUSY_LOOP);
sleep (1);
send_cmd (4, CMD_BUSY_LOOP);
sleep (1);
states[2].flags = FLAGS_REPORT_WAITMUTEX;
states[4].flags = FLAGS_REPORT_WAITMUTEX;
param.sched_priority = states[2].priority;
assert (pthread_setschedparam (states[4].tid, SCHED_FIFO,
¶m) == 0);
sleep (5);
check_run_order ("4,2");
states[2].flags = 0;
states[4].flags = 0;
param.sched_priority = states[2].priority;
assert (pthread_setschedparam (states[2].tid, SCHED_RR,
¶m) == 0);
param.sched_priority = states[4].priority;
assert (pthread_setschedparam (states[4].tid, SCHED_RR,
¶m) == 0);
send_cmd (4, CMD_RELEASE_MUTEX);
sleep (1);
for (i = 0; i < 3; i++)
assert (pthread_mutex_destroy (&m[i]) == 0);
}
}
int main (int argc, char *argv[])
{
pthread_mutexattr_t mattr;
pthread_condattr_t cattr;
pthread_attr_t pattr;
int i, policy, main_prio;
void * exit_status;
sigset_t mask;
struct sigaction act;
struct sched_param param;
char buf[30];
logfile = stdout;
assert (pthread_getschedparam (pthread_self (), &policy, ¶m) == 0);
main_prio = param.sched_priority;
sigfillset (&mask);
sigdelset (&mask, SIGINT);
sigprocmask (SIG_SETMASK, &mask, NULL);
sigemptyset (&act.sa_mask);
sigaddset (&act.sa_mask, SIGINT);
act.sa_handler = sighandler;
act.sa_flags = SA_RESTART;
sigaction (SIGINT, &act, NULL);
assert (pthread_attr_init (&pattr) == 0);
assert (pthread_attr_setdetachstate (&pattr,
PTHREAD_CREATE_JOINABLE) == 0);
assert (pthread_mutexattr_init (&mattr) == 0);
assert (pthread_mutex_init (&waiter_mutex, &mattr) == 0);
assert (pthread_mutex_init (&cond_mutex, &mattr) == 0);
assert (pthread_condattr_init (&cattr) == 0);
assert (pthread_cond_init (&cond_var, &cattr) == 0);
assert (pipe (pipefd) == 0);
#ifdef DEBUG
assert (pthread_switch_add_np (kern_switch) == 0);
#endif
for (i = 0; i < NUM_THREADS; i++) {
assert (pthread_cond_init (&states[i].cond_var, &cattr) == 0);
states[i].id = (u_int8_t) i;
states[i].status = 0;
states[i].cmd.cmd_id = CMD_NONE;
states[i].flags = 0;
assert (pthread_create (&states[i].tid, &pattr, waiter,
(void *) &states[i]) == 0);
param.sched_priority = main_prio - 10 + i;
states[i].priority = param.sched_priority;
assert (pthread_setschedparam (states[i].tid, SCHED_OTHER,
¶m) == 0);
snprintf (buf, sizeof(buf), "waiter_%d", i);
pthread_set_name_np (states[i].tid, buf);
}
sleep (1);
log_trace ("Done creating threads.\n");
log_info ("\n");
mutex_init_test ();
log_info ("\n");
mutex_destroy_test ();
log_info ("\n");
mutex_lock_test ();
log_info ("\n");
mutex_unlock_test ();
log_info ("\n");
queueing_order_test ();
log_info ("\n");
mutex_prioinherit_test ();
log_info ("\n");
mutex_prioceiling_test ();
log_info ("\n");
log_info ("Total tests %d, passed %d, failed %d\n",
total, pass_count, error_count);
log_trace ("Setting done flag.\n");
done = 1;
log_trace ("Trying to join threads.\n");
for (i = 0; i < NUM_THREADS; i++) {
send_cmd (i, CMD_NONE);
assert (pthread_join (states[i].tid, &exit_status) == 0);
}
close (pipefd[0]);
close (pipefd[1]);
if (error_count != 0)
exit (EX_OSERR);
else
exit (EX_OK);
}