CSC 357 Lab 8
Threads and Thread Synchronization



ISSUED:
Friday, 1 June 2007
DUE: On or before 11:59:59PM Wednesday 13 June, via handin on falcon/hornet
POINTS POSSIBLE: 100
WEIGHT: 2% of total class grade
READING: Stevens Chapter 11, Lecture Notes Week 10, appropriate man pages

Introduction

This lab is similar to Lab Number 6. Here you are doing quasi-concurrent execution using threads, instead of the "heavy-weight" processes created by fork and exec.

Unsynchronized Threaded Program

The following program is the basis for the work you will do in this lab. The program comments explain what it does. Source code for the program is available online in the lab 8 directory, in the file p-test.c. The executable is in p-test.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

/****
 *
 * This program is a simple illustration of threads.  It is a version of the
 * f-test program from lab 6, here using a thread instead of the "heavy-weight"
 * process created by calling fork.
 *
 * The program takes an integer command-line argument, N.  The program creates
 * a thread to print consecutive odd numbers from 1 to N.  The main thread
 * prints consecutive even integers from 2 to N.
 *
 * The main thread and created thread run asynchronously, in the same way that
 * the main process and forked process ran asynchronously in the lab 6
 * program.  This means that there is the same form of race condition between
 * the threads, such that standard output of odd and even numbers is
 * unpredictably interspersed.
 *
 */

/**
 * Print consecutive odd integers from 1 to arg.  The signature of this
 * function is that required for a thread start routine.
 */
void* print_odds(void* arg) {
   int i;
   int n = *((int*) arg);

   for (i = 1; i <= n; i += 2)
   {
      printf("%d0, i);
   }

   return NULL;
}

/*
 * Print consecutive odd and even integers, up to n.  A created thread prints
 * the odds, the main thread prints the evens.
 */
void print_numbers(int n)
{
    int i;              /* loop index */
    pthread_t tid;      /* id for thread to be created */
    int *arg;           /* argument sent to thread start routine */

    /*
     * Allocate storage for the start routine argument, which must be void*.
     */
    if ((arg = malloc(sizeof(int))) == NULL) {
       perror("malloc");
       exit(-1);
    }

    /*
     * Initialize the argument.
     */
    *arg = n;

    /*
     * Create the thread, which means its start routine begins quasi-concurrent
     * execution.
     */
    if (pthread_create(&tid, NULL, print_odds, arg)) {
       perror("pthread_create");
       exit(-1);
    }

    /*
     * Back in the main thread, print out even integers, concurrently with the
     * odd printing in the thread.
     */
    for (i = 2; i <= n; i += 2) {
       printf("%d0, i);
    }

    /*
     * Wait for the thread to terminate.
     */
    if (pthread_join(tid, NULL)) {
       perror("pthread_join");
       exit(-1);
    }
}


/*
 * Get the single command-line argument and call print_numbers with it.
 */
int main(int argc, char *argv[]) {

    int n;              /* integer value of command-line arg */
    char* end;          /* for use with strtol */

    /*
     * Make sure there is exactly one command-line arg.
     */
    if (argc != 2) {
       fprintf(stderr, "usage: %s <number>0, argv[0]);
       exit(1);
    }

    /*
     * Convert and validate arg as an integer.
     */
    n = strtol(argv[1], &end, 10);
    if (*end != ' ') {
       fprintf(stderr, "%s - %s is not a valid integer0, argv[0], argv[1]);
       exit(1);
    }

    /*
     * Do the deed.
     */
    print_numbers(n);

    return EXIT_SUCCESS;
}

Task:

Starting with the p-test program above, write a program named m-test that uses thread synchronization to guarantee that the threads will take turns printing each integer. The program begins by having the main thread print 0. Then the created thread prints 1. Printing continues with each thread printing only a single number each turn.

There are a few ways to do this. Your solution must work without any sort of busy wait, i.e., no looping until a condition becomes true. The most straight forward implementation uses mutex-style synchronization, with the following threading functions:

Function Description
pthread_mutex_init create a mutex
pthread_mutex_lock lock a mutex
pthread_cond_wait wait on a locked mutex
pthread_mutex_lock unlock a mutex

Deliverables

The program m-test.c, any additional .c files you have, and a Makefile to compile everything. The executable name for the compiled program must be "m-test".

Scoring Details

100% all-or-nothing for the correct synchronized behavior.

Collaboration Allowed

Collaboration with a lab partner IS allowed, but each partner must turn a copy of the work.

How to Submit the Deliverables

Submit C source file(s) and Makefile via handin.


index | lectures | labs | programs | handouts | solutions | examples | documentation | bin