TCP Card Dealer - Threads



For this program, you will again be modifying your TCP card dealer server to use a different technique for handling multiple clients. This time, you will split the difference between the single-process model and creating a child for each client connection by using threads. Since you are working in C/C++ on Linux, the available option for threading is Pthreads. You should implement the same server functionality that you had in Program 7, including handling the UDP location requests. You won't have to do anything to the client for this program.

As most of you have seen in your operating systems class, threads are essentially "lightweight processes". That is, you can have different code running in each thread, as if each one was a separate process. However, you don't need all the overhead of starting a separate process for each thread, Another big advantage of threads is that they share the same address space. In your card dealer, That means that every thread can access the data structures that store the cards to be dealt, so each thread can decide what the next card is without somehow communicating with a "master thread" that must do all the work.

As always, with convenience comes trouble. It is likely that the process of dealing a card involves several steps to get all the data structures updated, and depending on how you implemented the code, if two different threads are trying to deal a card at the same time, they might cause the code to not work correctly. Consider the following code snippet:

If two threads are both executing the code that manipulates int_arr and next_val, they could definitely interfere with each other. Consider the second statement, which updates next_val. Even though it is one line of C code, that will compile into several assembly instructions. Suppose one thread fetches the value of next_val, adds one, and mods by 10, but then that thread gets swapped out by the OS and the other one starts running. It does the same thing, but it updates next_val before it gets swapped out. If the first thread then starts, it will overwrite next_val with the value that it was going to write - the net result is that both threads should have each incremented next_val, but it will end up only getting incremented once.

In order to prevent concurrency problems like this, you need to identify sections of your code or date elements that have potential concurrency issues and implement some mechanism to guarantee that the code in each thread is executed properly to produce correct results. A common technique is to use a semaphore to protect a data structure or section of code. The semaphore essentially acts as a lock - when a thread gets to a critical section of code, it tries to obtain the semaphore. If another thread already has it, the requesting thread is blocked and must wait until the semaphore is released. It then gets the semaphore and can execute the critical code, then release the semaphore. This ensures that each thread executes the critical code section as an atomic unit of work.

This version of your card dealer server must use threads to deal with client connections, and it must also support the UDP locator interface you developed for the last program. There are different ways you could partition up the work among threads; the easiest is probably to create a thread to handle the UDP socket and a thread for each client connection, then handle the main TCP server socket in the main program thread. Like the program which used client sockets, the network code should be pretty simple. Since each thread is dealing strictly with one specific task, it doesn't have to worry about non-blocking or asynchronous I/O; it's fine for a client thread to call recv() on a socket and block there until a message comes in, since it doesn't have anything else to do. Unlike the child process version, you won't have to worry about setting up pipes to communicate with child processes. Instead, you will need to figure out how to coordinate access to your card data structures and code so each thread can safely deal a card or reshuffle when it gets a request.

There might be other threading packages you can use instead of Pthreads, but I strongly suggest going with the Pthreads library. Dig around on the Web for Pthreads documentation - you'll need to find out how to create a thread and specify what code the thread should execute, and determine how to create a semaphore or some similar mechanism to lock sections of code. You should also understand what happens when a thread terminates; your threads that are handling client connections should clean up and exit when the client closes the connection.

I do have the O'Reilly Pthreads book in my office, if someone wants to borrow it. But the online documentation you can find on the Web is probably as good a resource as you'll find.

The requirements for this program are thus the following:

The client code should not need to be changed for this program, and you don't need to add threading code to your library. At this point, your socket library should have enough code that you can just pick functions out of your library to do the networking tasks and can just concentrate on the threading code.

Design

Your design should indicate how you are going to handle dealing cards within the client threads and how you will synchronize access between threads.

Assignment Submission

NO hard copy, please. Everything should be submitted via email by the due date - mail everything to Anthony. Follow Anthony's specifications for assignment submissions. You need to include the design, your source for the main application, your source for your library, and the output from your server for two different test runs. (You don't need to include any source for the client, since it should be the same as last time.) For the first test run, start the server with one deck and choose one of the ports designated for the lab (40000-40007). If your server can't open a socket on that port, try another one; you should succeed before you exhaust all eight possibilities. Start a client in another window and request twenty cards, shuffle, then request five more cards. For the second run, start the server with four decks, choosing the port as in the previous run, then start two different clients and request twenty cards from each. (These are the same test runs as you did for the last program.)

You should include the following:

The first item should just be in the body of the email - attach the design, source, and output as separate files.