fee5c3f1f8
Don't cast thread_self() to int. Cast to uintptr_t. Pull in the predefined printf format from <inttypes.h>
1556 lines
42 KiB
C
1556 lines
42 KiB
C
/*
|
|
* Copyright (c) 1998 Daniel M. 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 M. 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 M. 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.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include "pthread.h"
|
|
#include <sched.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sysexits.h>
|
|
|
|
#if defined(_LIBC_R_)
|
|
#include <pthread_np.h>
|
|
#endif
|
|
|
|
#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 (const char *, ...) __printflike(1, 2);
|
|
|
|
/*------------------------------------------------------------
|
|
* Types
|
|
*----------------------------------------------------------*/
|
|
|
|
typedef enum {
|
|
STAT_INITIAL, /* initial state */
|
|
STAT_WAITCONDVAR, /* waiting for condition variable signal */
|
|
STAT_WAITMUTEX /* waiting for mutex lock */
|
|
} 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;
|
|
|
|
|
|
/*------------------------------------------------------------
|
|
* Constants
|
|
*----------------------------------------------------------*/
|
|
|
|
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, /* M_POSIX */
|
|
PTHREAD_MUTEX_DEFAULT, /* M_SS2_DEFAULT */
|
|
PTHREAD_MUTEX_ERRORCHECK, /* M_SS2_ERRORCHECK */
|
|
PTHREAD_MUTEX_NORMAL, /* M_SS2_NORMAL */
|
|
PTHREAD_MUTEX_RECURSIVE /* M_SS2_RECURSIVE */
|
|
};
|
|
|
|
|
|
/*------------------------------------------------------------
|
|
* Objects
|
|
*----------------------------------------------------------*/
|
|
|
|
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;
|
|
|
|
|
|
/*------------------------------------------------------------
|
|
* Prototypes
|
|
*----------------------------------------------------------*/
|
|
extern char *strtok_r(char *str, const char *sep, char **last);
|
|
|
|
|
|
/*------------------------------------------------------------
|
|
* Functions
|
|
*----------------------------------------------------------*/
|
|
|
|
#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 (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 ();
|
|
}
|
|
|
|
|
|
/*
|
|
* Check to see that the threads ran in the specified order.
|
|
*/
|
|
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); /* tok has to be larger than 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)) {
|
|
|
|
/* Get the expected id: */
|
|
expected_id = (int) strtol (idstr, &endptr, 10);
|
|
assert ((endptr != NULL) && (*endptr == '\0'));
|
|
|
|
/* Read the actual id from the pipe: */
|
|
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) {
|
|
/* Clear the pipe: */
|
|
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", 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;
|
|
|
|
/* Block all signals except for interrupt.*/
|
|
sigfillset (&mask);
|
|
sigdelset (&mask, SIGINT);
|
|
sigprocmask (SIG_BLOCK, &mask, NULL);
|
|
|
|
while (done == 0) {
|
|
/* Wait for signal from the main thread to continue. */
|
|
statep->status = STAT_WAITMUTEX;
|
|
log_trace ("Thread %d: locking cond_mutex.\n",
|
|
(int) statep->id);
|
|
pthread_mutex_lock (&cond_mutex);
|
|
|
|
/* Do we report our status. */
|
|
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);
|
|
|
|
/* Wait for a command. */
|
|
statep->status = STAT_WAITCONDVAR;
|
|
|
|
/*
|
|
* The threads are allowed commanded to wait either on
|
|
* their own unique condition variable (so they may be
|
|
* separately signaled) or on one global condition variable
|
|
* (so they may be signaled together).
|
|
*/
|
|
if (use_global_condvar != 0)
|
|
pthread_cond_wait (&cond_var, &cond_mutex);
|
|
else
|
|
pthread_cond_wait (&statep->cond_var, &cond_mutex);
|
|
|
|
/* Do we report our status? */
|
|
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);
|
|
|
|
/* Get a copy of the command before releasing the mutex. */
|
|
cmd = statep->cmd;
|
|
|
|
/* Clear the command after copying it. */
|
|
statep->cmd.cmd_id = CMD_NONE;
|
|
|
|
/* Unlock the condition variable mutex. */
|
|
assert (pthread_mutex_unlock (&cond_mutex) == 0);
|
|
|
|
/* Peform the command.*/
|
|
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);
|
|
/* Spin for 15 seconds. */
|
|
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;
|
|
|
|
/* Do we report our status? */
|
|
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;
|
|
/* Do we report our status? */
|
|
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;
|
|
}
|
|
|
|
/* Wait for the big giant waiter lock. */
|
|
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%" PRIxPTR "\n",
|
|
(int) statep->id, (uintptr_t) 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;
|
|
|
|
/* Block all signals except for interrupt.*/
|
|
sigfillset (&mask);
|
|
sigdelset (&mask, SIGINT);
|
|
sigprocmask (SIG_BLOCK, &mask, NULL);
|
|
|
|
/* Wait for a signal to continue. */
|
|
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);
|
|
|
|
/* Unlock the condition variable mutex. */
|
|
assert (pthread_mutex_unlock (&cond_mutex) == 0);
|
|
|
|
statep->status = STAT_WAITMUTEX;
|
|
/* Lock the mutex once. */
|
|
assert (pthread_mutex_lock (statep->cmd.mutex) == 0);
|
|
|
|
/* Lock it again and capture the error. */
|
|
statep->ret = pthread_mutex_lock (statep->cmd.mutex);
|
|
statep->status = 0;
|
|
|
|
assert (pthread_mutex_unlock (statep->cmd.mutex) == 0);
|
|
|
|
/* Unlock it again if it is locked recursively. */
|
|
if (statep->ret == 0)
|
|
pthread_mutex_unlock (statep->cmd.mutex);
|
|
|
|
log_trace ("Thread %d: Exiting thread 0x%" PRIxPTR "\n",
|
|
(int) statep->id, (uintptr_t) pthread_self());
|
|
pthread_exit (arg);
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
static void
|
|
sighandler (int signo)
|
|
{
|
|
log ("Signal handler caught signal %d, thread id 0x%" PRIxPTR "\n",
|
|
signo, (uintptr_t) 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;
|
|
/* Clear the busy flags. */
|
|
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;
|
|
/* Clear the busy flags. */
|
|
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;
|
|
/* Clear the busy flags. */
|
|
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;
|
|
|
|
/*
|
|
* Initialize a mutex attribute.
|
|
*
|
|
* pthread_mutexattr_init not tested for: ENOMEM
|
|
*/
|
|
assert (pthread_mutexattr_init (&mattr) == 0);
|
|
|
|
/*
|
|
* Initialize a mutex.
|
|
*
|
|
* pthread_mutex_init not tested for: EAGAIN ENOMEM EPERM EBUSY
|
|
*/
|
|
log ("Testing pthread_mutex_init\n");
|
|
log ("--------------------------\n");
|
|
|
|
for (mproto = 0; mproto < NELEMENTS(protocols); mproto++) {
|
|
for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
|
|
/* Initialize the mutex attribute. */
|
|
assert (pthread_mutexattr_init (&mattr) == 0);
|
|
assert (pthread_mutexattr_setprotocol (&mattr,
|
|
protocols[mproto]) == 0);
|
|
|
|
/*
|
|
* Ensure that the first mutex type is a POSIX
|
|
* compliant mutex.
|
|
*/
|
|
if (mkind != M_POSIX) {
|
|
assert (pthread_mutexattr_settype (&mattr,
|
|
mutex_types[mkind]) == 0);
|
|
}
|
|
|
|
log (" Protocol %s, Type %s - ",
|
|
protocol_strs[mproto], mutextype_strs[mkind]);
|
|
ret = pthread_mutex_init (&mutex, &mattr);
|
|
check_result (/* expected */ 0, ret);
|
|
assert (pthread_mutex_destroy (&mutex) == 0);
|
|
|
|
/*
|
|
* Destroy a mutex attribute.
|
|
*
|
|
* XXX - There should probably be a magic number
|
|
* associated with a mutex attribute so that
|
|
* destroy can be reasonably sure the attribute
|
|
* is valid.
|
|
*
|
|
* pthread_mutexattr_destroy not tested for: EINVAL
|
|
*/
|
|
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;
|
|
thread_state_t state;
|
|
|
|
/*
|
|
* Destroy a mutex.
|
|
*
|
|
* XXX - There should probably be a magic number associated
|
|
* with a mutex so that destroy can be reasonably sure
|
|
* the mutex is valid.
|
|
*
|
|
* pthread_mutex_destroy not tested for:
|
|
*/
|
|
log ("Testing pthread_mutex_destroy\n");
|
|
log ("-----------------------------\n");
|
|
|
|
assert (pthread_attr_init (&pattr) == 0);
|
|
assert (pthread_attr_setdetachstate (&pattr,
|
|
PTHREAD_CREATE_DETACHED) == 0);
|
|
state.flags = 0; /* No flags yet. */
|
|
|
|
for (mproto = 0; mproto < NELEMENTS(protocols); mproto++) {
|
|
for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
|
|
/* Initialize the mutex attribute. */
|
|
assert (pthread_mutexattr_init (&mattr) == 0);
|
|
assert (pthread_mutexattr_setprotocol (&mattr,
|
|
protocols[mproto]) == 0);
|
|
|
|
/*
|
|
* Ensure that the first mutex type is a POSIX
|
|
* compliant mutex.
|
|
*/
|
|
if (mkind != M_POSIX) {
|
|
assert (pthread_mutexattr_settype (&mattr,
|
|
mutex_types[mkind]) == 0);
|
|
}
|
|
|
|
/* Create the mutex. */
|
|
assert (pthread_mutex_init (&mutex, &mattr) == 0);
|
|
|
|
log (" Protocol %s, Type %s\n",
|
|
protocol_strs[mproto], mutextype_strs[mkind]);
|
|
|
|
log (" Destruction of unused mutex - ");
|
|
assert (pthread_mutex_init (&mutex, &mattr) == 0);
|
|
ret = pthread_mutex_destroy (&mutex);
|
|
check_result (/* expected */ 0, ret);
|
|
|
|
log (" 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 (/* expected */ EBUSY, ret);
|
|
assert (pthread_mutex_unlock (&mutex) == 0);
|
|
assert (pthread_mutex_destroy (&mutex) == 0);
|
|
|
|
log (" 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 (/* expected */ EBUSY, ret);
|
|
send_cmd (0, CMD_RELEASE_ALL);
|
|
sleep (1);
|
|
assert (pthread_mutex_destroy (&mutex) == 0);
|
|
|
|
log (" 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 (/* expected */ 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;
|
|
|
|
/*
|
|
* Lock a mutex.
|
|
*
|
|
* pthread_lock not tested for:
|
|
*/
|
|
log ("Testing pthread_mutex_lock\n");
|
|
log ("--------------------------\n");
|
|
|
|
assert (pthread_attr_init (&pattr) == 0);
|
|
assert (pthread_attr_setdetachstate (&pattr,
|
|
PTHREAD_CREATE_DETACHED) == 0);
|
|
state.flags = 0; /* No flags yet. */
|
|
|
|
for (mproto = 0; mproto < NELEMENTS(protocols); mproto++) {
|
|
for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
|
|
/* Initialize the mutex attribute. */
|
|
assert (pthread_mutexattr_init (&mattr) == 0);
|
|
assert (pthread_mutexattr_setprotocol (&mattr,
|
|
protocols[mproto]) == 0);
|
|
|
|
/*
|
|
* Ensure that the first mutex type is a POSIX
|
|
* compliant mutex.
|
|
*/
|
|
if (mkind != M_POSIX) {
|
|
assert (pthread_mutexattr_settype (&mattr,
|
|
mutex_types[mkind]) == 0);
|
|
}
|
|
|
|
/* Create the mutex. */
|
|
assert (pthread_mutex_init (&mutex, &mattr) == 0);
|
|
|
|
log (" Protocol %s, Type %s\n",
|
|
protocol_strs[mproto], mutextype_strs[mkind]);
|
|
|
|
log (" Lock on unlocked mutex - ");
|
|
ret = pthread_mutex_lock (&mutex);
|
|
check_result (/* expected */ 0, ret);
|
|
pthread_mutex_unlock (&mutex);
|
|
|
|
log (" Lock on invalid mutex - ");
|
|
ret = pthread_mutex_lock (NULL);
|
|
check_result (/* expected */ EINVAL, ret);
|
|
|
|
log (" Lock on mutex held by self - ");
|
|
assert (pthread_create (&state.tid, &pattr, lock_twice,
|
|
(void *) &state) == 0);
|
|
/* Let the thread start. */
|
|
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);
|
|
/* Let the thread receive and process the command. */
|
|
sleep (1);
|
|
|
|
switch (mkind) {
|
|
case M_POSIX:
|
|
check_result (/* expected */ EDEADLK,
|
|
state.ret);
|
|
break;
|
|
case M_SS2_DEFAULT:
|
|
check_result (/* expected */ EDEADLK,
|
|
state.ret);
|
|
break;
|
|
case M_SS2_ERRORCHECK:
|
|
check_result (/* expected */ EDEADLK,
|
|
state.ret);
|
|
break;
|
|
case M_SS2_NORMAL:
|
|
check_result (/* expected */ 0xdeadbeef,
|
|
state.ret);
|
|
break;
|
|
case M_SS2_RECURSIVE:
|
|
check_result (/* expected */ 0, state.ret);
|
|
break;
|
|
}
|
|
pthread_mutex_destroy (&mutex);
|
|
pthread_mutexattr_destroy (&mattr);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
mutex_unlock_test (void)
|
|
{
|
|
const int test_thread_id = 0; /* ID of test thread */
|
|
pthread_mutexattr_t mattr;
|
|
pthread_mutex_t mutex;
|
|
int mproto, ret;
|
|
mutex_kind_t mkind;
|
|
|
|
/*
|
|
* Unlock a mutex.
|
|
*
|
|
* pthread_unlock not tested for:
|
|
*/
|
|
log ("Testing pthread_mutex_unlock\n");
|
|
log ("----------------------------\n");
|
|
|
|
for (mproto = 0; mproto < NELEMENTS(protocols); mproto++) {
|
|
for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
|
|
/* Initialize the mutex attribute. */
|
|
assert (pthread_mutexattr_init (&mattr) == 0);
|
|
assert (pthread_mutexattr_setprotocol (&mattr,
|
|
protocols[mproto]) == 0);
|
|
|
|
/*
|
|
* Ensure that the first mutex type is a POSIX
|
|
* compliant mutex.
|
|
*/
|
|
if (mkind != M_POSIX) {
|
|
assert (pthread_mutexattr_settype (&mattr,
|
|
mutex_types[mkind]) == 0);
|
|
}
|
|
|
|
/* Create the mutex. */
|
|
assert (pthread_mutex_init (&mutex, &mattr) == 0);
|
|
|
|
log (" Protocol %s, Type %s\n",
|
|
protocol_strs[mproto], mutextype_strs[mkind]);
|
|
|
|
log (" Unlock on mutex held by self - ");
|
|
assert (pthread_mutex_lock (&mutex) == 0);
|
|
ret = pthread_mutex_unlock (&mutex);
|
|
check_result (/* expected */ 0, ret);
|
|
|
|
log (" Unlock on invalid mutex - ");
|
|
ret = pthread_mutex_unlock (NULL);
|
|
check_result (/* expected */ EINVAL, ret);
|
|
|
|
log (" 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 (/* expected */ EPERM, ret);
|
|
break;
|
|
case M_SS2_DEFAULT:
|
|
check_result (/* expected */ EPERM, ret);
|
|
break;
|
|
case M_SS2_ERRORCHECK:
|
|
check_result (/* expected */ EPERM, ret);
|
|
break;
|
|
case M_SS2_NORMAL:
|
|
check_result (/* expected */ EPERM, ret);
|
|
break;
|
|
case M_SS2_RECURSIVE:
|
|
check_result (/* expected */ EPERM, ret);
|
|
break;
|
|
}
|
|
if (ret == 0) {
|
|
/*
|
|
* If for some reason we were able to unlock
|
|
* the mutex, relock it so that the test
|
|
* thread has no problems releasing the mutex.
|
|
*/
|
|
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 ("Testing queueing order\n");
|
|
log ("----------------------\n");
|
|
assert (pthread_mutex_lock (&waiter_mutex) == 0);
|
|
/*
|
|
* Tell the threads to report when they take the waiters mutex.
|
|
*/
|
|
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);
|
|
|
|
/* Signal the threads to continue. */
|
|
sleep (1);
|
|
|
|
/* Use the global condition variable next time. */
|
|
use_global_condvar = 1;
|
|
|
|
/* Release the waiting threads and allow them to run again. */
|
|
assert (pthread_mutex_unlock (&waiter_mutex) == 0);
|
|
sleep (1);
|
|
|
|
log (" 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) {
|
|
/* Tell the threads to report when they've been signaled. */
|
|
states[i].flags = FLAGS_REPORT_WAITCONDVAR;
|
|
}
|
|
|
|
/*
|
|
* Prevent the threads from continuing their loop after we
|
|
* signal them.
|
|
*/
|
|
assert (pthread_mutex_lock (&waiter_mutex) == 0);
|
|
|
|
|
|
log (" Queueing order on a condition variable - ");
|
|
/*
|
|
* Signal one thread to run and see that the highest priority
|
|
* thread executes.
|
|
*/
|
|
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");
|
|
|
|
/* Signal the remaining threads. */
|
|
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) {
|
|
/* Tell the threads not to report anything. */
|
|
states[i].flags = 0;
|
|
}
|
|
|
|
/* Use the thread unique condition variable next time. */
|
|
use_global_condvar = 0;
|
|
|
|
/* Allow the threads to continue their loop. */
|
|
assert (pthread_mutex_unlock (&waiter_mutex) == 0);
|
|
sleep (1);
|
|
}
|
|
|
|
|
|
static void
|
|
mutex_prioceiling_test (void)
|
|
{
|
|
const int test_thread_id = 0; /* ID of test thread */
|
|
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 ("Testing priority ceilings\n");
|
|
log ("-------------------------\n");
|
|
for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
|
|
|
|
log (" Protype PTHREAD_PRIO_PROTECT, Type %s\n",
|
|
mutextype_strs[mkind]);
|
|
|
|
/*
|
|
* Initialize and create a mutex.
|
|
*/
|
|
assert (pthread_mutexattr_init (&mattr) == 0);
|
|
|
|
/* Get this threads current priority. */
|
|
assert (pthread_getschedparam (pthread_self(), &policy,
|
|
¶m) == 0);
|
|
my_prio = param.sched_priority; /* save for later use */
|
|
log_trace ("Current scheduling policy %d, priority %d\n",
|
|
policy, my_prio);
|
|
|
|
/*
|
|
* Initialize and create 3 priority protection mutexes with
|
|
* default (max priority) ceilings.
|
|
*/
|
|
assert (pthread_mutexattr_setprotocol(&mattr,
|
|
PTHREAD_PRIO_PROTECT) == 0);
|
|
|
|
/*
|
|
* Ensure that the first mutex type is a POSIX
|
|
* compliant mutex.
|
|
*/
|
|
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);
|
|
|
|
/*
|
|
* Set the ceiling priorities for the 3 priority protection
|
|
* mutexes to, 5 less than, equal to, and 5 greater than,
|
|
* this threads current priority.
|
|
*/
|
|
for (i = 0; i < 3; i++)
|
|
assert (pthread_mutex_setprioceiling (&m[i],
|
|
my_prio - 5 + 5*i, &old_ceiling) == 0);
|
|
|
|
/*
|
|
* Check that if we attempt to take a mutex whose priority
|
|
* ceiling is lower than our priority, we get an error.
|
|
*/
|
|
log (" Lock with ceiling priority < thread priority - ");
|
|
ret = pthread_mutex_lock (&m[0]);
|
|
check_result (/* expected */ EINVAL, ret);
|
|
if (ret == 0)
|
|
pthread_mutex_unlock (&m[0]);
|
|
|
|
/*
|
|
* Check that we can take a mutex whose priority ceiling
|
|
* is equal to our priority.
|
|
*/
|
|
log (" Lock with ceiling priority = thread priority - ");
|
|
ret = pthread_mutex_lock (&m[1]);
|
|
check_result (/* expected */ 0, ret);
|
|
if (ret == 0)
|
|
pthread_mutex_unlock (&m[1]);
|
|
|
|
/*
|
|
* Check that we can take a mutex whose priority ceiling
|
|
* is higher than our priority.
|
|
*/
|
|
log (" Lock with ceiling priority > thread priority - ");
|
|
ret = pthread_mutex_lock (&m[2]);
|
|
check_result (/* expected */ 0, ret);
|
|
if (ret == 0)
|
|
pthread_mutex_unlock (&m[2]);
|
|
|
|
/*
|
|
* Have the test thread go into a busy loop for 5 seconds
|
|
* and see that it doesn't block this thread (since the
|
|
* priority ceiling of mutex 0 and the priority of the test
|
|
* thread are both less than the priority of this thread).
|
|
*/
|
|
log (" Preemption with ceiling priority < thread "
|
|
"priority - ");
|
|
/* Have the test thread take mutex 0. */
|
|
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 {
|
|
/* Let the thread finish its busy loop. */
|
|
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;
|
|
|
|
/* Have the test thread release mutex 0. */
|
|
send_cmd (test_thread_id, CMD_RELEASE_ALL);
|
|
sleep (1);
|
|
|
|
/*
|
|
* Have the test thread go into a busy loop for 5 seconds
|
|
* and see that it preempts this thread (since the priority
|
|
* ceiling of mutex 1 is the same as the priority of this
|
|
* thread). The test thread should not run to completion
|
|
* as its time quantum should expire before the 5 seconds
|
|
* are up.
|
|
*/
|
|
log (" Preemption with ceiling priority = thread "
|
|
"priority - ");
|
|
|
|
/* Have the test thread take mutex 1. */
|
|
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 {
|
|
/* Let the thread finish its busy loop. */
|
|
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;
|
|
|
|
/* Have the test thread release mutex 1. */
|
|
send_cmd (test_thread_id, CMD_RELEASE_ALL);
|
|
sleep (1);
|
|
|
|
/*
|
|
* Set the scheduling policy of the test thread to SCHED_FIFO
|
|
* and have it go into a busy loop for 5 seconds. This
|
|
* thread is SCHED_RR, and since the priority ceiling of
|
|
* mutex 1 is the same as the priority of this thread, the
|
|
* test thread should run to completion once it is switched
|
|
* in.
|
|
*/
|
|
log (" 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);
|
|
|
|
/* Have the test thread take mutex 1. */
|
|
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");
|
|
/* Let the thread finish it's busy loop. */
|
|
sleep (6);
|
|
}
|
|
else
|
|
log_pass ();
|
|
states[test_thread_id].flags &= ~FLAGS_WAS_BUSY;
|
|
|
|
/* Restore the test thread scheduling parameters. */
|
|
param.sched_priority = states[test_thread_id].priority;
|
|
assert (pthread_setschedparam (states[test_thread_id].tid,
|
|
SCHED_RR, ¶m) == 0);
|
|
|
|
/* Have the test thread release mutex 1. */
|
|
send_cmd (test_thread_id, CMD_RELEASE_ALL);
|
|
sleep (1);
|
|
|
|
/*
|
|
* Have the test thread go into a busy loop for 5 seconds
|
|
* and see that it preempts this thread (since the priority
|
|
* ceiling of mutex 2 is the greater than the priority of
|
|
* this thread). The test thread should run to completion
|
|
* and block this thread because its active priority is
|
|
* higher.
|
|
*/
|
|
log (" SCHED_FIFO scheduling and ceiling priority > "
|
|
"thread priority - ");
|
|
/* Have the test thread take mutex 2. */
|
|
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");
|
|
/* Let the thread finish it's busy loop. */
|
|
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;
|
|
|
|
/* Have the test thread release mutex 2. */
|
|
send_cmd (test_thread_id, CMD_RELEASE_ALL);
|
|
sleep (1);
|
|
|
|
/* Destroy the mutexes. */
|
|
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;
|
|
|
|
/* Get this threads current priority. */
|
|
assert (pthread_getschedparam (pthread_self(), &policy,
|
|
¶m) == 0);
|
|
my_prio = param.sched_priority; /* save for later use */
|
|
log_trace ("Current scheduling policy %d, priority %d\n",
|
|
policy, my_prio);
|
|
|
|
log ("Testing priority inheritence\n");
|
|
log ("----------------------------\n");
|
|
for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
|
|
|
|
log (" Protype PTHREAD_PRIO_INHERIT, Type %s\n",
|
|
mutextype_strs[mkind]);
|
|
|
|
/*
|
|
* Initialize and create a mutex.
|
|
*/
|
|
assert (pthread_mutexattr_init (&mattr) == 0);
|
|
|
|
/*
|
|
* Initialize and create 3 priority inheritence mutexes with
|
|
* default (max priority) ceilings.
|
|
*/
|
|
assert (pthread_mutexattr_setprotocol(&mattr,
|
|
PTHREAD_PRIO_INHERIT) == 0);
|
|
|
|
/*
|
|
* Ensure that the first mutex type is a POSIX
|
|
* compliant mutex.
|
|
*/
|
|
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);
|
|
|
|
/*
|
|
* Test setup:
|
|
* Thread 4 - take mutex 0, 1
|
|
* Thread 2 - enter protected busy loop with mutex 0
|
|
* Thread 3 - enter protected busy loop with mutex 1
|
|
* Thread 4 - enter protected busy loop with mutex 2
|
|
* Thread 5 - enter busy loop
|
|
* Thread 6 - enter protected busy loop with mutex 0
|
|
* Thread 4 - releases mutexes 1 and 0.
|
|
*
|
|
* Expected results:
|
|
* Threads complete in order 4, 6, 5, 3, 2
|
|
*/
|
|
log (" Simple inheritence test - ");
|
|
|
|
/*
|
|
* Command thread 4 to take mutexes 0 and 1.
|
|
*/
|
|
send_mutex_cmd (4, CMD_TAKE_MUTEX, &m[0]);
|
|
sleep (1); /* Allow command to be received. */
|
|
send_mutex_cmd (4, CMD_TAKE_MUTEX, &m[1]);
|
|
sleep (1);
|
|
|
|
/*
|
|
* Tell the threads to report themselves when they are
|
|
* at the bottom of their loop (waiting on wait_mutex).
|
|
*/
|
|
for (i = 0; i < NUM_THREADS; i++)
|
|
states[i].flags |= FLAGS_REPORT_WAITMUTEX;
|
|
|
|
/*
|
|
* Command thread 2 to take mutex 0 and thread 3 to take
|
|
* mutex 1, both via a protected operation command. Since
|
|
* thread 4 owns mutexes 0 and 1, both threads 2 and 3
|
|
* will block until the mutexes are released by thread 4.
|
|
*/
|
|
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);
|
|
|
|
/*
|
|
* Command thread 4 to take mutex 2 via a protected operation
|
|
* and thread 5 to enter a busy loop for 5 seconds. Since
|
|
* thread 5 has higher priority than thread 4, thread 5 will
|
|
* enter the busy loop before thread 4 is activated.
|
|
*/
|
|
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");
|
|
|
|
/*
|
|
* Clear the flags.
|
|
*/
|
|
for (i = 0; i < NUM_THREADS; i++)
|
|
states[i].flags = 0;
|
|
|
|
/*
|
|
* Test setup:
|
|
* Thread 2 - enter busy loop (SCHED_FIFO)
|
|
* Thread 4 - take mutex 0
|
|
* Thread 4 - priority change to same priority as thread 2
|
|
* Thread 4 - release mutex 0
|
|
*
|
|
* Expected results:
|
|
* Since thread 4 owns a priority mutex, it should be
|
|
* placed at the front of the run queue (for its new
|
|
* priority slot) when its priority is lowered to the
|
|
* same priority as thread 2. If thread 4 did not own
|
|
* a priority mutex, then it would have been added to
|
|
* the end of the run queue and thread 2 would have
|
|
* executed until it blocked (because it's scheduling
|
|
* policy is SCHED_FIFO).
|
|
*
|
|
*/
|
|
log (" Inheritence test with change of priority - ");
|
|
|
|
/*
|
|
* Change threads 2 and 4 scheduling policies to be
|
|
* SCHED_FIFO.
|
|
*/
|
|
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);
|
|
|
|
/*
|
|
* Command thread 4 to take mutex 0.
|
|
*/
|
|
send_mutex_cmd (4, CMD_TAKE_MUTEX, &m[0]);
|
|
sleep (1);
|
|
|
|
/*
|
|
* Command thread 2 to enter busy loop.
|
|
*/
|
|
send_cmd (2, CMD_BUSY_LOOP);
|
|
sleep (1); /* Allow command to be received. */
|
|
|
|
/*
|
|
* Command thread 4 to enter busy loop.
|
|
*/
|
|
send_cmd (4, CMD_BUSY_LOOP);
|
|
sleep (1); /* Allow command to be received. */
|
|
|
|
/* Have threads 2 and 4 report themselves. */
|
|
states[2].flags = FLAGS_REPORT_WAITMUTEX;
|
|
states[4].flags = FLAGS_REPORT_WAITMUTEX;
|
|
|
|
/* Change the priority of thread 4. */
|
|
param.sched_priority = states[2].priority;
|
|
assert (pthread_setschedparam (states[4].tid, SCHED_FIFO,
|
|
¶m) == 0);
|
|
sleep (5);
|
|
check_run_order ("4,2");
|
|
|
|
/* Clear the flags */
|
|
states[2].flags = 0;
|
|
states[4].flags = 0;
|
|
|
|
/* Reset the policies. */
|
|
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);
|
|
|
|
/* Destroy the mutexes. */
|
|
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;
|
|
|
|
logfile = stdout;
|
|
|
|
assert (pthread_getschedparam (pthread_self (), &policy, ¶m) == 0);
|
|
main_prio = param.sched_priority;
|
|
|
|
/* Setupt our signal mask. */
|
|
sigfillset (&mask);
|
|
sigdelset (&mask, SIGINT);
|
|
sigprocmask (SIG_SETMASK, &mask, NULL);
|
|
|
|
/* Install a signal handler for SIGINT */
|
|
sigemptyset (&act.sa_mask);
|
|
sigaddset (&act.sa_mask, SIGINT);
|
|
act.sa_handler = sighandler;
|
|
act.sa_flags = SA_RESTART;
|
|
sigaction (SIGINT, &act, NULL);
|
|
|
|
/*
|
|
* Initialize the thread attribute.
|
|
*/
|
|
assert (pthread_attr_init (&pattr) == 0);
|
|
assert (pthread_attr_setdetachstate (&pattr,
|
|
PTHREAD_CREATE_JOINABLE) == 0);
|
|
|
|
/*
|
|
* Initialize and create the waiter and condvar mutexes.
|
|
*/
|
|
assert (pthread_mutexattr_init (&mattr) == 0);
|
|
assert (pthread_mutex_init (&waiter_mutex, &mattr) == 0);
|
|
assert (pthread_mutex_init (&cond_mutex, &mattr) == 0);
|
|
|
|
/*
|
|
* Initialize and create a condition variable.
|
|
*/
|
|
assert (pthread_condattr_init (&cattr) == 0);
|
|
assert (pthread_cond_init (&cond_var, &cattr) == 0);
|
|
|
|
/* Create a pipe to catch the results of thread wakeups. */
|
|
assert (pipe (pipefd) == 0);
|
|
|
|
#ifdef DEBUG
|
|
assert (pthread_switch_add_np (kern_switch) == 0);
|
|
#endif
|
|
|
|
/*
|
|
* Create the waiting threads.
|
|
*/
|
|
for (i = 0; i < NUM_THREADS; i++) {
|
|
assert (pthread_cond_init (&states[i].cond_var, &cattr) == 0);
|
|
states[i].id = (u_int8_t) i; /* NUM_THREADS must be <= 256 */
|
|
states[i].status = 0;
|
|
states[i].cmd.cmd_id = CMD_NONE;
|
|
states[i].flags = 0; /* No flags yet. */
|
|
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);
|
|
#if defined(_LIBC_R_)
|
|
{
|
|
char buf[30];
|
|
|
|
snprintf (buf, sizeof(buf), "waiter_%d", i);
|
|
pthread_set_name_np (states[i].tid, buf);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Allow the threads to start. */
|
|
sleep (1);
|
|
log_trace ("Done creating threads.\n");
|
|
|
|
log ("\n");
|
|
mutex_init_test ();
|
|
log ("\n");
|
|
mutex_destroy_test ();
|
|
log ("\n");
|
|
mutex_lock_test ();
|
|
log ("\n");
|
|
mutex_unlock_test ();
|
|
log ("\n");
|
|
queueing_order_test ();
|
|
log ("\n");
|
|
mutex_prioinherit_test ();
|
|
log ("\n");
|
|
mutex_prioceiling_test ();
|
|
log ("\n");
|
|
|
|
log ("Total tests %d, passed %d, failed %d\n",
|
|
total, pass_count, error_count);
|
|
|
|
/* Set the done flag and signal the threads to exit. */
|
|
log_trace ("Setting done flag.\n");
|
|
done = 1;
|
|
|
|
/*
|
|
* Wait for the threads to finish.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/* Clean up after ourselves. */
|
|
close (pipefd[0]);
|
|
close (pipefd[1]);
|
|
|
|
if (error_count != 0)
|
|
exit (EX_OSERR); /* any better ideas??? */
|
|
else
|
|
exit (EX_OK);
|
|
}
|