4. assign3/assign3_part2/worker.c
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "scheduler.h"
/****************************************************
***************************
*
* Implement these functions.
*
*****************************************************
*************************/
/* Handler for SIGTERM signal */
void cancel_thread()
{
printf("Thread %u: terminating.n", (unsigned
int)pthread_self());
/* signal that done in queue */
sem_post(&queue_sem);
pthread_exit(NULL);
}
/* TODO: Handle the SIGUSR1 signal */
void suspend_thread()
{
printf("Thread %u: suspending.n", (unsigned
5. int)pthread_self());
/*add your code here to wait for a resume signal from the
scheduler*/
printf("Thread %u: resuming.n",(unsigned int)
pthread_self());
}
/****************************************************
***************************
*
*
*
*****************************************************
*************************/
/*
* waits to gain access to the scheduler queue.
*/
static int enter_scheduler_queue(thread_info_t *info)
{
/*
* wait for available room in queue.
* create a new list entry for this thread
* store this thread info in the new entry.
*/
sem_wait(&queue_sem);
list_elem *item = (list_elem*)malloc(sizeof(list_elem));
info->le = item;
item->info = info;
item->prev = 0;
item->next = 0;
list_insert_tail(&sched_queue, item);
return 0;
}
6. /*
* leaves the scheduler queue
*/
void leave_scheduler_queue(thread_info_t *info)
{
printf("Thread %lu: leaving scheduler queue.n", info-
>thrid);
/*
* remove the given worker from queue
* clean up the memory that we malloc'd for the list
* clean up the memory that was passed to us
*/
list_remove(&sched_queue, info->le);
free(info->le);
free(info);
}
/*
* Initialize thread, enter scheduling queue, and execute
instructions.
* arg is a pointer to thread_info_t
*/
void *start_worker(void *arg)
{
thread_info_t *info = (thread_info_t *) arg;
float calc = 0.8;
int j = 0;
/* TODO: Block SIGALRM and SIGUSR2. */
/* TODO: Unblock SIGUSR1 and SIGTERM. */
/* compete with other threads to enter queue. */
15. my_wait++;
}
} else {
queue[queue_index] = 0;
in_queue--;
}
} else {
term = 0;
susp = 1;
}
prev = tid;
my_run++;
my_wait += in_queue+(num_workers-worker_index)-1+term;
queue_index= (queue_index+1)%queue_size;
}
quit_if(fscanf(stream, "Th%c",&dummyc) != 1);
if (dummyc=='r') {
quit_if(fscanf(stream, "ead %lu: terminating.nThe",&tid)!=1
|| tid != prev);
}
quit_if(fscanf(stream, " total wait time is %f
seconds.n",&tot_wait) != 1);
quit_if(fscanf(stream, "The total run time is %f
seconds.n",&tot_run) != 1);
quit_if(fscanf(stream, "The average wait time is %f
seconds.n",&ave_wait) != 1);
quit_if(fscanf(stream, "The average run time is %f
seconds.n",&ave_run) != 1);
if (dummyc=='e')
quit_if(fscanf(stream, "Thread %lu: terminating.nThe",&tid)
!= 1|| tid != prev);
quit_if(abs(tot_wait-my_wait)>1);
quit_if(abs(tot_run-my_run)>1);
quit_if(abs(tot_wait/num_workers-ave_wait)>.5);
quit_if(abs(tot_run/num_workers-ave_run)>.5);
return 0;
16. }
int general_test(int argc, const char **argv) {
FILE *f;
int nw, qs, *q;
run_test(argc,argv);
f = fopen("smp5.out","r");
args_to_nums(argc,argv,&nw,&qs,&q);
test_output(f,nw,qs,q);
return EXIT_SUCCESS;
}
int specific_test(int nw, int qs, int *q) {
FILE *f;
int argc;
char **argv;
nums_to_args(nw,qs,q,&argc,&argv);
run_test(argc,(const char **)argv);
f = fopen("smp5.out","r");
test_output(f,nw,qs,q);
return EXIT_SUCCESS;
}
int test_3_1_2_2_2() {
int q[3] = {2,2,2};
return specific_test(3,1,q);
}
int test_2_2_2_2() {
int q[2]={2,2};
return specific_test(2,2,q);
}
int test_5_7_1_2_1_2_1() {
int q[5] = {1,2,1,2,1};
17. return specific_test(5,7,q);
}
int test_4_1_1_2_3_4() {
int q[4] = {1,2,3,4};
return specific_test(4,1,q);
}
int test_3_3_4_3_2() {
int q[3] = {4,3,2};
return specific_test(3,3,q);
}
/*
* Main entry point for SMP% test harness
*/
int run_smp5_tests(int argc, const char **argv)
{
/* Tests can be invoked by matching their name or their
suite name
* or 'all' */
testentry_t tests[] = {
{"test_3_1_2_2_2", "rr", test_3_1_2_2_2},
{"test_2_2_2_2", "rr", test_2_2_2_2},
{"test_5_7_1_2_1_2_1", "rr", test_5_7_1_2_1_2_1},
{"test_4_1_1_2_3_4", "rr", test_4_1_1_2_3_4},
{"test_3_3_4_3_2", "rr", test_3_3_4_3_2},
{"general", "gen", general_test}
};
int result = run_testrunner(argc, argv, tests, sizeof(tests) /
sizeof(testentry_t));
unlink("smp5.out");
return result;
}
/* The real main function. */
18. int main(int argc, const char **argv)
{
if (argc > 1 && !strcmp(argv[1], "-test")) {
return run_smp5_tests(argc - 1, argv + 1);
} else {
return smp5_main(argc, argv);
}
}
__MACOSX/assign3/assign3_part2/._smp5_tests.c
assign3/assign3_part2/testrunner.h
/*************** YOU SHOULD NOT MODIFY ANYTHING
IN THIS FILE ***************/
typedef int (*test_fp) (int, const char **);
typedef struct {
const char *name;
const char *suite;
test_fp test_function;
} testentry_t;
int run_testrunner(int argc, const char **argv, testentry_t *
entries,
int entry_count);
void set_testrunner_default_timeout(int s);
void set_testrunner_timeout(int s);
__MACOSX/assign3/assign3_part2/._testrunner.h
assign3/assign3_part2/worker.h
#ifndef __WORKER_H_
#define __WORKER_H_
20. __MACOSX/assign3/assign3_part2/._list.h
assign3/assign3_part2/README.txt
SMP5: Scheduler with Signals
============================
This MP is a variation of SMP4.
In the last MP, we built a simulated OS process scheduler. The
scheduler can
hold only a certain number of processes (workers) at one time.
Once the process
has been accepted into the scheduler, the scheduler decides in
what order the
processes execute. We implemented two scheduling algorithms:
FIFO and Round
Robin.
In this MP, we are to simulate a time-sharing system by using
signals and
timers. We will only implement the Round Robin algorithm.
Instead of using
iterations to model the concept of "time slices" (as in the last
MP), we use
interval timers. The scheduler is installed with an interval
timer. The timer
starts ticking when the scheduler picks a thread to use the CPU
which in turn
signals the thread when its time slice is finished thus allowing
the scheduler
to pick another thread and so on. When a thread has completely
finished its work
it leaves the scheduler to allow a waiting thread to enter. Please
note that in
this MP, only the timer and scheduler send signals. The threads
passively handle
21. the signals without signaling back to the scheduler.
The program takes a number of arguments. Arg1 determines the
number of jobs
(threads in our implementation) created; arg2 specifies the
queue size of the
scheduler. Arg3 through argN gives the duration (the required
time slices to
complete a job) of each job. Hence if we create 2 jobs, we
should supply arg3
and arg4 for the required duration. You can assume that the
autograder will
always supply the correct number of arguments and hence you
do not have to
detect invalid input.
Here is an example of program output, once the program is
complete:
% scheduler 3 2 3 2 3
Main: running 3 workers with queue size 2 for quanta:
3 2 3
Main: detaching worker thread 3075926960.
Main: detaching worker thread 3065437104.
Main: detaching worker thread 3054947248.
Main: waiting for scheduler 3086416816.
Scheduler: waiting for workers.
Thread 3075926960: in scheduler queue.
Thread 3075926960: suspending.
Thread 3065437104: in scheduler queue.
Thread 3065437104: suspending.
Scheduler: scheduling.
Scheduler: resuming 3075926960.
Thread 3075926960: resuming.
Scheduler: suspending 3075926960.
Scheduler: scheduling.
23. Thread 3054947248: resuming.
Scheduler: suspending 3054947248.
Thread 3054947248: leaving scheduler queue.
Thread 3054947248: terminating.
The total wait time is 12.062254 seconds.
The total run time is 7.958618 seconds.
The average wait time is 4.020751 seconds.
The average run time is 2.652873 seconds.
The goal of this MP is to help you understand (1) how signals
and timers work,
and (2) how to evaluate the performance of your program. You
will first
implement the time-sharing system using timers and signals.
Then, you will
evaluate the overall performance of your program by keeping
track of how long
each thread is idle, running, etc.
The program will use these four signals:
SIGALRM: sent by the timer to the scheduler, to indicate
another time
quantum has passed.
SIGUSR1: sent by the scheduler to a worker, to tell it to
suspend.
SIGUSR2: sent by the scheduler to a suspended worker, to tell
it to resume.
SIGTERM: sent by the scheduler to a worker, to tell it to
cancel.
You will need to set up the appropriate handlers and masks for
these signals.
24. You will use these functions:
clock_gettime
pthread_sigmask
pthread_kill
sigaction
sigaddset
sigemptyset
sigwait
timer_settime
timer_create
Also, make sure you understand how the POSIX:TMR interval
timer works.
There are two ways you can test your code. You can run the
built-in grading
tests by running "scheduler -test -f0 rr". This runs 5 tests, each
of which can
be run individually. You can also test you program with
specific parameters by
running "scheduler -test gen ..." where the ellipsis contains the
parameters you
would pass to scheduler.
Programming
===========
Part I: Modify the scheduler code (scheduler.c)
-----------------------------------------------
We use the scheduler thread to setup the timer and handle the
scheduling for the
system. The scheduler handles the SIGALRM events that come
from the timer, and
sends out signals to the worker threads.
25. Step 1.
Modify the code in init_sched_queue() function in scheduler.c
to initialize the
scheduler with a POSIX:TMR interval timer. Use
CLOCK_REALTIME in timer_create().
The timer will be stored in the global variable "timer", which
will be started
in scheduler_run() (see Step 4 below).
Step 2.
Implement setup_sig_handlers(). Use sigaction() to install
signal handlers for
SIGALRM, SIGUSR1, and SIGTERM. SIGALRM should
trigger timer_handler(), SIGUSR1
should trigger suspend_thread(), and SIGTERM should trigger
cancel_thread().
Notice no handler is installed for SIGUSR2; this signal will be
handled
differently, in step 8.
Step 3.
In the scheduler_run() function, start the timer. Use
timer_settime(). The
time quantum (1 second) is given in scheduler.h. The timer
should go off
repeatedly at regular intervals defined by the timer quantum.
In Round-Robin, whenever the timer goes off, the scheduler
suspends the
currently running thread, and tells the next thread to resume its
operations
26. using signals. These steps are listed in timer_handler(), which is
called every
time the timer goes off. In this implementation, the timer
handler makes use of
suspend_worker() and resume_worker() to accomplush these
steps.
Step 4.
Complete the suspend_worker() function. First, update the
info->quanta value.
This is the number of quanta that remain for this thread to
execute. It is
initialized to the value passed on the command line, and
decreases as the thread
executes. If there is any more work for this worker to do, send
it a signal to
suspend, and update the scheduler queue. Otherwise, cancel the
thread.
Step 5.
Complete the cancel_worker() function by sending the
appropriate signal to the
thread, telling it to kill itself.
Step 6.
Complete the resume_worker() function by sending the
appropriate signal to the
thread, telling it to resume execution.
Part II: Modify the worker code (worker.c)
------------------------------------------
27. In this section, you will modify the worker code to correctly
handle the signals
from the scheduler that you implemented in the previous
section.
You need to modify the thread functions so that it immediately
suspends the
thread, waiting for a resume signal from the scheduler. You will
need to use
sigwait() to force the thread to suspend itself and wait for a
resume signal.
You need also to implement a signal handler in worker.c to
catch and handle the
suspend signals.
Step 7.
Modify start_worker() to (1) block SIGUSR2 and SIGALRM,
and (2) unblock SIGUSR1
and SIGTERM.
Step 8.
Implement suspend_thread(), the handler for the SIGUSR1
signal. The
thread should block until it receives a resume (SIGUSR2)
signal.
Part III: Modify the evaluation code (scheduler.c)
--------------------------------------------------
This program keeps track of run time, and wait time. Each
thread saves these
two values regarding its own execution in its thread_info_t.
Tracking these
28. values requires also knowing the last time the thread suspended
or resumed.
Therefore, these two values are also kept in thread_info_t. See
scheduler.h.
In this section, you will implement the functions that calculate
run time and
wait time. All code that does this will be in scheduler.c. When
the program
is done, it will collect all these values, and print out the total
and average
wait time and run time. For your convenience, you are given a
function
time_difference() to compute the difference between two times
in microseconds.
Step 9.
Modify create_workers() to initialize the various time variables.
Step 10.
Implement update_run_time(). This is called by
suspend_worker().
Step 11.
Implement update_wait_time(). This is called by
resume_worker().
Questions
==========
Question 1.
Why do we block SIGUSR2 and SIGALRM in worker.c? Why
29. do we unblock SIGUSR1 and
SIGTERM in worker.c?
Question 2.
We use sigwait() and sigaction() in our code. Explain the
difference between the
two. (Please explain from the aspect of thread behavior rather
than syntax).
Question 3.
When we use POSIX:TMR interval timer, we are using relative
time. What is the
alternative? Explain the difference between the two.
Question 4.
Look at start_worker() in worker.c, a worker thread is executing
within an
infinite loop at the end. When does a worker thread terminate?
Question 5.
When does the scheduler finish? Why does it not exit when the
scheduler queue
is empty?
Question 6.
After a thread is scheduled to run, is it still in the sched_queue?
When is it
removed from the head of the queue? When is it removed from
the queue completely?
Question 7.
We've removed all other condition variables in SMP4, and
replaced them with a
timer and signals. Why do we still use the semaphore
queue_sem?
30. Question 8.
What's the purpose of the global variable "completed" in
scheduler.c? Why do we
compare "completed" with thread_count before we
wait_for_queue() in
next_worker()?
Question 9.
We only implemented Round Robin in this SMP. If we want to
implement a FIFO
scheduling algorithm and keep the modification as minimum,
which function in
scheduler.c is the one that you should modify? Briefly describe
how you would
modify this function.
Question 10.
In this implementation, the scheduler only changes threads
when the time quantum
expires. Briefly explain how you would use an additional signal
to allow the
scheduler to change threads in the middle of a time quantum. In
what situations
would this be useful?
__MACOSX/assign3/assign3_part2/._README.txt
assign3/assign3_part2/scheduler.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
31. #include "scheduler.h"
#include "worker.h"
/*
* define the extern global variables here.
*/
sem_t queue_sem; /* semaphore for scheduler queue
*/
thread_info_list sched_queue; /* list of current workers */
static int quit = 0;
static timer_t timer;
static thread_info_t *currentThread= 0;
static long wait_times;
static long run_times;
static int completed = 0;
static int thread_count = 0;
static void exit_error(int); /* helper function. */
static void wait_for_queue();
/****************************************************
***************************
*
* Implement these functions.
*
*****************************************************
*************************/
/*
* This function intializes the queue semaphore and the queue
itself.
*/
/*
32. * Update the worker's current running time.
* This function is called every time the thread is suspended.
*/
void update_run_time(thread_info_t *info) {
/* TODO: implement this function */
}
/*
* Update the worker's current waiting time.
* This function is called every time the thread resumes.
*/
void update_wait_time(thread_info_t *info) {
/* TODO: implement this function */
}
static void init_sched_queue(int queue_size)
{
/* set up a semaphore to restrict access to the queue */
sem_init(&queue_sem, 0, queue_size);
/* initialize the scheduler queue */
sched_queue.head = sched_queue.tail = 0;
pthread_mutex_init(&sched_queue.lock, NULL);
/* TODO: initialize the timer */
}
/*
* signal a worker thread that it can resume.
*/
static void resume_worker(thread_info_t *info)
{
printf("Scheduler: resuming %lu.n", info->thrid);
33. /*
* TODO: signal the worker thread that it can resume
*/
/* update the wait time for the thread */
update_wait_time(info);
}
/*send a signal to the thread, telling it to kill itself*/
void cancel_worker(thread_info_t *info)
{
/* TODO: send a signal to the thread, telling it to kill
itself*/
/* Update global wait and run time info */
wait_times += info->wait_time;
run_times += info->run_time;
completed++;
/* Update schedule queue */
leave_scheduler_queue(info);
if (completed >= thread_count) {
sched_yield(); /* Let other threads terminate. */
printf("The total wait time is %f seconds.n",
(float)wait_times / 1000000);
printf("The total run time is %f seconds.n",
(float)run_times / 1000000);
printf("The average wait time is %f seconds.n",
(float)wait_times / 1000000 / thread_count);
printf("The average run time is %f seconds.n",
(float)run_times / 1000000 / thread_count);
}
}
34. /*
* signals a worker thread that it should suspend.
*/
static void suspend_worker(thread_info_t *info)
{
int whatgoeshere = 0;
printf("Scheduler: suspending %lu.n", info->thrid);
/*update the run time for the thread*/
update_run_time(info);
/* TODO: Update quanta remaining. */
/* TODO: decide whether to cancel or suspend thread */
if(whatgoeshere) {
/*
* Thread still running: suspend.
* TODO: Signal the worker thread that it should
suspend.
*/
/* Update Schedule queue */
list_remove(&sched_queue,info->le);
list_insert_tail(&sched_queue,info->le);
} else {
/* Thread done: cancel */
cancel_worker(info);
}
}
/*
* this is the scheduling algorithm
* pick the next worker thread from the available list
* you may need to add new information to the thread_info
35. struct
*/
static thread_info_t *next_worker()
{
if (completed >= thread_count)
return 0;
wait_for_queue();
printf("Scheduler: scheduling.n");
/* return the thread_info_t for the next thread to run */
return sched_queue.head->info;
}
void timer_handler()
{
thread_info_t *info = 0;
/* once the last worker has been removed, we're done. */
if (list_size(&sched_queue) == 0) {
quit = 1;
return;
}
/*suspend the current worker*/
if (currentThread)
suspend_worker(currentThread);
//resume the next worker
info = next_worker();
/* Update currentThread */
currentThread = info;
if (info)
resume_worker(info);
36. else
quit = 1;
}
/*
* Set up the signal handlers for SIGALRM, SIGUSR1, and
SIGTERM.
* TODO: Implement this function.
*/
void setup_sig_handlers() {
/* Setup timer handler for SIGALRM signal in scheduler */
/* Setup cancel handler for SIGTERM signal in workers */
/* Setup suspend handler for SIGUSR1 signal in workers
*/
}
/****************************************************
***************************
*
*
*
*****************************************************
*************************/
/*
* waits until there are workers in the scheduling queue.
*/
static void wait_for_queue()
{
while(!list_size(&sched_queue)) {
37. printf("Scheduler: waiting for workers.n");
sched_yield();
}
}
/*
* runs at the end of the program just before exit.
*/
static void clean_up()
{
/*
* destroy any mutexes/condition variables/semaphores
that were created.
* free any malloc'd memory not already free'd
*/
sem_destroy(&queue_sem);
pthread_mutex_destroy(&sched_queue.lock);
}
/*
* prints the program help message.
*/
static void print_help(const char *progname)
{
printf("usage: %s <num_threads> <queue_size> <i_1, i_2
... i_numofthreads>n", progname);
printf("tnum_threads: the number of worker threads to
runn");
printf("tqueue_size: the number of threads that can be in
the scheduler at one timen");
printf("ti_1, i_2 ...i_numofthreads: the number of quanta
each worker thread runsn");
}
/*
* prints an error summary and exits.
38. */
static void exit_error(int err_num)
{
fprintf(stderr, "failure: %sn", strerror(err_num));
exit(1);
}
/*
* creates the worker threads.
*/
static void create_workers(int thread_count, int *quanta)
{
int i = 0;
int err = 0;
for (i = 0; i < thread_count; i++) {
thread_info_t *info = (thread_info_t *)
malloc(sizeof(thread_info_t));
info->quanta = quanta[i];
if ((err = pthread_create(&info->thrid, NULL,
start_worker, (void *)info)) != 0) {
exit_error(err);
}
printf("Main: detaching worker thread %lu.n", info-
>thrid);
pthread_detach(info->thrid);
/* TODO: initialize the time variables for each thread
for performance evalution*/
}
}
/*
* runs the scheduler.
39. */
static void *scheduler_run(void *unused)
{
wait_for_queue();
/* TODO: start the timer */
/*keep the scheduler thread alive*/
while( !quit )
sched_yield();
return NULL;
}
/*
* starts the scheduler.
* returns 0 on success or exits program on failure.
*/
static int start_scheduler(pthread_t *thrid)
{
int err = 0;
if ((err = pthread_create(thrid, NULL, scheduler_run, 0))
!= 0) {
exit_error(err);
}
return err;
}
/*
* reads the command line arguments and starts the scheduler &
worker threads.
*/
int smp5_main(int argc, const char** argv)
{
40. int queue_size = 0;
int ret_val = 0;
int *quanta,i;
pthread_t sched_thread;
/* check the arguments. */
if (argc < 3) {
print_help(argv[0]);
exit(0);
}
thread_count = atoi(argv[1]);
queue_size = atoi(argv[2]);
quanta = (int*)malloc(sizeof(int)*thread_count);
if (argc != 3 + thread_count) {
print_help(argv[0]);
exit(0);
}
for ( i = 0; i < thread_count; i++)
quanta[i] = atoi(argv[i+3]);
printf("Main: running %d workers with queue size %d for
quanta:n", thread_count, queue_size);
for ( i = 0; i < thread_count; i++)
printf(" %d", quanta[i]);
printf("n");
/*setup the sig handlers for scheduler and workers*/
setup_sig_handlers();
/* initialize anything that needs to be done for the
scheduler queue. */
init_sched_queue(queue_size);
/* creates a thread for the scheduler. */
41. start_scheduler(&sched_thread);
/* creates the worker threads and returns. */
create_workers(thread_count, quanta);
/* wait for scheduler to finish */
printf("Main: waiting for scheduler %lu.n",
sched_thread);
pthread_join(sched_thread, (void **) &ret_val);
/* clean up our resources */
clean_up();
/* this will wait for all other threads */
pthread_exit(0);
}
long time_difference(const struct timespec *time1, const struct
timespec *time2) {
return (time1->tv_sec - time2->tv_sec) * 1000000 +
(time1->tv_nsec - time2->tv_nsec) / 1000;
}
__MACOSX/assign3/assign3_part2/._scheduler.c
assign3/assign3_part2/testrunner.c
/*************** YOU SHOULD NOT MODIFY ANYTHING
IN THIS FILE ***************/
/*
A simple testrunner framework
Original Author: L. Angrave
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
43. return s1 && s2 && !strcmp(s1, s2);
}
/* Callback function for qsort on strings */
static int mystrcmp(const void *p1, const void *p2)
{
return eql((const char *) p1, (const char *) p2);
}
/* Stats of all tests run so far */
typedef struct {
int ran, passed, failed;
} stats_t;
/* -- Signal handlers -- */
static pid_t child_pid;
static int sent_child_timeout_kill_signal;
static void kill_child_signal_handler(intsigno)
{
if (!child_pid)
return;
char m[] = "-Timeout(Killing test process)-";
write(0, m, sizeof(m) - 1);
kill(child_pid, SIGKILL);
sent_child_timeout_kill_signal = 1;
}
/* Internal function to run a test as a forked child. The child
process is terminated if it runs for more than a few seconds */
static int invoke_test_with_timelimit(testentry_t * test,
int redirect_stdouterr, int argc,
const char **argv)
{
45. sent_child_timeout_kill_signal = 0;
alarm(timeout_seconds);
wait_val = waitpid(child_pid, &wait_status, 0);
int child_exited_normally = WIFEXITED(wait_status);
int child_exit_value = WEXITSTATUS(wait_status);
int child_term_by_signal = WIFSIGNALED(wait_status);
int child_term_signal = WTERMSIG(wait_status);
if (child_term_by_signal) {
fprintf(stderr, "testrunner:Test terminated by signal
%dn",
child_term_signal);
fprintf(stderr,
"testrunner:waitpid returned %d
(child_pid=%d,wait_status=%d)",
wait_val, child_pid, wait_status);
}
if (child_pid != wait_val)
fprintf(stderr,
"testrunner: strange... wait_val != child_pidn");
int passed = (child_pid == wait_val) && (child_exit_value
== 0)
&& (child_exited_normally != 0);
alarm(0);
kill(child_pid, SIGKILL);
child_pid = 0;
return sent_child_timeout_kill_signal ? test_killed : passed
? 0 :
1;
}
}
46. /*
* run a test and update the stats. The main guts of this
functionality is provided by invoke_test_with_timelimit
* This outer wrapper updates thes output and statistics before
and after running the test.
*/
static int
run_one_test(stats_t * stats, testentry_t * test, int
redirect_stdouterr,
int argc, const char **argv)
{
int test_result;
assert(stats && test->name && argc > 0 && argv && *argv);
stats->ran++;
stats->failed++;
printf("%2d.%-20s:", stats->ran, test->name);
fflush(stdout);
test_result =
invoke_test_with_timelimit(test, redirect_stdouterr, argc,
argv);
if (test_result == 0) {
stats->failed--;
stats->passed++;
}
printf(":%sn", (test_result == 0 ? "pass" : test_result ==
2 ? "TIMEOUT * " : "FAIL *"));
return test_result != 0;
}
47. /* Help functionality to print out sorted list of test names and
suite names */
static void print_targets(testentry_t tests[], int count)
{
const char **array;
const char *previous;
int i;
array = (const char **) calloc(sizeof(const char *), count);
/* Sort the test names and print unique entries */
for (i = 0; i < count; i++)
array[i] = tests[i].name;
qsort(array, count, sizeof(array[0]), mystrcmp);
printf("nValid tests : all");
for (i = 0, previous = ""; i < count; i++)
if (!eql(previous, array[i]))
printf(" %s", (previous = array[i]));
/* Sort the suite names and print unique entries */
for (i = 0; i < count; i++)
array[i] = tests[i].suite;
qsort(array, count, sizeof(array[0]), mystrcmp);
printf("nValid suites:");
for (i = 0, previous = ""; i < count; i++)
if (!eql(previous, array[i]))
printf(" %s", (previous = array[i]));
printf("n");
}
48. /*
* Main entry point for test harness
*/
int
run_testrunner(int argc, const char **argv, testentry_t tests[],
int test_count)
{
const char *test_name, *target;
int i;
stats_t stats;
int target_matched, max_errors_before_quit,
redirect_stdouterr;
memset(&stats, 0, sizeof(stats));
max_errors_before_quit = 1;
redirect_stdouterr = 0;
assert(tests != NULL);
assert(test_count > 0);
assert(argc > 0 && argv && *argv);
while (true) {
target = argc > 1 ? argv[1] : "";
assert(target);
if (*target != '-')
break;
argc--;
argv++;
if (target[1] == 'f' && target[2])
max_errors_before_quit = atoi(target + 1);
else if (target[1] == 'r')
redirect_stdouterr = 1;
}
target_matched = false;