0% found this document useful (0 votes)
7 views39 pages

24 Sync Advanced

Uploaded by

Việt Phạm
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views39 pages

24 Sync Advanced

Uploaded by

Việt Phạm
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 39

Carnegie Mellon

Synchroniza+on:  Advanced  
15-­‐213:  Introduc0on  to  Computer  Systems  
24th  Lecture,  Nov.  18,  2010  

Instructors:    
Randy  Bryant  and  Dave  O’Hallaron  

1
Carnegie Mellon

Today  
 Producer-­‐consumer  problem  
 Readers-­‐writers  problem  
 Thread  safety  
 Races  
 Deadlocks  

2
Carnegie Mellon

Using  Semaphores  to  Schedule  Access  


to  Shared  Resources  
 Basic  idea:  Thread  uses  a  semaphore  opera+on  to  no+fy  
another  thread  that  some  condi+on  has  become  true  
 Use  coun0ng  semaphores  to  keep  track  of  resource  state.  
 Use  binary  semaphores  to  no0fy  other  threads.    

 Two  classic  examples:  


 The  Producer-­‐Consumer  Problem  
 The  Readers-­‐Writers  Problem  

3
Carnegie Mellon

Producer-­‐Consumer  Problem  
producer   shared   consumer  
thread   buffer   thread  

 Common  synchroniza+on  paGern:  


 Producer  waits  for  empty  slot,  inserts  item  in  buffer,  and  no0fies  consumer  
 Consumer  waits  for  item,  removes  it  from  buffer,  and  no0fies  producer  

 Examples  
 Mul0media  processing:  
 Producer  creates  MPEG  video  frames,  consumer  renders  them    
  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  
4
Carnegie Mellon

Producer-­‐Consumer  on  1-­‐element  Buffer  


#include “csapp.h” int main() {
pthread_t tid_producer;
#define NITERS 5 pthread_t tid_consumer;

void *producer(void *arg); /* Initialize the semaphores */


void *consumer(void *arg); Sem_init(&shared.empty, 0, 1);
Sem_init(&shared.full, 0, 0);
struct {
int buf; /* shared var */ /* Create threads and wait */
sem_t full; /* sems */ Pthread_create(&tid_producer, NULL,
sem_t empty; producer, NULL);
} shared; Pthread_create(&tid_consumer, NULL,
consumer, NULL);
Pthread_join(tid_producer, NULL);
Pthread_join(tid_consumer, NULL);

exit(0);
}

5
Carnegie Mellon

Producer-­‐Consumer  on  1-­‐element  Buffer  


Ini+ally:    empty==1, full==0

Producer  Thread   Consumer  Thread  


void *producer(void *arg) { void *consumer(void *arg) {
int i, item; int i, item;

for (i=0; i<NITERS; i++) { for (i=0; i<NITERS; i++) {


/* Produce item */ /* Read item from buf */
item = i; P(&shared.full);
printf("produced %d\n", item = shared.buf;
item); V(&shared.empty);

/* Write item to buf */ /* Consume item */


P(&shared.empty); printf("consumed %d\n“, item);
shared.buf = item; }
V(&shared.full); return NULL;
} }
return NULL;
}
6
Carnegie Mellon

Producer-­‐Consumer  on  an  n-­‐element  Buffer  


 Requires  a  mutex  and  two  coun+ng  semaphores:  
 mutex:  enforces  mutually  exclusive  access  to  the  the  buffer  
 slots:  counts  the  available  slots  in  the  buffer  
 items:  counts  the  available  items  in  the  buffer  

 Implemented  using  a  shared  buffer  package  called  sbuf.    

7
Carnegie Mellon

sbuf Package  -­‐  Declara+ons  


#include "csapp.h”

typedef struct {
int *buf; /* Buffer array */
int n; /* Maximum number of slots */
int front; /* buf[(front+1)%n] is first item */
int rear; /* buf[rear%n] is last item */
sem_t mutex; /* Protects accesses to buf */
sem_t slots; /* Counts available slots */
sem_t items; /* Counts available items */
} 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);

sbuf.h  
8
Carnegie Mellon

sbuf Package  -­‐  Implementa+on  


Ini+alizing  and  deini+alizing  a  shared  buffer:  
/* Create an empty, bounded, shared FIFO buffer with n slots */
void sbuf_init(sbuf_t *sp, int n)
{
sp->buf = Calloc(n, sizeof(int));
sp->n = n; /* Buffer holds max of n items */
sp->front = sp->rear = 0; /* Empty buffer iff front == rear */
Sem_init(&sp->mutex, 0, 1); /* Binary semaphore for locking */
Sem_init(&sp->slots, 0, n); /* Initially, buf has n empty slots */
Sem_init(&sp->items, 0, 0); /* Initially, buf has zero items */
}

/* Clean up buffer sp */
void sbuf_deinit(sbuf_t *sp)
{
Free(sp->buf);
}

sbuf.c  
9
Carnegie Mellon

sbuf Package  -­‐  Implementa+on  


Inser+ng  an  item  into  a  shared  buffer:  
/* Insert item onto the rear of shared buffer sp */
void sbuf_insert(sbuf_t *sp, int item)
{
P(&sp->slots); /* Wait for available slot */
P(&sp->mutex); /* Lock the buffer */
sp->buf[(++sp->rear)%(sp->n)] = item; /* Insert the item */
V(&sp->mutex); /* Unlock the buffer */
V(&sp->items); /* Announce available item */
}

sbuf.c  

10
Carnegie Mellon

sbuf Package  -­‐  Implementa+on  


Removing  an  item  from  a  shared  buffer:  
/* Remove and return the first item from buffer sp */
int sbuf_remove(sbuf_t *sp)
{
int item;

P(&sp->items); /* Wait for available item */


P(&sp->mutex); /* Lock the buffer */
item = sp->buf[(++sp->front)%(sp->n)]; /* Remove the item */
V(&sp->mutex); /* Unlock the buffer */
V(&sp->slots); /* Announce available slot */
return item;
}
sbuf.c  

11
Carnegie Mellon

Today  
 Producer-­‐consumer  problem  
 Readers-­‐writers  problem  
 Thread  safety  
 Races  
 Deadlocks  

12
Carnegie Mellon

Readers-­‐Writers  Problem  
 Generaliza+on  of  the  mutual  exclusion  problem  

 Problem  statement:  
 Reader  threads  only  read  the  object  
 Writer  threads  modify  the  object  
 Writers  must  have  exclusive  access  to  the  object  
 Unlimited  number  of  readers  can  access  the  object  

 Occurs  frequently  in  real  systems,  e.g.,  


 Online  airline  reserva0on  system  
 Mul0threaded  caching  Web  proxy  

13
Carnegie Mellon

Variants  of  Readers-­‐Writers    


 First  readers-­‐writers  problem  (favors  readers)  
 No  reader  should  be  kept  wai0ng  unless  a  writer  has  already  been  
granted  permission  to  use  the  object.    
 A  reader  that  arrives  a[er  a  wai0ng  writer  gets  priority  over  the  
writer.    

 Second  readers-­‐writers  problem  (favors  writers)  


 Once  a  writer  is  ready  to  write,  it  performs  its  write  as  soon  as  
possible    
 A  reader  that  arrives  a[er  a  writer  must  wait,  even  if  the  writer  is  
also  wai0ng.    

 Starva5on  (where  a  thread  waits  indefinitely)  is  possible  


in  both  cases.    
14
Carnegie Mellon

Solu+on  to  First  Readers-­‐Writers  Problem  


Readers:   Writers:  
int readcnt; /* Initially 0 */ void writer(void)
sem_t mutex, w; /* Both initially 1 */ {
while (1) {
void reader(void) P(&w);
{
while (1) { /* Writing here */
P(&mutex);
readcnt++; V(&w);
if (readcnt == 1) /* First in */ }
P(&w); }
V(&mutex);
rw1.c  
/* Reading happens here */

P(&mutex);
readcnt--;
if (readcnt == 0) /* Last out */
V(&w);
V(&mutex);
}
} 15
Carnegie Mellon

Case  Study:  Prethreaded  Concurrent  Server  

Pool  of    
worker  
 threads  
Service  client  
 Client   Worker  
Insert   thread  
Accept   Master   descriptors   Remove  
 Buffer  
...  

...  
connec5ons   thread   descriptors  
Worker  
Client  
thread  
Service  client  

16
Carnegie Mellon

Prethreaded  Concurrent  Server  


sbuf_t sbuf; /* Shared buffer of connected descriptors */

int main(int argc, char **argv)


{
int i, listenfd, connfd, port;
socklen_t clientlen=sizeof(struct sockaddr_in);
struct sockaddr_in clientaddr;
pthread_t tid;

port = atoi(argv[1]);
sbuf_init(&sbuf, SBUFSIZE);
listenfd = Open_listenfd(port);

for (i = 0; i < NTHREADS; i++) /* Create worker threads */


Pthread_create(&tid, NULL, thread, NULL);

while (1) {
connfd = Accept(listenfd, (SA *) &clientaddr, &clientlen);
sbuf_insert(&sbuf, connfd); /* Insert connfd in buffer */
}
}

echoservert_pre.c   17
Carnegie Mellon

Prethreaded  Concurrent  Server  

Worker  thread  rou+ne:    


void *thread(void *vargp)
{
Pthread_detach(pthread_self());
while (1) {
int connfd = sbuf_remove(&sbuf); /* Remove connfd from
buffer */
echo_cnt(connfd); /* Service client */
Close(connfd);
}
}
echoservert_pre.c  

18
Carnegie Mellon

Prethreaded  Concurrent  Server  

echo_cnt  ini+aliza+on  rou+ne:  


static int byte_cnt; /* Byte counter */
static sem_t mutex; /* and the mutex that protects it */

static void init_echo_cnt(void)


{
Sem_init(&mutex, 0, 1);
byte_cnt = 0;
}

echo_cnt.c  

19
Carnegie Mellon

Prethreaded  Concurrent  Server  


Worker  thread  service  rou+ne:  

void echo_cnt(int connfd)


{
int n;
char buf[MAXLINE];
rio_t rio;
static pthread_once_t once = PTHREAD_ONCE_INIT;

Pthread_once(&once, init_echo_cnt);
Rio_readinitb(&rio, connfd);
while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) {
P(&mutex);
byte_cnt += n;
printf("thread %d received %d (%d total) bytes on fd %d\n”,
(int) pthread_self(), n, byte_cnt, connfd);
V(&mutex);
Rio_writen(connfd, buf, n);
}
}

echo_cnt.c  
20
Carnegie Mellon

Today  
 Producer-­‐consumer  problem  
 Readers-­‐writers  problem  
 Thread  safety  
 Races  
 Deadlocks  

21
Carnegie Mellon

Crucial  concept:  Thread  Safety  


 Func+ons  called  from  a  thread    must  be  thread-­‐safe  

 Def:    A  func+on  is  thread-­‐safe  iff  it  will  always  produce  


correct  results  when  called  repeatedly  from  mul+ple  
concurrent  threads.    

 Classes  of  thread-­‐unsafe  func+ons:  


 Class  1:  Func0ons  that  do  not  protect  shared  variables.  
 Class  2:  Func0ons  that  keep  state  across  mul0ple  invoca0ons.  
 Class  3:  Func0ons  that  return  a  pointer  to  a  sta0c  variable.  
 Class  4:  Func0ons  that  call  thread-­‐unsafe  func0ons.    

22
Carnegie Mellon

Thread-­‐Unsafe  Func+ons  (Class  1)  


 Failing  to  protect  shared  variables  
 Fix:  Use  P  and  V  semaphore  opera0ons  
 Example:  goodcnt.c  
 Issue:  Synchroniza0on  opera0ons  will  slow  down  code  

23
Carnegie Mellon

Thread-­‐Unsafe  Func+ons  (Class  2)  


 Relying  on  persistent  state  across  mul+ple  func+on  invoca+ons  
 Example:  Random  number  generator  that  relies  on  sta0c  state    

static unsigned int next = 1;

/* rand: return pseudo-random integer on 0..32767 */


int rand(void)
{
next = next*1103515245 + 12345;
return (unsigned int)(next/65536) % 32768;
}

/* srand: set seed for rand() */


void srand(unsigned int seed)
{
next = seed;
}

24
Carnegie Mellon

Thread-­‐Safe  Random  Number  Generator  


 Pass  state  as  part  of  argument  
 and,  thereby,  eliminate  sta0c  state    

/* rand_r - return pseudo-random integer on 0..32767 */

int rand_r(int *nextp)


{
*nextp = *nextp*1103515245 + 12345;
return (unsigned int)(*nextp/65536) % 32768;
}

 Consequence:  programmer  using  rand_r  must  maintain  seed

25
Carnegie Mellon

Thread-­‐Unsafe  Func+ons  (Class  3)  


 Returning  a  pointer    to  a  
sta+c  variable  
 Fix  1.    Rewrite  func+on  so   /* lock-and-copy version */
char *ctime_ts(const time_t *timep,
caller  passes  address  of   char *privatep)
variable  to  store  result   {
char *sharedp;
 Requires  changes  in  caller  and  
callee   P(&mutex);
sharedp = ctime(timep);
 Fix  2.  Lock-­‐and-­‐copy   strcpy(privatep, sharedp);
V(&mutex);
 Requires  simple  changes  in   return privatep;
caller  (and  none  in  callee)   }
 However,  caller  must  free  
memory.    
Warning:  Some  func+ons  like  gethostbyname  
require  a  deep  copy.  Use  reentrant  
gethostbyname_r  version  instead.  
26
Carnegie Mellon

Thread-­‐Unsafe  Func+ons  (Class  4)  


 Calling  thread-­‐unsafe  func+ons  
 Calling  one  thread-­‐unsafe  func0on  makes  the  en0re  func0on  that  calls  it  
thread-­‐unsafe  

 Fix:  Modify  the  func0on  so  it  calls  only  thread-­‐safe  func0ons    

27
Carnegie Mellon

Reentrant  Func+ons    
 Def:  A  func+on  is  reentrant  iff  it  accesses  no  shared  
variables  when  called  by  mul+ple  threads.    
 Important  subset  of  thread-­‐safe  func0ons.  
 Require  no  synchroniza0on  opera0ons.  
 Only  way  to  make  a  Class  2  func0on  thread-­‐safe  is  to  make  it  
reetnrant  (e.g.,  rand_r  )  

All  func+ons  
Thread-­‐safe  
func+ons  
Thread-­‐unsafe  
Reentrant   func+ons  
func+ons  

28
Carnegie Mellon

Thread-­‐Safe  Library  Func+ons  


 All  func+ons  in  the  Standard  C  Library  (at  the  back  of  your  
K&R  text)  are  thread-­‐safe  
 Examples:  malloc,  free,  printf,  scanf
 Most  Unix  system  calls  are  thread-­‐safe,  with  a  few  
excep+ons:  
Thread-­‐unsafe  func+on  Class  Reentrant  version  
asctime 3 asctime_r
ctime 3 ctime_r
gethostbyaddr 3 gethostbyaddr_r
gethostbyname 3 gethostbyname_r
inet_ntoa 3 (none)
localtime 3 localtime_r
rand 2 rand_r

29
Carnegie Mellon

Today  
 Producer-­‐consumer  problem  
 Readers-­‐writers  problem  
 Thread  safety  
 Races  
 Deadlocks  

30
Carnegie Mellon

One  Worry:  Races  


 A  race  occurs  when  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 tid[N];
int i;
for (i = 0; i < N; i++)
Pthread_create(&tid[i], NULL, thread, &i);
for (i = 0; i < N; i++)
Pthread_join(tid[i], NULL);
exit(0);
}

/* thread routine */
void *thread(void *vargp) {
int myid = *((int *)vargp);
printf("Hello from thread %d\n", myid);
return NULL;
}
race.c   31
Carnegie Mellon

Race  Elimina+on  
 Make  sure  don’t  have  unintended  sharing  of  state  
/* a threaded program without the race */
int main() {
pthread_t tid[N];
int i;
for (i = 0; i < N; i++) {
int *valp = malloc(sizeof(int));
*valp = i;
Pthread_create(&tid[i], NULL, thread, valp);
}
for (i = 0; i < N; i++)
Pthread_join(tid[i], NULL);
exit(0);
}

/* thread routine */
void *thread(void *vargp) {
int myid = *((int *)vargp);
free(vargp);
printf("Hello from thread %d\n", myid);
return NULL;
} norace.c   32
Carnegie Mellon

Today  
 Producer-­‐consumer  problem  
 Readers-­‐writers  problem  
 Thread  safety  
 Races  
 Deadlocks  

33
Carnegie Mellon

Another  Worry:  Deadlock  


 Def:  A  process  is  deadlocked  iff  it  is  wai+ng  for  a  condi+on  
that  will  never  be  true.    

 Typical  Scenario  
 Processes  1  and  2  needs  two  resources  (A  and  B)  to  proceed  
 Process  1  acquires  A,  waits  for  B  
 Process  2  acquires  B,  waits  for  A  
 Both  will  wait  forever!  

34
Carnegie Mellon

Deadlocking  With  Semaphores  


int main()
{
pthread_t tid[2];
Sem_init(&mutex[0], 0, 1); /* mutex[0] = 1 */
Sem_init(&mutex[1], 0, 1); /* mutex[1] = 1 */
Pthread_create(&tid[0], NULL, count, (void*) 0);
Pthread_create(&tid[1], NULL, count, (void*) 1);
Pthread_join(tid[0], NULL);
Pthread_join(tid[1], NULL);
printf("cnt=%d\n", cnt);
exit(0);
}
void *count(void *vargp)
{
Tid[0]:   Tid[1]:  
int i;
int id = (int) vargp;
P(s0);   P(s1);  
for (i = 0; i < NITERS; i++) { P(s1);   P(s0);  
P(&mutex[id]); P(&mutex[1-id]); cnt++;   cnt++;  
cnt++; V(s0);   V(s1);  
V(&mutex[id]); V(&mutex[1-id]); V(s1);   V(s0);  
}
return NULL;
} 35
Carnegie Mellon

Deadlock  Visualized  in  Progress  Graph  


Thread  2   Locking  introduces    the  
poten+al  for  deadlock:    
wai+ng  for  a  condi+on  that  
Deadlock   will  never  be  true  
V(s0)  
Forbidden  region   state  
Any  trajectory  that  enters  
for  s0   the  deadlock  region  will  
V(s1)   eventually  reach  the  
deadlock  state,  wai+ng  for  
either  s0  or  s1  to  become  
P(s0)   nonzero  
Deadlock   Forbidden  region  
region   for  s1   Other  trajectories  luck  out  and  
P(s1)   skirt  the  deadlock  region  

Thread  1   Unfortunate  fact:  deadlock  is  


P(s0)   P(s1)   V(s0)   V(s1)   ofen  nondeterminis+c  
s0=s1=1  
36
Carnegie Mellon

Avoiding  Deadlock   Acquire  shared  resources  in  same  order  


int main()
{
pthread_t tid[2];
Sem_init(&mutex[0], 0, 1); /* mutex[0] = 1 */
Sem_init(&mutex[1], 0, 1); /* mutex[1] = 1 */
Pthread_create(&tid[0], NULL, count, (void*) 0);
Pthread_create(&tid[1], NULL, count, (void*) 1);
Pthread_join(tid[0], NULL);
Pthread_join(tid[1], NULL);
printf("cnt=%d\n", cnt);
exit(0);
}
void *count(void *vargp)
{ Tid[0]:   Tid[1]:  
int i;
P(s0);   P(s0);  
int id = (int) vargp;
for (i = 0; i < NITERS; i++) {
P(s1);   P(s1);  
P(&mutex[0]); P(&mutex[1]); cnt++;   cnt++;  
cnt++; V(s0);   V(s1);  
V(&mutex[id]); V(&mutex[1-id]); V(s1);   V(s0);  
}
return NULL;
} 37
Carnegie Mellon

Avoided  Deadlock  in  Progress  Graph  


Thread  2   No  way  for  trajectory  to  get  
stuck  

V(s0)   Processes  acquire  locks  in  


Forbidden  region   same  order  
for  s0  
V(s1)   Order  in  which  locks  released  
immaterial  

P(s0)  
Forbidden  region  
P(s1)   for  s1  

Thread  1  
P(s0)   P(s1)   V(s0)   V(s1)  
s0=s1=1  
38
Carnegie Mellon

Threads  Summary  
 Threads  provide  another  mechanism  for  wri+ng  
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  synchroniza0on  errors  
 Tread  carefully  with  threads!  

 For  more  info:  


 D.  Butenhof,  “Programming  with  Posix  Threads”,  Addison-­‐Wesley,  
1997  

39

You might also like