CS252: Systems Programming
This presentation is the property of its rightful owner.
Sponsored Links
1 / 36

CS252: Systems Programming PowerPoint PPT Presentation


  • 93 Views
  • Uploaded on
  • Presentation posted in: General

CS252: Systems Programming. Ninghui Li Based on Slides by Prof. Gustavo Rodriguez-Rivera Topic 11: Thread-safe Data Structures, Semaphores. Pthread Overview. Pthreads : POSIX threads Mutual exclusion and synchronization tools that we cover Mutex locks Spin locks Read/write locks

Download Presentation

CS252: Systems Programming

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


Cs252 systems programming

CS252: Systems Programming

Ninghui Li

Based on Slides by Prof. Gustavo Rodriguez-Rivera

Topic 11: Thread-safe Data Structures, Semaphores


Cs252 systems programming

Pthread Overview

  • Pthreads: POSIX threads

  • Mutual exclusion and synchronization tools that we cover

    • Mutex locks

    • Spin locks

    • Read/write locks

    • Semaphores (not part of pthreads, but part of POSIX standards)

    • Condition variables: Suspend the execution of a thread until some pre-defined condition occurs


Cs252 systems programming

Type of Mutex Locks

  • Depends on behavior in the following 2 cases

    • A: a thread that holds lock calls lock again

    • B: a thread that does not hold lock calls unlock

  • Recursive lock: A succeeds (locking n times requires unlocking n times); B returns error

    • pthread_mutexattr_init(&Attr); pthread_mutexattr_settype(&Attr, PTHREAD_MUTEX_RECURSIVE);

    • pthread_mutex_init(&Mutex, &Attr);

  • Error checking lock: returns error on A,B

  • Normal (fast) lock: deadlock on A; undefined on B

    • Implementation does not require remembering A,B

  • Default lock: undefined on A,B


Cs252 systems programming

Which Type to Use

  • It is recommended to use Recursivein production systems when available.

  • When one has high confidence and full control of code, then using normal (fast) is fine

    • i.e., when one is certain that A and B cannot occur


Cs252 systems programming

Behavior When Lock

  • Blocked until the lock is available.

    • Behavior of mutex lock

  • Yield CPU and try to obtain lock when scheduled, return when lock is available

    • Seems to be undesirable, since thread is off CPU, might as well wait until lock is available

  • Busy-waiting when lock is unavailable.

    • Behavior of SpinLock; don’t use one single CPU

  • Return “unable to obtain lock” immediately

    • To get this behavior in pthreads, use trylock

      • E.g., pthread_mutex_trylock()


Cs252 systems programming

Threads and Fork

  • Example:

    • In Solaris one process creates 5 threads using thr_create and then calls fork() then the child will also have a copy of the 5 threads so we have a total of 10 threads

    • In Solaris (or other UNIX flavors) one process creates 5 threads using pthread create and then calls fork() therefore the child will only get one thread that is the thread that called fork so we have a total of 6 threads

  • Solaris has a system call fork1() that only copies the calling thread in the child process.

  • Modern UNIX use the pthread_create semantics that only copies the calling thread.


Cs252 systems programming

Thread Safe and Race Condition

Data structures and functions that are able to handle multiple threads are called “Thread Safe” or “Multi-Threaded (MT), or “Concurrent”.

A Thread Unsafe function or data structure is one that when executed by multiple threads can result in logical errors.

A bug related to having multiple threads modifying a data structure simultaneously is called a “Race Condition”.

Race Conditions are difficult to debug because they are often difficult to reproduce.


Cs252 systems programming

A Thread Unsafe List Class

#include <pthread.h>

struct ListEntry {

int _val;ListEntry *_next;

};

class List{

ListEntry *_head;

public:List();void insert(int val);int remove();

}

List::List(){

_head = NULL;

}

List::insert(intval){

ListEntry *e =

new ListEntry;

e->_val = val;

a) e->_next = _head;

b) _head = e;

}


Cs252 systems programming

A Thread Unsafe List Class

int List::remove(){

ListEntry *tmp;

c) tmp = _head;

if(tmp == NULL) { return -1;

}

d) _head=tmp->_next;

intval=tmp->_val;

delete tmp;

return val;

}


Cs252 systems programming

Race Conditions in List

We can have the following race condition: Assume T1 calls remove() and T2 calls insert().

Initially

_head

1

2

NULL


Cs252 systems programming

Race Conditions in List

2. T2 insert(5)

ctxswitch

1. T1 remove()

_head

1

2

_head

1

2

tmp1

NULL

NULL

tmp1

5

E2

1. T1: c) tmp=_head

2. T2: a) e2->next=_head

3. T2 insert(5)

ctxswitch

4. T1 remove()

1

2

_head

1

2

tmp1

NULL

tmp1

NULL

_head

5

5

4. T1: d)_head=tmp->_next;

Node 5 is lost!!!

3. T2: b) _head = e2


Cs252 systems programming

Race Conditions in List

Now find a race condition involving two insert.

Now find a race condition involving two remove.

Initially

_head

1

2

NULL


Cs252 systems programming

An Thread Safe List Class

#include <pthread.h>

struct ListEntry {

int _val;ListEntry *_next;

};

class MTList{

ListEntry *_head;pthread_mutex_t _mutex;

public:MTList();void insert(int val);int remove();

}

MTList::MTList(){

_head = NULL;

pthread_mutex_init(

&_mutex, NULL );

}

MTList::insert(int val){ ListEntry *e =

new ListEntry;e->_val = val;pthread_mutex_lock(&_mutex);

a) e->_next = _head;

b)_head = e;

pthread_mutex_unlock(&mutex);

}


Cs252 systems programming

A Thread Safe List

intMTList::remove(){

ListEntry *tmp;pthread_mutex_lock(&_mutex); c) tmp = _head;if(tmp == NULL) {

pthread_mutex_unlock(&_mutex); return -1;

}d) _head=tmp->_next; pthread_mutex_unlock(&mutex);

intval=tmp->_val;

delete tmp;

return val;

}


Cs252 systems programming

An Additional Requirement in the List Class

The MT List implementation described before returns -1 if the list is empty.

Suppose that we would like an implementation where the remove waits if the List is empty.

This behavior is naturally implemented using semaphores.


Cs252 systems programming

Semaphores

  • Semaphores are a signaling mechanism.

  • A semaphore is initialized with an initial counter that is greater or equal to 0.

    • Conceptually indicating # of available resources

  • A sem_wait call decreases the counter, and block the thread if the value is negative.

  • A sem_post call increases the counter, and unblock one waiting thread


Cs252 systems programming

Semaphore Calls

  • Declaration:

    • #include <semaphore.h>

    • sem_tsem;

  • Initialization:

    • sem_init (sem_t*sem, intpshared, unsigned int value);

  • Decrement Semaphore:

    • sem_wait ( sem_t*sem);

  • Increment Semaphore

    • sem_post (sem_t*sem);

  • Man pages

    • man sem_wait


Cs252 systems programming

Semaphore Calls Semantics

Pseudocode:

sem_init(sem_t*sem, counter){

sem -> count = counter;

}

sem_wait(sem_t*sem){

sem-> count--;

if(sem ->count < 0){

wait();

}

}


Cs252 systems programming

Semaphore Calls Semantics

sem_post(sem_t*sem){

sem ->count++;

if(there is a thread waiting){

wake up the thread that

has been waiting the longest.

}

// one out and can let one in

}

Note: The mutex lock calls are omitted in the pseudocode for simplicity.


Cs252 systems programming

Semaphores vs. Mutex

  • Semaphores are a signaling mechanism; not specifically for mutual exclusion

  • Semaphore can be initialized to any positive integer, and mutex is binary

  • Binary semaphore vs. Mutex

    • With mutex, only the thread holding the lock is supposed to unlock

    • With semaphore, any thread can call sem_post to wake up a blocking thread

    • In general, semaphore and mutex are different

    • One can use a binary semaphore to achieve the effect of mutex, if one is careful to ensure that sem_postis only called after sem_wait succeeds


Cs252 systems programming

Use of Semaphore Counter

  • Mutex Lock Case: initial counter == 1

    • Can achieve effect of Mutex Lock

  • N Resources Case: initial counter is n > 1

    • Control access to a fixed number n of resources. Example: Access to 5 printers. 5 computers can use them. 6th computer will need to wait.

  • Wait for an Event Case: initial counter == 0

    • Synchronization. Wait for an event. A thread calling sem_waitwill block until another threads sends an event by calling sem_post.


Cs252 systems programming

Example Semaphore count=1 (Mutex Lock Case)

Assume sem_tsem; sem_init(&sem, 1);

T1 T2

sem_wait(&sem)

sem->count--(==0)

Does not wait

..

ctxswitchsem_wait(&sem)

sem->count--(==-1)

if (sem->count < 0)

wait (ctxswitch)

..

sem_post(&sem)

sem->count++(==0)

wakeup T2 continue


Cs252 systems programming

Example Semaphore count=3 (N Resources Case)

Assume sem_tsem; sem_init(&sem, 3); (3 printers)

T1 T2 T3

sem_wait(&sem)

sem->count--(==2)

Does not wait

print

.. sem_wait(&sem)

sem->count--(==1)

Does not wait

print

..

sem_wait(&sem)

sem->count--(==0)

Does not wait

print


Cs252 systems programming

Example Semaphore count=3 (N Resources Case)

T4 T5 T1

sem_wait(&sem)

sem->count--(==-1)

wait

sem_wait(&sem)

sem->count--(==-2)

Wait

Finished printing

sem_post(&sem)

sem->count++(==-1)

Wakeup T4

print


Cs252 systems programming

Example Semaphore count=0 Wait for an Event Case

Assume sem_tsem; sem_init(&sem, 0);

T1 T2

// wait for event

sem_wait(&sem)

sem->count--(==-1)

wait

//Send event to t1

sem_post(&sem)

sem->count++(==0)

Wakeup T1

..

T1 continues


Cs252 systems programming

A Synchronized List Class

We want to implement a List class where remove() will block if the list is empty.

To implement this class, we will use a semaphore “_emptySem” that will be initialized with a counter of 0.

Remove() will call sem_wait(&_emptySem) and it will block until insert() calls sem_post(&emptySem).

The counter in semaphore will be equivalent to the number of items in the list.


Cs252 systems programming

A Synchronized List Class

SynchList.cc

SyncList:: SyncList(){ _

_head = NULL;

pthread_mutex_init(

&_mutex, NULL );

sem_init(&_emptySem,0, 0);

}

SyncList ::insert(intval){

ListEntry *e = new ListEntry;e->_val = val;pthread_mutex_lock(&_mutex);

a)e->_next = _head;

b)_head = e;

pthread_mutex_unlock(&_mutex);

sem_post(&_emptySem);

}

SyncList.h

#include <pthread.h>

structListEntry {

int _val;ListEntry *_next;

};

class SyncList{

ListEntry *_head;

pthread_mutex_t _mutex;

sem_t_emptySem;

public:SyncList();void insert(intval);int remove();

};


Cs252 systems programming

A Synchronized List

intSyncList ::remove(){

ListEntry *tmp;

// Wait until list is not empty

sem_wait(&_emptySem);

pthread_mutex_lock(&_mutex); c)tmp= _head;d)head=tmp->_next; pthread_mutex_unlock(&mutex);

intval=tmp->_val;

delete tmp;

return val;

}


Cs252 systems programming

Example: Removing Before Inserting

T1 T2

remove()

sem_wait(&_emptySem)

(count==-1)

wait

insert(5)

pthread_mutex_lock()

a) b)

pthread_mutex_unlock()

sem_post(&_emptySem)

wakeup T1

T1 continues

pthread_mutex_lock()

c) d)

pthread_mutex_unlock()


Cs252 systems programming

Example: Inserting Before Removing

T1 T2

insert(7)

pthread_mutex_lock()

a) b)

pthread_mutex_unlock()

sem_post(&_emptySem)(count==1)

Starts running

remove()

sem_wait(_emptySem);

continue (count==0)

pthread_mutex_lock()

c) d)

pthread_mutex_unlock()


Cs252 systems programming

Notes on Synchronized List Class

We need the mutex lock to enforce atomicity in the critical sections a) b) and c) d).

The semaphore is not enough to enforce atomicity.


Cs252 systems programming

Notes on Synchronized List Class

intMTList::remove(){

ListEntry *tmp;

sem_wait(&_emptySem);

pthread_mutex_lock(&_mutex);

c)tmp = _head;

d)head=tmp->_next;

pthread_mutex_unlock(&mutex);

intval=tmp->_val;

delete tmp;

return val;

}

>N

N

1

(N= items in list)


Cs252 systems programming

Notes on Synchronized List Class

The sem_wait() call has to be done outside the mutex_lock/unlock section.

Otherwise we can get a deadlock.

Example:

pthread_mutex_lock(&_mutex);

sem_wait(&_empty);

c)tmp = _head;

d)head=tmp->_next;

pthread_mutex_unlock(&mutex);


Cs252 systems programming

Notes on Synchronized List Class

T1 T2

remove()

mutex_lock

sem_wait(&_empty)

wait for sem_post

from T2

insert(5)

pthread_mutex_lock()

wait for mutex_unlock in T1

Deadlock!!!!


Cs252 systems programming

Review Questions

How do Recursive Mutex locks differ from regular Mutex locks?

Why one may prefer to use a mutex lock that is not resursive?

How to implement recursive mutex locks (part of Project 4)?

Why one should not use spin locks on a single CPU machone?

When a process with multiple threads call fork(), what threads are created in child process, under pthread semantics?


Cs252 systems programming

Review Questions

What is a race condition? (When given simple codes, should be able to come up with race condition scenario.)

What happens when calling sem_wait(), sem_post? (Should be able to produce pseudo-code.)

Different usage of semaphore when initiating it with different values.

What are the key difference between binary semaphore and mutex lock?

How to implement a synchronized list? Why we need both mutex and semaphore?


  • Login