CS 351- Fall 2005 - Rutgers University

Download Report

Transcript CS 351- Fall 2005 - Rutgers University





Project 2
- Work on CEREAL
- Due: Oct 10, 2008 5:00 pm
Using Pthread Library.
Paper & pencil assignment
Sample Questions
1




Gain experience with multithread
programming
Use common synchronization primitives:
locks & condition variables.
Gain some tangential exposure to network
programming
Understanding the concept of overlapping I/O
& computation
2
Single-thread
Multi-threads
3

3 parts
Resource:MTServer.tar.gz
Part 1: Build a thread pool in C

README:

Makefile:
 Edit ONLY “threadpool.c”
 Test by using “threadpool_test.c”
 Learn from “example_thread.c”


Part 2: Use your thread pool to
turn a single-threaded server
into a multi-threaded server
 Edit ONLY server.c

Part 3: Measure the
performance of your multithreaded server
 Clear debug code from part 2.
 Make NUM_LOOPS -> command-line
argument.






to compile
example_thread, server, and client,
just type "make" or "make all".
common.[c|h]: some code that is
useful to both the server and client
server.c:
the source code for the
single-threaded server
client.c:
the source code for a
single-threaded test client
example_thread.c: an example
multithreaded program that uses
pthreads
threadpool.[c|h]: the code you will
modify to implement a thread pool
threadpool_test.c: some sample code
that invokes a threadpool
SocketLibarary:
4

-
Implement 3 functions:
threadpool create_threadpool(int num_threads_in_pool);
void dispatch(threadpool from_me,
dispatch_fn dispatch_to_here, void *arg);
Dispatch() will cause exactly one thread in the pool to wake up
and invoke the supplied function with the supplied argument.
Once the function call returns, the dispatched thread will re-enter
the thread pool.
-


void destroy_threadpool(threadpool destroyme);
Test by using “threadpool_test.c”
Do synchronization to satisfy 2 requirements:
(1) NO BUSYWAITING
(2) NO DEADLOCKS & NO RACE CONDITIONS
5

What need to change?
Create a thread pool
Create a listening socket
Loop:
1.
2.
3.

(x)
-
Accept new connection from a client. (x)
Dispatch the connection to a thread from the thread pool.
Read data from the dispatched connection (x)
Process the request (x)
Write a response back to the client. (x)
Close the connection to the client. (x)
◦
◦
◦
◦
int socket_listen; int socket_talk;
socket_listen = setup_listen(argv[1]);
socket_talk = saccept(socket_listen); // step 1
request = read_request(socket_talk); // step 2
Socket
 close(socket_talk); // step 5
6

How?
◦ In the main thread, increment a counter for every
dispatch
◦ Whenever the counter increases by 50(or 100), take a
timestamp (using gettimeofday())
◦ Run 36 situation:
# threads in pool = 1 and NUM_LOOPS=1, 100, 1000, 10000, 100000, 500000
# threads in pool = 2 and NUM_LOOPS=1, 100, 1000, 10000, 100000, 500000
..
# threads in pool = 32 and NUM_LOOPS=1, 100, 1000, 10000, 100000, 500000

Run in “quiet” environment
7
8

A gzipped tar file includes:
1. Write Up
2. The server/ directory will ALL your codes, including
unmodified files, such as Makefile, client.c…

WriteUp:
◦ A verbal description of the structure of your threadpool
implementation, including a list of any design decisions you made.
◦ A list of critical sections inside your threadpool code (and why they
are critical sections).
◦ A description of the synchronization inside your threadpool. What
condition variables did you need, where, and why? Under what
conditions do threads block, and which thread is responsible for
waking up a blocked thread?
◦ The graph you produced in part 3, as well as an explanation of why
your graph is shaped the way it is. Try to explain all of the
interesting features on your graph, if possible.
9


Links: https://computing.llnl.gov/tutorials/pthreads/
Creating and Terminating Threads
◦ int pthread_create (pthread_t *thread,
const pthread_attr_t *attr, // NULL
void *(*start_routine), void * arg);
◦ void pthread_exit (status) // NULL

Passing Multiple Arguments to Threads via a structure.
void *PrintHello(void *threadarg) {
struct thread_data *my_data; ...
my_data = (struct thread_data *) threadarg;
taskid = my_data->thread_id; sum = my_data->sum; hello_msg = my_data->message;
...
}
//in main ()
rc = pthread_create(&threads[t], NULL, PrintHello, (void *)
&thread_data_array[t]);
10


Creating and Destroying
◦ pthread_mutex_init (mutex,attr)
pthread_mutex_destroy (mutex)
◦ pthread_mutexattr_init (attr)
◦ pthread_mutexattr_destroy (attr)
Locking & Unlocking mutexes
◦ pthread_mutex_lock (mutex)
◦ pthread_mutex_trylock (mutex)
◦ pthread_mutex_unlock (mutex)
11


Creating and Destroying
◦ pthread_cond_init (condition,attr)
pthread_cond_destroy (condition)
◦ pthread_condattr_init (attr)
◦ pthread_condattr_destroy (attr)
Usage
◦ pthread_cond_wait (condition,mutex) //
◦ pthread_cond_signal (condition)
◦ pthread_cond_broadcast (condition)
12
THREAD A
 Do work up to the point where a
certain condition must occur (such
as "count" must reach a specified
value)
 Lock associated mutex and check
value of a global variable
 Call pthread_cond_wait() to
perform a blocking wait for signal
from Thread-B. Note that a call to
pthread_cond_wait() automatically
and atomically unlocks the
associated mutex variable so that
it can be used by Thread-B.
 When signalled, wake up. Mutex is
automatically and atomically
locked.
 Explicitly unlock mutex
 Continue
THREAD B
 Do work
 Lock associated mutex
 Change the value of the global
variable that Thread-A is waiting
upon.
 Check value of the global ThreadA wait variable. If it fulfills the
desired condition, signal ThreadA.
 Unlock mutex.
 Continue
13



Lock
Condition Variables
Semaphore
◦ Synchronized counting variables
◦ Formally, a semaphore is comprised of:
 An integer value
 Two operations: P() and V()
◦ P()
 While value = 0, sleep
 Decrement value and return
◦ V()
 Increments value
 If there are any threads sleeping waiting for value to become non-zero,
 wakeup at least 1 thread
14

Registers:
◦ %ebp -> base register points to the local variables (another
name : frame pointer).
◦ esp -> stack pointer
◦ GCC requires that some register not change across a
function call: %ebp, & segments register %ds, %es & %ss ..
-> pushl ebp; popl ebp

When GCC calls a function:
◦ Push all the arguments onto the stack, starting with the last
one.
◦ Then, issues a call. Ex: “call foo”
- push the return address -> stack
- %esp -> return address
◦ After a call, discard the called activation record.
addl n, %esp
15
← 8(%esp)
← 4(%esp)
16

Inside the function:
◦ Make %esp & %ebp point to right places
pushl %ebp
◦ Local variable:
 -4(%ebp)

Return Value
◦ Integers (of any size up to 32 bits) & pointers are
returned in the %eax register.
◦ Compute the return value & store in %eax
◦ Invoke “ret”
17
18
procedure producer() {
while (true) {
item = produceItem()
if (itemCount == BUFFER_SIZE)
{sleep() }
putItemIntoBuffer(item)
itemCount = itemCount + 1
if (itemCount == 1) {
wakeup(consumer) }
}
}
procedure consumer() {
while (true) {
if (itemCount == 0) { sleep() }
item = removeItemFromBuffer()
itemCount = itemCount – 1
if (itemCount == BUFFER_SIZE 1) { wakeup(producer) }
consumeItem(item)
}}
semaphore fillCount = 0
semaphore emptyCount =
BUFFER_SIZE
procedure producer() {
while (true) {
item = produceItem()
down(emptyCount)
putItemIntoBuffer(item)
up(fillCount)
}}
procedure consumer() {
while (true) {
down(fillCount)
item = removeItemFromBuffer()
up(emptyCount)
consumeItem(item)
}}
19