Concurrent programming - PowerPoint PPT Presentation

1 / 72
About This Presentation
Title:

Concurrent programming

Description:

Concurrent servers use multiple concurrent flows to serve ... Not necessary for systems with POSIX signal handling. ... Posix Threads (Pthreads) Interface ... – PowerPoint PPT presentation

Number of Views:85
Avg rating:3.0/5.0
Slides: 73
Provided by: randa84
Category:

less

Transcript and Presenter's Notes

Title: Concurrent programming


1
Concurrent programming
  • Concurrent Servers
  • Limitations of iterative servers
  • Process-based concurrent servers
  • Threads-based concurrent servers
  • Programming with threads
  • Shared variables
  • The need for synchronization
  • Synchronizing with semaphores
  • Thread safety and reentrancy
  • Races and deadlocks

2
Iterative Servers
  • Iterative servers process one request at a time.

client 1
server
client 2
call connect
call connect
call accept
ret connect
ret accept
call read
write
ret read
close
close
call accept
ret connect
ret accept
call read
write
ret read
close
close
3
Fundamental Flaw of Iterative Servers
client 1
server
client 2
call accept
call connect
ret connect
ret accept
call fgets
call read
Server blocks waiting for data from Client 1
call connect
User goes out to lunch Client 1 blocks waiting
for user to type in data
Client 2 blocks waiting to complete its
connection request until after lunch!
  • Solution use concurrent servers instead.
  • Concurrent servers use multiple concurrent flows
    to serve multiple clients at the same time.

4
Concurrent Servers
  • Concurrent servers handle multiple requests
    concurrently.

client 1
server
client 2
call accept
call connect
call connect
ret connect
ret accept
call fgets
fork
child 1
call accept
call read
User goes out to lunch Client 1 blocks waiting
for user to type in data
ret connect
call fgets
ret accept
write
fork
child 2
call read
call read
...
write
end read
close
close
5
Three Basic Mechanisms for Creating Concurrent
Flows
  • 1. Processes
  • Kernel automatically interleaves multiple logical
    flows.
  • Each flow has its own private address space.
  • 2. I/O multiplexing with select()
  • User manually interleaves multiple logical flows.
  • Each flow shares the same address space.
  • Popular for high-performance server designs.
  • 3. Threads
  • Kernel automatically interleaves multiple logical
    flows.
  • Each flow shares the same address space.
  • Hybrid of processes and I/O multiplexing!

6
Process-Based Concurrent Server
/ echoserverp.c - A concurrent echo server
based on processes Usage echoserverp ltportgt
/ include ltics.hgt define BUFSIZE 1024 void
echo(int connfd) void handler(int sig) int
main(int argc, char argv) int listenfd,
connfd int portno struct sockaddr_in
clientaddr int clientlen sizeof(struct
sockaddr_in) if (argc ! 2)
fprintf(stderr, "usage s ltportgt\n", argv0)
exit(0) portno atoi(argv1)
listenfd open_listenfd(portno)
7
Process-Based Concurrent Server (cont)
Signal(SIGCHLD, handler) / parent must reap
children! / / main server loop / while
(1) connfd Accept(listenfd, (struct
sockaddr ) clientaddr,
clientlen)) if (Fork() 0)
Close(listenfd) / child closes its listening
socket / echo(connfd) / child reads
and echoes input line / Close(connfd)
/ child is done with this client /
exit(0) / child exits /
Close(connfd) / parent must close connected
socket! /
8
Process-Based Concurrent Server(cont)
/ handler - reaps children as they terminate
/ void handler(int sig) pid_t pid int
stat while ((pid waitpid(-1, stat,
WNOHANG)) gt 0) return
9
Implementation Issues With Process-Based Designs
  • Server should restart accept call if it is
    interrupted by a transfer of control to the
    SIGCHLD handler
  • Not necessary for systems with POSIX signal
    handling.
  • Our Signal wrapper tells kernel to automatically
    restart accept
  • Required for portability on some older Unix
    systems.
  • Server must reap zombie children
  • to avoid fatal memory leak.
  • Server must close its copy of connfd.
  • Kernel keeps reference for each socket.
  • After fork, refcnt(connfd) 2.
  • Connection will not be closed until
    refcnt(connfd)0.

10
Pros and Cons of Process-Based Designs
  • Handles multiple connections concurrently
  • Clean sharing model
  • descriptors (no)
  • file tables (yes)
  • global variables (no)
  • Simple and straightforward.
  • - Additional overhead for process control.
  • - Nontrivial to share data between processes.
  • Requires IPC (interprocess communication)
    mechanisms
  • FIFOs (named pipes), System V shared memory and
    semaphores

11
Traditional View of a Process
  • Process process context code, data, and stack

Code, data, and stack
Process context
stack
Program context Data registers Condition
codes Stack pointer (SP) Program counter
(PC) Kernel context VM structures
Descriptor table brk pointer
SP
shared libraries
brk
run-time heap
read/write data
PC
read-only code/data
0
12
Alternate View of a Process
  • Process thread code, data, and kernel context

Code and Data
Thread (main thread)
shared libraries
stack
brk
SP
run-time heap
read/write data
Thread context Data registers Condition
codes Stack pointer (SP) Program counter
(PC)
PC
read-only code/data
0
Kernel context VM structures Descriptor
table brk pointer
13
A Process With Multiple Threads
  • Multiple threads can be associated with a process
  • Each thread has its own logical control flow
    (sequence of PC values)
  • Each thread shares the same code, data, and
    kernel context
  • Each thread has its own thread id (TID)

Shared code and data
Thread 1 (main thread)
Thread 2 (peer thread)
shared libraries
stack 1
stack 2
run-time heap
read/write data
Thread 1 context Data registers
Condition codes SP1 PC1
Thread 2 context Data registers
Condition codes SP2 PC2
read-only code/data
0
Kernel context VM structures Descriptor
table brk pointer
14
Logical View of Threads
  • Threads associated with a process form a pool of
    peers.
  • Unlike processes which form a tree hierarchy

Threads associated with process foo
Process hierarchy
P0
T2
T4
T1
P1
shared code, data and kernel context
sh
sh
sh
T3
T5
foo
bar
15
Concurrent Thread Execution
  • Two threads run concurrently (are concurrent) if
    their logical flows overlap in time.
  • Otherwise, they are sequential.
  • Examples
  • Concurrent A B, AC
  • Sequential B C

Thread A
Thread B
Thread C
Time
16
Threads vs. Processes
  • How threads and processes are similar
  • Each has its own logical control flow.
  • Each can run concurrently.
  • Each is context switched.
  • How threads and processes are different
  • Threads share code and data, processes
    (typically) do not.
  • Threads are somewhat less expensive than
    processes.
  • Process control (creating and reaping) is twice
    as expensive as thread control.
  • Linux/Pentium III numbers
  • 20K cycles to create and reap a process.
  • 10K cycles to create and reap a thread.

17
Posix Threads (Pthreads) Interface
  • Pthreads Standard interface for 60 functions
    that manipulate threads from C programs.
  • Creating and reaping threads.
  • pthread_create
  • pthread_join
  • Determining your thread ID
  • pthread_self
  • Terminating threads
  • pthread_cancel
  • pthread_exit
  • exit terminates all threads , ret terminates
    current thread
  • Synchronizing access to shared variables
  • pthread_mutex_init
  • pthread_mutex_unlock
  • pthread_cond_init
  • pthread_cond_timedwait

18
The Pthreads "hello, world" Program
/ hello.c - Pthreads "hello, world" program
/ include "csapp.h" void thread(void
vargp) int main() pthread_t tid
Pthread_create(tid, NULL, thread, NULL)
Pthread_join(tid, NULL) exit(0) / thread
routine / void thread(void vargp)
printf("Hello, world!\n") return NULL
Thread attributes (usually NULL)
Thread arguments (void p)
return value (void p)
19
Execution of Threadedhello, world
main thread
call Pthread_create()
Pthread_create() returns
peer thread
call Pthread_join()
printf()
main thread waits for peer thread to terminate
return NULL
(peer thread terminates)
Pthread_join() returns
exit() terminates main thread and any peer
threads
20
Thread-Based Concurrent Echo Server
int main(int argc, char argv) int
listenfd, connfdp, port, clientlen struct
sockaddr_in clientaddr pthread_t tid
if (argc ! 2) fprintf(stderr, "usage
s ltportgt\n", argv0) exit(0)
port atoi(argv1) listenfd
open_listenfd(port) while (1)
clientlen sizeof(clientaddr) connfdp
Malloc(sizeof(int)) connfdp
Accept(listenfd, (SA ) clientaddr,
clientlen) Pthread_create(tid, NULL,
thread, connfdp)
21
Thread-Based Concurrent Server (cont)
thread routine / void thread(void vargp)
int connfd ((int )vargp)
Pthread_detach(pthread_self())
Free(vargp) echo_r(connfd) / reentrant
version of echo() / Close(connfd)
return NULL
22
Issues With Thread-Based Servers
  • Must run detached to avoid memory leak.
  • At any point in time, a thread is either joinable
    or detached.
  • Joinable thread can be reaped and killed by other
    threads.
  • must be reaped (with pthread_join) to free memory
    resources.
  • Detached thread cannot be reaped or killed by
    other threads.
  • resources are automatically reaped on
    termination.
  • Default state is joinable.
  • use pthread_detach(pthread_self()) to make
    detached.
  • Must be careful to avoid unintended sharing.
  • For example, what happens if we pass the address
    of connfd to the thread routine?
  • Pthread_create(tid, NULL, thread, (void
    )connfd)
  • All functions called by a thread must be
    thread-safe

23
Pros and Cons of Thread-Based Designs
  • Easy to share data structures between threads
  • e.g., logging information, file cache.
  • Threads are more efficient than processes.
  • --- Unintentional sharing can introduce subtle
    and hard-to-reproduce errors!
  • The ease with which data can be shared is both
    the greatest strength and the greatest weakness
    of threads.

24
Shared Variables in Threaded C Programs
  • Question Which variables in a threaded C
    program are shared variables?
  • The answer is not as simple as global variables
    are shared and stack variables are private.
  • Requires answers to the following questions
  • What is the memory model for threads?
  • How are variables mapped to memory instances?
  • How many threads reference each of these
    instances?

25
Threads Memory Model
  • Conceptual model
  • Each thread runs in the context of a process.
  • Each thread has its own separate thread context.
  • Thread ID, stack, stack pointer, program counter,
    condition codes, and general purpose registers.
  • All threads share the remaining process context.
  • Code, data, heap, and shared library segments of
    the process virtual address space.
  • Open files and installed handlers
  • Operationally, this model is not strictly
    enforced
  • While register values are truly separate and
    protected....
  • Any thread can read and write the stack of any
    other thread.
  • Mismatch between the conceptual and operation
    model is a source of confusion and errors.

26
Example of Threads Accessing Another Threads
Stack
/ thread routine / void thread(void vargp)
int myid (int)vargp static int svar
0 printf("d s (svard)\n",
myid, ptrmyid, svar)
char ptr / global / int main() int
i pthread_t tid char msgsN
"Hello from foo", "Hello from bar"
ptr msgs for (i 0 i lt 2 i)
Pthread_create(tid, NULL,
thread, (void )i)
Pthread_exit(NULL)
Peer threads access main threads
stack indirectly through global ptr variable
27
Mapping Variables to Mem. Instances
Global var 1 instance (ptr data)
Local automatic vars 1 instance (i.m, msgs.m )
Local automatic var 2 instances (
myid.p0peer thread 0s stack, myid.p1peer
thread 1s stack )
char ptr / global / int main() int
i pthread_t tid char msgsN
"Hello from foo", "Hello from bar"
ptr msgs for (i 0 i lt 2 i)
Pthread_create(tid, NULL,
thread, (void )i)
Pthread_exit(NULL)
/ thread routine / void thread(void vargp)
int myid (int)vargp static int svar
0 printf("d s (svard)\n",
myid, ptrmyid, svar)
Local static var 1 instance (svar data)
28
Shared Variable Analysis
  • Which variables are shared?

Variable Referenced by Referenced by Referenced
by instance main thread? peer thread 0? peer
thread 1? ptr yes yes yes svar no yes yes
i.m yes no no msgs.m yes yes yes myid.p0 n
o yes no myid.p1 no no yes
  • Answer A variable x is shared iff multiple
    threads reference at least one instance of x.
    Thus
  • ptr, svar, and msgs are shared.
  • i and myid are NOT shared.

29
badcnt.c An Improperly Synchronized Threaded
Program
/ thread routine / void count(void arg)
int i for (i0 iltNITERS i)
cnt return NULL
unsigned int cnt 0 / shared / int main()
pthread_t tid1, tid2 Pthread_create(tid1,
NULL, count, NULL)
Pthread_create(tid2, NULL,
count, NULL) Pthread_join(tid1, NULL)
Pthread_join(tid2, NULL) if (cnt !
(unsigned)NITERS2) printf("BOOM!
cntd\n", cnt) else
printf("OK cntd\n", cnt)
linuxgt ./badcnt BOOM! cnt198841183 linuxgt
./badcnt BOOM! cnt198261801 linuxgt
./badcnt BOOM! cnt198269672
cnt should be equal to 200,000,000. What went
wrong?!
30
Assembly Code for Counter Loop
C code for counter loop
Corresponding asm code (gcc -O0 -fforce-mem)
for (i0 iltNITERS i) cnt
.L9 movl -4(ebp),eax cmpl 99999999,eax jle
.L12 jmp .L10 .L12 movl cnt,eax
Load leal 1(eax),edx Update movl edx,cnt
Store .L11 movl -4(ebp),eax leal
1(eax),edx movl edx,-4(ebp) jmp .L9 .L10
Head (Hi)
Load cnt (Li) Update cnt (Ui) Store cnt (Si)
Tail (Ti)
31
Concurrent Execution
  • Key idea In general, any sequentially consistent
    interleaving is possible, but some are incorrect!
  • Ii denotes that thread i executes instruction I
  • eaxi is the contents of eax in thread is
    context

i (thread)
instri
cnt
eax1
eax2
H1
1
-
0
-
L1
1
0
0
-
U1
1
1
0
-
S1
1
1
1
-
H2
2
-
1
-
L2
2
-
1
1
U2
2
-
1
2
S2
2
-
2
2
T2
2
-
2
2
T1
1
1
2
-
OK
32
Concurrent Execution (cont)
  • Incorrect ordering two threads increment the
    counter, but the result is 1 instead of 2.

i (thread)
instri
cnt
eax1
eax2
H1
1
-
0
-
L1
1
0
0
-
U1
1
1
0
-
H2
2
-
0
-
L2
2
-
0
0
S1
1
1
1
-
T1
1
1
1
-
U2
2
-
1
1
S2
2
-
1
1
T2
2
-
1
1
Oops!
33
Concurrent Execution (cont)
  • How about this ordering?

i (thread)
instri
cnt
eax1
eax2
H1
1
L1
1
H2
2
L2
2
U2
2
S2
2
U1
1
S1
1
T1
1
T2
2
We can clarify our understanding of
concurrent execution with the help of the
progress graph
34
Progress Graphs
A progress graph depicts the discrete execution
state space of concurrent threads. Each axis
corresponds to the sequential order
of instructions in a thread. Each point
corresponds to a possible execution state (Inst1,
Inst2). E.g., (L1, S2) denotes state where
thread 1 has completed L1 and thread 2 has
completed S2.
Thread 2
T2
(L1, S2)
S2
U2
L2
H2
Thread 1
H1
L1
U1
S1
T1
35
Trajectories in Progress Graphs
Thread 2
A trajectory is a sequence of legal state
transitions that describes one possible
concurrent execution of the threads. Example H
1, L1, U1, H2, L2, S1, T1, U2, S2, T2
T2
S2
U2
L2
H2
Thread 1
H1
L1
U1
S1
T1
36
Critical Sections and Unsafe Regions
Thread 2
L, U, and S form a critical section with respect
to the shared variable cnt. Instructions in
critical sections (wrt to some shared variable)
should not be interleaved. Sets of states where
such interleaving occurs form unsafe regions.
T2
S2
critical section wrt cnt
Unsafe region
U2
L2
H2
Thread 1
H1
L1
U1
S1
T1
critical section wrt cnt
37
Safe and Unsafe Trajectories
Thread 2
Def A trajectory is safe iff it doesnt touch
any part of an unsafe region. Claim A
trajectory is correct (wrt cnt) iff it is safe.
Safe trajectory
T2
S2
Unsafe trajectory
Unsafe region
critical section wrt cnt
U2
L2
H2
Thread 1
H1
L1
U1
S1
T1
critical section wrt cnt
38
Semaphores
  • Question How can we guarantee a safe trajectory?
  • We must synchronize the threads so that they
    never enter an unsafe state.
  • Classic solution Dijkstra's P and V operations
    on semaphores.
  • semaphore non-negative integer synchronization
    variable.
  • P(s) while (s 0) wait() s--
  • Dutch for "Proberen" (test)
  • V(s) s
  • Dutch for "Verhogen" (increment)
  • OS guarantees that operations between brackets
    are executed indivisibly.
  • Only one P or V operation at a time can modify s.
  • When while loop in P terminates, only that P can
    decrement s.
  • Semaphore invariant (s gt 0)

39
Safe Sharing with Semaphores
  • Here is how we would use P and V operations to
    synchronize the threads that update cnt.

/ Semaphore s is initially 1 / / Thread
routine / void count(void arg) int i
for (i0 iltNITERS i) P(s)
cnt V(s) return NULL
40
Safe Sharing With Semaphores
Thread 2
Provide mutually exclusive access to shared
variable by surrounding critical section with P
and V operations on semaphore s (initially set to
1). Semaphore invariant creates a forbidden
region that encloses unsafe region and is never
touched by any trajectory.
T2
V(s)
Forbidden region
0
0
0
0
-1
-1
-1
-1
S2
0
0
0
0
-1
-1
-1
-1
U2
Unsafe region
0
0
0
0
-1
-1
-1
-1
L2
-1
-1
-1
-1
0
0
0
0
P(s)
H2
H1
P(s)
V(s)
T1
L1
U1
S1
Thread 1
Initially s 1
41
POSIX Semaphores
/ Initialize semaphore sem to value / /
pshared0 if thread, pshared1 if process / void
Sem_init(sem_t sem, int pshared, unsigned int
value) if (sem_init(sem, pshared, value) lt
0) unix_error("Sem_init") / P operation
on semaphore sem / void P(sem_t sem) if
(sem_wait(sem)) unix_error("P") / V
operation on semaphore sem / void V(sem_t sem)
if (sem_post(sem)) unix_error("V")
42
Sharing With POSIX Semaphores
/ goodcnt.c - properly syncd counter program
/ include "csapp.h" define NITERS
10000000 unsigned int cnt / counter / sem_t
sem / semaphore / int main()
pthread_t tid1, tid2 Sem_init(sem, 0, 1)
/ sem1 / / create 2 threads and wait /
... if (cnt ! (unsigned)NITERS2)
printf("BOOM! cntd\n", cnt) else
printf("OK cntd\n", cnt) exit(0)
/ thread routine / void count(void arg)
int i for (i0 iltNITERS i)
P(sem) cnt V(sem)
return NULL
43
Signaling With Semaphores
producer thread
consumer thread
shared buffer
  • Common synchronization pattern
  • Producer waits for slot, inserts item in buffer,
    and signals consumer.
  • Consumer waits for item, removes it from buffer,
    and signals producer.
  • signals in this context has nothing to do with
    Unix signals
  • Examples
  • Multimedia processing
  • Producer creates MPEG video frames, consumer
    renders the frames
  • Event-driven graphical user interfaces
  • Producer detects mouse clicks, mouse movements,
    and keyboard hits and inserts corresponding
    events in buffer.
  • Consumer retrieves events from buffer and paints
    the display.

44
Producer-Consumer on a Buffer That Holds One Item
int main() pthread_t tid_producer
pthread_t tid_consumer / initialize the
semaphores / Sem_init(shared.empty, 0, 1)
Sem_init(shared.full, 0, 0) / create
threads and wait / Pthread_create(tid_producer
, NULL, producer, NULL)
Pthread_create(tid_consumer, NULL,
consumer, NULL) Pthread_join(tid_producer,
NULL) Pthread_join(tid_consumer, NULL)
exit(0)
/ buf1.c - producer-consumer on 1-element buffer
/ include csapp.h define NITERS 5 void
producer(void arg) void consumer(void
arg) struct int buf / shared var /
sem_t full / sems / sem_t empty shared
45
Producer-Consumer (cont)
Initially empty 1, full 0.
/ producer thread / void producer(void arg)
int i, item for (i0 iltNITERS i)
/ produce item / item i
printf("produced d\n", item)
/ write item to buf / P(shared.empty)
shared.buf item V(shared.full)
return NULL
/ consumer thread / void consumer(void arg)
int i, item for (i0 iltNITERS i)
/ read item from buf / P(shared.full)
item shared.buf V(shared.empty) /
consume item / printf("consumed d\n",
item) return NULL
46
Thread Safety
  • Functions called from a thread must be
    thread-safe.
  • We identify four (non-disjoint) classes of
    thread-unsafe functions
  • Class 1 Failing to protect shared variables.
  • Class 2 Relying on persistent state across
    invocations.
  • Class 3 Returning a pointer to a static
    variable.
  • Class 4 Calling thread-unsafe functions.

47
Thread-Unsafe Functions
  • Class 1 Failing to protect shared variables.
  • Fix Use P and V semaphore operations.
  • Issue Synchronization operations will slow down
    code.
  • Example goodcnt.c

48
Thread-Unsafe Functions (cont)
  • Class 2 Relying on persistent state across
    multiple function invocations.
  • Random number generator relies on static state
  • Fix Rewrite function so that caller passes in
    all necessary state.

/ rand - return pseudo-random integer on
0..32767 / int rand(void) static
unsigned int next 1 next
next1103515245 12345 return (unsigned
int)(next/65536) 32768 / srand - set
seed for rand() / void srand(unsigned int seed)
next seed
49
Thread-Unsafe Functions (cont)
  • Class 3 Returning a ptr to a static variable.
  • Fixes
  • 1. Rewrite code so caller passes pointer to
    struct.
  • Issue Requires changes in caller and callee.
  • 2. Lock-and-copy
  • Issue Requires only simple changes in caller
    (and none in callee)
  • However, caller must free memory.

struct hostent gethostbyname(char name)
static struct hostent h ltcontact DNS and fill
in hgt return h
hostp Malloc(...)) gethostbyname_r(name,
hostp)
struct hostent gethostbyname_ts(char p)
struct hostent q Malloc(...) P(mutex) /
lock / p gethostbyname(name) q p
/ copy / V(mutex) return q
50
Thread-Unsafe Functions
  • Class 4 Calling thread-unsafe functions.
  • Calling one thread-unsafe function makes an
    entire function thread-unsafe.
  • Fix Modify the function so it calls only
    thread-safe functions

51
Reentrant Functions
  • A function is reentrant iff it accesses NO shared
    variables when called from multiple threads.
  • Reentrant functions are a proper subset of the
    set of thread-safe functions.
  • NOTE The fixes to Class 2 and 3 thread-unsafe
    functions require modifying the function to make
    it reentrant.

All functions
Thread-safe functions
Thread-unsafe functions
Reentrant functions
52
Thread-Safe Library Functions
  • All functions in the Standard C Library (at the
    back of your KR text) are thread-safe.
  • Examples malloc, free, printf, scanf
  • Most Unix system calls are thread-safe, with a
    few exceptions

Thread-unsafe function Class Reentrant
version asctime 3 asctime_r ctime
3 ctime_r gethostbyaddr 3 gethostbyaddr_r gethos
tbyname 3 gethostbyname_r inet_ntoa
3 (none) localtime 3 localtime_r rand
2 rand_r
53
Races
  • A race occurs when the correctness of the program
    depends on one thread reaching point x before
    another thread reaches point y.

/ a threaded program with a race / int main()
pthread_t tidN int i for (i
0 i lt N i) Pthread_create(tidi,
NULL, thread, i) for (i 0 i lt N i)
Pthread_join(tidi, NULL)
exit(0) / thread routine / void
thread(void vargp) int myid ((int
)vargp) printf("Hello from thread d\n",
myid) return NULL
54
Deadlock
Locking introduces the potential for deadlock
waiting for a condition that will never be
true. Any trajectory that enters the deadlock
region will eventually reach the deadlock state,
waiting for either s or t to become
nonzero. Other trajectories luck out and skirt
the deadlock region. Unfortunate fact deadlock
is often non-deterministic.
Thread 2
V(s)
deadlock state
forbidden region for s
V(t)
P(s)
deadlock region
forbidden region for t
P(t)
P(s)
V(s)
P(t)
V(t)
Thread 1
Initially, st1
55
Threads Summary
  • Threads provide another mechanism for writing
    concurrent programs.
  • Threads are growing in popularity
  • Somewhat cheaper than processes.
  • Easy to share data between threads.
  • However, the ease of sharing has a cost
  • Easy to introduce subtle synchronization errors.
  • Tread carefully with threads!
  • For more info
  • D. Butenhof, Programming with Posix Threads,
    Addison-Wesley, 1997.

56
Final words
  • Concurrent Servers
  • Event-based concurrent servers
  • Thread-pool based concurrent servers
  • What next?

57
Event-Based Concurrent Servers Using I/O
Multiplexing
  • Maintain a pool of connected descriptors.
  • Repeat the following forever
  • Use the Unix select function to block until
  • (a) New connection request arrives on the
    listening descriptor.
  • (b) New data arrives on an existing connected
    descriptor.
  • If (a), add the new connection to the pool of
    connections.
  • If (b), read any available data from the
    connection
  • Close connection on EOF and remove it from the
    pool.

58
The select Function
  • select() sleeps until one or more file
    descriptors in the set readset are ready for
    reading.

include ltsys/select.hgt int select(int maxfdp1,
fd_set readset, NULL, NULL, NULL)
  • readset
  • Opaque bit vector (max FD_SETSIZE bits) that
    indicates membership in a descriptor set.
  • If bit k is 1, then descriptor k is a member of
    the descriptor set.
  • maxfdp1
  • Maximum descriptor in descriptor set plus 1.
  • Tests descriptors 0, 1, 2, ..., maxfdp1 - 1 for
    set membership.

select() returns the number of ready descriptors
and sets each bit of readset to indicate the
ready status of its corresponding descriptor.
59
Macros for Manipulating Set Descriptors
  • void FD_ZERO(fd_set fdset)
  • Turn off all bits in fdset.
  • void FD_SET(int fd, fd_set fdset)
  • Turn on bit fd in fdset.
  • void FD_CLR(int fd, fd_set fdset)
  • Turn off bit fd in fdset.
  • int FD_ISSET(int fd, fdset)
  • Is bit fd in fdset turned on?

60
select Example
/ main loop wait for connection request or
stdin command. If connection request, then
echo input line and close connection. If
stdin command, then process. /
printf("servergt ") fflush(stdout) while
(notdone) / select check if the
user typed something to stdin or if a
connection request arrived. /
FD_ZERO(readfds) / initialize the fd
set / FD_SET(listenfd, readfds) / add
socket fd / FD_SET(0, readfds) /
add stdin fd (0) / Select(listenfd1,
readfds, NULL, NULL, NULL)
61
select Example (cont)
  • First we check for a pending event on stdin.

/ if the user has typed a command, process it
/ if (FD_ISSET(0, readfds)) fgets(buf,
BUFSIZE, stdin) switch (buf0) case
'c' / print the connection count /
printf("Received d conn. requests so far.\n",
connectcnt) printf("servergt ")
fflush(stdout) break case 'q' /
terminate the server / notdone 0
break default / bad input /
printf("ERROR unknown command\n")
printf("servergt ") fflush(stdout)

62
select Example (cont)
  • Next we check for a pending connection request.

/ if a connection request has arrived, process
it / if (FD_ISSET(listenfd, readfds))
connfd Accept(listenfd,
(struct sockaddr ) clientaddr, clientlen)
connectcnt bzero(buf, BUFSIZE)
Rio_readn(connfd, buf, BUFSIZE)
Rio_writen(connfd, buf, strlen(buf))
Close(connfd) / while /
63
Event-based Concurrent Echo Server
/ echoservers.c - A concurrent echo server
based on select / include "csapp.h"
typedef struct / represents a pool of
connected descriptors / int maxfd
/ largest descriptor in read_set /
fd_set read_set / set of all active
descriptors / fd_set ready_set / subset
of descriptors ready for reading / int
nready / number of ready descriptors from
select / int maxi / highwater
index into client array / int
clientfdFD_SETSIZE / set of active
descriptors / rio_t clientrioFD_SETSIZE
/ set of active read buffers / pool int
byte_cnt 0 / counts total bytes received by
server /
64
Event-based Concurrent Server (cont)
int main(int argc, char argv) int
listenfd, connfd, clientlen sizeof(struct
sockaddr_in) struct sockaddr_in clientaddr
static pool pool listenfd
Open_listenfd(argv1) init_pool(listenfd,
pool) while (1)
pool.ready_set pool.read_set
pool.nready Select(pool.maxfd1,
pool.ready_set,
NULL, NULL, NULL) if
(FD_ISSET(listenfd, pool.ready_set))
connfd Accept(listenfd, (SA
)clientaddr,clientlen)
add_client(connfd, pool)
check_clients(pool)
65
Event-based Concurrent Server (cont)
/ initialize the descriptor pool / void
init_pool(int listenfd, pool p) /
Initially, there are no connected descriptors /
int i p-gtmaxi -1 for (i0
ilt FD_SETSIZE i) p-gtclientfdi -1
/ Initially, listenfd is only member of
select read set / p-gtmaxfd listenfd
FD_ZERO(p-gtread_set) FD_SET(listenfd,
p-gtread_set)
66
Event-based Concurrent Server (cont)
void add_client(int connfd, pool p) / add
connfd to pool p / int i
p-gtnready-- for (i 0 i lt FD_SETSIZE
i) / Find available slot / if
(p-gtclientfdi lt 0) p-gtclientfdi
connfd Rio_readinitb(p-gtclientrio
i, connfd) FD_SET(connfd,
p-gtread_set) / Add desc to read set /
if (connfd gt p-gtmaxfd) / Update max
descriptor num / p-gtmaxfd
connfd if (i gt p-gtmaxi) / Update
pool high water mark / p-gtmaxi
i break if (i
FD_SETSIZE) / Couldn't find an empty slot /
app_error("add_client error Too many
clients")
67
Event-based Concurrent Server (cont)
void check_clients(pool p) / echo line from
ready descs in pool p / int i, connfd, n
char bufMAXLINE rio_t rio for
(i 0 (i lt p-gtmaxi) (p-gtnready gt 0) i)
connfd p-gtclientfdi rio
p-gtclientrioi / If the descriptor
is ready, echo a text line from it / if
((connfd gt 0) (FD_ISSET(connfd,
p-gtready_set))) p-gtnready--
if ((n Rio_readlineb(rio, buf,
MAXLINE)) ! 0) byte_cnt n
Rio_writen(connfd, buf, n)
else / EOF detected,
remove descriptor from pool /
Close(connfd) FD_CLR(connfd,
p-gtread_set) p-gtclientfdi
-1
68
Pro and Cons of Event-Based Designs
  • One logical control flow.
  • Can single-step with a debugger.
  • No process or thread control overhead.
  • Design of choice for high-performance Web servers
    and search engines.
  • - Significantly more complex to code than
    process- or thread-based designs.
  • - Can be vulnerable to denial of service attack
  • How?

69
Threadpool
  • In the thread-based server, a new thread was
    created for every client.
  • Creating a new thread take about 10K cycles
  • When a client disconnects, the thread is wasted
  • Idea re-use threads
  • Thread pool a set of worker threads and a buffer
    of jobs
  • Each worker thread iterates as follows
  • Remove a job from the queue
  • Executes the job
  • A main thread puts the jobs on the queue

70
Threadpool-based server
  • The buffer

typedef struct int buf / Buffer
array / int n / Maximum number
of slots / int front /
buf(front1)n is first item / int rear
/ bufrearn is last item /
/ smaphores here / sbuf_t void
sbuf_init(sbuf_t sp, int n) void
sbuf_deinit(sbuf_t sp) void sbuf_insert(sbuf_t
sp, int item) int sbuf_remove(sbuf_t sp)
The worker thread
while (1) int connfd sbuf_remove(sbuf) /
Remove connfd from buffer / echo_cnt(connfd)
/ Service client /
Close(connfd)
71
Threadpool-based Concurrent Server
  • The main thread starts by creating a bunch of
    worker threads (the thread pool)

for (i 0 i lt NTHREADS i) / Create worker
threads / Pthread_create(tid, NULL, thread,
NULL)
And then enters the standard server infinite loop
while (1) connfd Accept(listenfd, (SA )
clientaddr, clientlen) sbuf_insert(sbuf,
connfd) / Insert connfd in buffer /
Note that the main threads executes the echo
application on connfd by just inserting it into
the buffer
72
System courses after CSC373/374?
  • Operating Systems Implementation (CSC343/443)
  • Distributed Systems (SE335/336 or SE435/536)
  • Network Programming (TDC561)
  • Web Services (SE560)
  • Concurrent Software Development (SE552)
  • Mobile and Wireless System Development (SE540)
  • Limited and Embedded Device Development (SE542)
  • Frameworks for Web Application Development
    (CSC305/CSC4XX)
Write a Comment
User Comments (0)
About PowerShow.com