Try Firefox!

Lab 10: Concurrency, Worm World


Big Picture
This lab concerns a simple virtual world called Worm World. You can see a demo of Worm World in action by launching
/home/wcbrown/courses/IC221/labs/L10/demo/go
The general setup here is this: Worm World itself is a library. Your program makes calls to Worm World API functions (WWstart, WWmkworm, WWmove, and WWd2wall) to control one or more worms in Worm World. I'm giving you a program that reads one color and speed from the user via stdin, and controls a worm of that color and at that speed via the Worm World API. Your job is to modify the program so that the user can keep entering colors and speeds and get more and more worms moving, independently, around the screen. The way to do that is to have each call to the work-control function run in a separate thread!

Setup
Download the following tar'd and gzip'd file to the directly you keep your labs in:
/home/wcbrown/courses/IC221/labs/L10/lab10.tar.gz
Unpack it, cd to the lab10 directory it creates, and make and run the program contained there. Give it input (via stdin)
red 30
	
and see what happens.

Part 1: Preparing to make threads
Every thread is initiated by a call (via pthread_create) to a function like that returns void* and takes a single void* argument, like this:
void* foo(void *p);
	
This means that any data that gets sent to the thread must be packaged up and referred to via void* pointer.

Your job is to modify the ic221.c file so that main does not call wormctrl directly, but instead calls a function with prototype

void* wct(void *p);
which, in turn, calls wormctrl. Now, not only do you need to define wct, but you need to figure out how to get the color and delay parameters into wct so it can pass them to wormctrl. No global variables allowed! Here's how you do it:

  1. Define a struct wctparams that has two fields, char color[32] and int delay.
  2. Have main define a pointer struct wctparams *pp and a) use malloc to set pp to point to a new wctparams struct, and b) set the color and delay fields of that object appropriately.
  3. In main, when wct gets called, give it the pointer pp, cast to a void*, as its argument.
  4. In wct cast the parameter p to a struct wctparams* object to retrieve the color and delay values that wormctrl needs.

Make sure the program compiles and runs, exactly like before.

Part 2: Multi-thread this thing
Now all your hard work from Part 1 will have paid off. It should be trivial to call this wct via pthread_create so that it actually runs in its own thread. Be sure to put the whole read-input-call-wct thing in a loop so you can create multiple worms. If you don't, you'll have to at least call pause() at the end of main, because when main exits the whole program, Worm World and all, will exit. If the user hits end-or-file, you should be sure to call pause() ... otherwise the whole Worm World exits. Note that this is different from what would happen in the Part 1 program if you hit end-of-file instead of entering a color and delay value.

Part 3: Mutex and protecting critical sections!
Now, here's where things get interesting! Run your program like this:
bash$ head -n 40 input.txt | ./go  ← Just sends the first 40 lines of file to go
... where input.txt is one of the files you unpacked. (If it just crashes put usleep(30000) immediately after the call to pthread_create and try again. This should at least stop the crashes.) All hell breaks loose, right? What's going on? Are there simply too many threads? Nope! The problem is that Worm World's API functions: (WWstart, WWmkworm, WWmove, and WWd2wall) are not thread safe! If more than one thread calls one of these at the same time, bad things may happen. In other words, any chunk of code that calls one of these functions is a critical section and we need a mutex to protect each of these. So your job is to:
  1. Create a global pthread_mutex_t object.
  2. Initialize your mutex in main before any threads are created and any lock/unlock calls are made!
  3. Wrap each call to a Worm World API function (the functions that start with WW) in pthread_mutex_lock and pthread_mutex_unlock calls to ensure mutual exclusion of these critical sections.

Now if you run this version of the program with the above input, you should see the program run sanely with 40 worms! In fact, it should be OK with up to 200 worms. NOTE: If you put in the usleep(30000), you can remove it now.

Submission instructions
Be sure to put your name in all of the relevant files (including a README file with your name, alpha, and whether this is a Pat 1, 2 or 3 submission) and submit the lab10 directory using the submit script.


Christopher W Brown
Last modified: Thu Mar 26 16:47:35 EDT 2009