CS 2200 Presentation 20 Threads & Synchronization

Preview:

Citation preview

CS 2200

Presentation 20

Threads & Synchronization

Question 1• Why are threads becoming increasingly

important?

1. Widespread influence of Java (GUI's)

2. Increasing availability of SMP's

3. Importance of computer gaming market

4. Logical way of abstracting complex task

Question 2• Why use a "while" loop around a

pthread_cond_wait() call?

1. In order to properly spinlock

2. To insure that the wait gets executed

3. To verify that the resource is free

4. SMP cache coherency

Question 3• Why should we minimize lock scope

(minimize the extent of code within a lock/unlock block)?

1. Allow for more concurrency

2. Depends

3. 42

4. Add a bit

Question 4• Do you have any control over thread

scheduling?

1. Yes

2. No

3. Why would I want to?

4. Maybe

Questions?

Our Road Map

Processor

Networking

Parallel Systems

I/O Subsystem

Memory Hierarchy

What’s wrong with Semaphores?void wait(int *s) { /* P */

while (*S <= 0) {

/* spin */

}

*s = *s - 1;

}

void signal(int *s) { /* V */

*s = *s + 1;

}

What’s wrong with Semaphores?void wait(int *s) { /* P */

while (*S <= 0) {

/* spin */

}

*s = *s - 1;

}

void signal(int *s) { /* V */

*s = *s + 1;

}

Solution• Block thread while waiting.

• How?

• Let’s examine a simple threads package...

Typical API• Thread_init

• Thread_create

• Thread_yield

• Thread_exit

• Mutex_create

• Mutex_lock

• Mutex_unlock

• Alarm

Nothing muchhappening inThreadland

Program starts in main• main is going to call thread_init

Thread_init• Create linked list of thread control blocks

– Context– Status

• RUNNING• READY• DONE• BLOCKED

– Pointer to mutex thread is waiting for.

• Start timer (Request from OS) – Which will be handled by "Alarm"

Thread_Init

TCB

Context

RUNNING

mutexPointer

next

I even created a TCB for myself!

But, I'm so lonely...

Thread_create• Make a new TCB

• malloc space for the new threads stack

• Get current context

• Modify context – stack address– stack size– starting address

• Note: Context is in TCB

• Add TCB to linked list (Ready)

Thread_create (3 times)

TCB

Context

READY

mutexPointer

next

TCB

Context

RUNNING

mutexPointer

next TCB

Context

READY

mutexPointer

next

TCB

Context

READY

mutexPointer

next

I think I'llmake me amutex

Mutex_create• malloc new mutex variable (struct)

– status = UNLOCKED– Pointer to thread holding lock

Mutex_create

TCB

Context

READY

mutexPointer

next

TCB

Context

RUNNING

mutexPointer

next TCB

Context

READY

mutexPointer

next

TCB

Context

READY

mutexPointer

next

Mutex

unlock

Holder

I just madea mutex!

Alarm

• Reset alarm• Thread_yield

Thread_yield• If no other threads

– return• Else

– Make current threads status READY (if not DONE or BLOCKED)

– Make “next” thread RUNNING– Context switch current with next

Timer Interrupt (Alarm)

TCB

Context

RUNNING

mutexPointer

next

TCB

Context

READY

mutexPointer

next TCB

Context

READY

mutexPointer

next

TCB

Context

READY

mutexPointer

next

Mutex

unlock

Holder

Mutex_lock• If mutex is already locked

– Mark thread as blocked– Note in TCB mutex being waited on– Thread_yield

• Else– Make mutex locked– Point mutex at thread holding lock

Lock Mutex

TCB

Context

RUNNING

mutexPointer

next

TCB

Context

READY

mutexPointer

next TCB

Context

READY

mutexPointer

next

TCB

Context

READY

mutexPointer

next

Mutex

lock

Holder

I have the mutex!

Timer Interrupt (Alarm)

TCB

Context

READY

mutexPointer

next

TCB

Context

READY

mutexPointer

next TCB

Context

RUNNING

mutexPointer

next

TCB

Context

READY

mutexPointer

next

Mutex

lock

Holder

Mutex_lock• If mutex is already locked

– Mark thread as blocked– Note in TCB mutex being waited on– Thread_yield

• Else– Make mutex locked– Point mutex at thread holding lock

Try to Lock Mutex (Fails)

TCB

Context

READY

mutexPointer

next

TCB

Context

READY

mutexPointer

next TCB

Context

BLOCKED

mutexPointer

next

TCB

Context

RUNNING

mutexPointer

next

Mutex

lock

Holder

• If mutex is already locked– Mark thread as blocked

– Note in TCB mutex being waited on

– Thread_yield

• Else– Make mutex locked

– Point mutex at thread holding lock

I want themutex!

Timer Interrupt (Alarm)

TCB

Context

READY

mutexPointer

next

TCB

Context

RUNNING

mutexPointer

next TCB

Context

BLOCKED

mutexPointer

next

TCB

Context

READY

mutexPointer

next

Mutex

lock

Holder

Timer Interrupt (Alarm)

TCB

Context

RUNNING

mutexPointer

next

TCB

Context

READY

mutexPointer

next TCB

Context

BLOCKED

mutexPointer

next

TCB

Context

READY

mutexPointer

next

Mutex

lock

Holder

Timer Interrupt (Alarm)

TCB

Context

READY

mutexPointer

next

TCB

Context

READY

mutexPointer

next TCB

Context

BLOCKED

mutexPointer

next

TCB

Context

RUNNING

mutexPointer

next

Mutex

lock

Holder

Note: The yield routinecan check each blockedthread to make sure mutex is still locked

Stillwaiting!

Timer Interrupt (Alarm)

TCB

Context

READY

mutexPointer

next

TCB

Context

RUNNING

mutexPointer

next TCB

Context

BLOCKED

mutexPointer

next

TCB

Context

READY

mutexPointer

next

Mutex

lock

Holder

Timer Interrupt (Alarm)

TCB

Context

RUNNING

mutexPointer

next

TCB

Context

READY

mutexPointer

next TCB

Context

BLOCKED

mutexPointer

next

TCB

Context

READY

mutexPointer

next

Mutex

lock

Holder

Guess I don'tneed the

mutex anymore

Mutex_unlock• Check to see if thread trying to unlock is

the holder of the mutex

• Set mutex status to unlock

Unlocks Mutex

TCB

Context

RUNNING

mutexPointer

next

TCB

Context

READY

mutexPointer

next TCB

Context

BLOCKED

mutexPointer

next

TCB

Context

READY

mutexPointer

next

Mutex

unlock

Holder

Timer Interrupt (Alarm)

TCB

Context

READY

mutexPointer

next

TCB

Context

READY

mutexPointer

next TCB

Context

BLOCKED

mutexPointer

next

TCB

Context

READY

mutexPointer

next

Mutex

unlock

Holder

Since mutex is unlocked we canchange status to RUNNING

Same Timer Interrupt (Alarm)

TCB

Context

READY

mutexPointer

next

TCB

Context

READY

mutexPointer

next TCB

Context

RUNNING

mutexPointer

next

TCB

Context

READY

mutexPointer

next

Mutex

unlock

Holder

Since mutex is unlocked we canchange status to RUNNING

Same Timer Interrupt (Alarm)

TCB

Context

READY

mutexPointer

next

TCB

Context

READY

mutexPointer

next TCB

Context

RUNNING

mutexPointer

next

TCB

Context

READY

mutexPointer

next

Mutex

lock

Holder

And give the mutexto the requestor

Timer Interrupt (Alarm)

TCB

Context

READY

mutexPointer

next

TCB

Context

READY

mutexPointer

next TCB

Context

READY

mutexPointer

next

TCB

Context

RUNNING

mutexPointer

next

Mutex

unlock

Holder

I'm done!

Thread_exit• Make status DONE

• Thread_yield

Thread Exits

TCB

Context

READY

mutexPointer

next

TCB

Context

RUNNING

mutexPointer

next TCB

Context

READY

mutexPointer

next

TCB

Context

DONE

mutexPointer

next

Mutex

unlock

Holder

etc.

Question 51. Those slides made things much clearer

2. Those slides made things clearer

3. Those slides took up some time

4. Those slides confused me

5. Those slides baffled me

Questions?

Considerlock(m)

while(resource == busy)

wait(c, m)

resource = busy

unlock(m)

/* doing work... */

lock(m)

resource = available

unlock(m)

signal(c)

Demo

Sample pthreads program

1000

10

sortersortersortersortersortersortersorter

7

Scoreboard

producer

Operation• mainmain starts up operation• producerproducer thread

– Locks scoreboard– Waits for row to be available– Marks row as in use– Unlocks scoreboard – Fills buffer row with random ints– Locks scoreboard– Marks row as sortable– Unlocks scoreboard

AVAILABLE 0#define FILLING 1#define SORTABLE 2#define SORTING 3

Scoreboard

Row Status

0 AVAILABLE

1 AVAILABLE

2 AVAILABLE

3 AVAILABLE

4 AVAILABLE

5 AVAILABLE

... ...

Operation• mainmain starts up operation• producerproducer thread

– Locks scoreboard– Waits for row to be available– Marks row as in use– Unlocks scoreboard – Fills buffer row with random ints– Locks scoreboard– Marks row as sortable– Unlocks scoreboard

AVAILABLE 0#define FILLING 1#define SORTABLE 2#define SORTING 3

Scoreboard

Row Status

0 FILLING

1 AVAILABLE

2 AVAILABLE

3 AVAILABLE

4 AVAILABLE

5 AVAILABLE

... ...

Operation• mainmain starts up operation• producerproducer thread

– Locks scoreboard– Waits for row to be available– Marks row as in use– Unlocks scoreboard – Fills buffer row with random ints– Locks scoreboard– Marks row as sortable– Unlocks scoreboard

AVAILABLE 0#define FILLING 1#define SORTABLE 2#define SORTING 3

Scoreboard

Row Status

0 SORTABLE

1 AVAILABLE

2 AVAILABLE

3 AVAILABLE

4 AVAILABLE

5 AVAILABLE

... ...

Operation• sortersorter threads

– Lock scoreboard– Wait for row to be sortable– Mark row as sorting– Unlock scoreboard– Sort row (using bubblesort)– Lock scoreboard– Mark row as available

Scoreboard

Row Status

0 SORTABLE

1 AVAILABLE

2 AVAILABLE

3 AVAILABLE

4 AVAILABLE

5 AVAILABLE

... ...

Operation• sortersorter threads

– Lock scoreboard– Wait for row to be sortable– Mark row as sorting– Unlock scoreboard– Sort row (using bubblesort)– Lock scoreboard– Mark row as available

Scoreboard

Row Status

0 SORTING

1 AVAILABLE

2 AVAILABLE

3 AVAILABLE

4 AVAILABLE

5 AVAILABLE

... ...

Operation• sortersorter threads

– Lock scoreboard– Wait for row to be sortable– Mark row as sorting– Unlock scoreboard– Sort row (using bubblesort)– Lock scoreboard– Mark row as available

• Runnable as user level threads or kernel level (lwp/thread). [SOLARIS ONLY]

Scoreboard

Row Status

0 AVAILABLE

1 AVAILABLE

2 AVAILABLE

3 AVAILABLE

4 AVAILABLE

5 AVAILABLE

... ...

/* td -- Thread Demo */

#include <pthread.h>

#include <stdio.h>

#include <stdlib.h>

#define _REENTRANT

#define SIZE 1000 /* Size of sort buffers */

#define SCOUNT 7 /* Number of sorters */

#define ROWS 10 /* Number of buffers */

#define STEPS 22 /* Buffers full of data to

sort */

#define NONEFOUND -1 /* Used in searching */

/* Allowable states for row buffers (in scoreboard) */

enum {AVAILABLE, FILLING, SORTABLE, SORTING};

enum {NO, YES};

static int data[ROWS][SIZE]; /* The buffers */

static int available; /* Num of buffers

available to fill */

static int sortable; /* How many are

avail to sort */

static int scoreboard[ROWS]; /* Row access

scoreboard */

static int run; /* Flag used to

shutdown

gracefully */

/* Scoreboard mutex lock */

static pthread_mutex_t scorelock;

/* The producer can work! */

static pthread_cond_t pcanwork;

/* A sorter can work! */

static pthread_cond_t sorterworkavail;

/* Function prototypes */

static void *producer();

static void *sorter();

void sort(int);

Creatingnecessarymutex andconditionvariables

int main(int argc, char* argv[])

{

pthread_t producer_id;

pthread_t sorter_id[SCOUNT];

pthread_attr_t attr;

int i;

available = ROWS;

sortable = 0;

run = YES;

Threads haveid numbers!

pthread_attr_init(&attr);

/* This binds thread to lwp allowing kernel to

schedule thread (Will allow threads to run on

different cpu's) */

pthread_attr_setscope(&attr,PTHREAD_SCOPE_SYSTEM);

pthread_mutex_init(&scorelock, NULL);

pthread_cond_init(&pcanwork, NULL);

pthread_cond_init(&sorterworkavail, NULL);

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

scoreboard[i] = AVAILABLE;

}

if(argc == 1) /* No concurrency */

pthread_create

(&producer_id, NULL, producer, NULL);

else

pthread_create

(&producer_id, &attr, producer, NULL);

This trick onlyworks on Solaris

for(i = 0; i < SCOUNT; i++)

{

if(argc == 1)

pthread_create

(&sorter_id[i], NULL, sorter, NULL);

else

pthread_create

(&sorter_id[i], &attr, sorter, NULL);

}

printf("main> All threads running\n");

pthread_join(producer_id, NULL);

/* After the producer is finished we send signals

to all sorters to wake up and see that they

should quit */

for(i = 0; i < SCOUNT; i++)

{

pthread_cond_signal(&sorterworkavail);

}

for(i = 0; i < SCOUNT; i++)

{

pthread_join(sorter_id[i], NULL);

}

printf("Normal Termination\n");

return 0;

}

static void *producer() {

int pcount;

int target;

int i;

for(pcount = 0; pcount < STEPS; pcount++) {

pthread_mutex_lock(&scorelock);

while(available == 0) {

pthread_cond_wait(&pcanwork, &scorelock);

}

target = NONEFOUND;

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

if(scoreboard[i] == AVAILABLE) {

target = i;

available = available - 1;

break;

}

}

This is the loop which controls the total number of rows we process

pthread_mutex_unlock(&scorelock);

if(target == NONEFOUND) {

printf("*** Producer cannot find"

" available row!\n");

pthread_exit(NULL);

}

printf("p> Filling row %d\n", target);

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

data[target][i] = rand();

}

printf("p> Row %d complete\n", target);

pthread_mutex_lock(&scorelock);

scoreboard[target] = SORTABLE;

sortable = sortable + 1;

pthread_mutex_unlock(&scorelock);

pthread_cond_signal(&sorterworkavail);

}

run = NO;

return NULL;

/* pthread_exit(NULL); */

}

This means that we can quitonce we finish all sorting

static void *sorter() {

int i;

int target;

pthread_t me;

me = pthread_self();

while(1) {

pthread_mutex_lock(&scorelock);

while(sortable == 0 && run == YES) {

pthread_cond_wait

(&sorterworkavail, &scorelock);

}

/* If the producer says stop and there is no

work...exit */

if(run == NO && available == ROWS) {

printf(" S> %x Exiting..."

"prod done & no filled rows\n", me);

pthread_mutex_unlock(&scorelock);

pthread_exit(NULL);

}

target = NONEFOUND;

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

if(scoreboard[i] == SORTABLE) {

target = i;

sortable = sortable - 1;

scoreboard[target] = SORTING;

break;

}

}

if(target == NONEFOUND) {

/* We get here if the producer is finished

and some threads are being sorted but

none are available for sorting */

printf("S> %x couldn't find thread to "

"sort.\n", me);

pthread_mutex_unlock(&scorelock);

pthread_exit(NULL);

}

pthread_mutex_unlock(&scorelock);

printf("S> %x starting...\n", me);

sort(target);

printf("S> %x finishing min = %d max = %d\n",

me, data[target][0],

data[target][SIZE-1]);

pthread_mutex_lock(&scorelock);

scoreboard[target] = AVAILABLE;

available = available + 1;

pthread_mutex_unlock(&scorelock);

pthread_cond_signal(&pcanwork);

}

}

void sort(int target) {

int outer;

int inner;

int temp;

outer = SIZE - 1;

for(outer = SIZE - 1; outer > 0; outer--) {

for(inner=0; inner < outer; inner++) {

if(data[target][inner] >

data[target][inner+1]) {

temp = data[target][inner];

data[target][inner] =

data[target][inner+1];

data[target][inner+1] = temp;

}

}

}

}

Questions?

System Calls getcontext(2)

NAME getcontext, setcontext - get and set current user context

SYNOPSIS #include

int getcontext(ucontext_t *ucp);

int setcontext(const ucontext_t *ucp);

DESCRIPTION The getcontext() function initializes the structure pointed to by ucp to the current user context of the calling pro- cess. The ucontext_t type that ucp points to defines the user context and includes the contents of the calling pro- cess' machine registers, the signal mask, and the current execution stack.

The setcontext() function restores the user context pointed to by ucp. A successful call to setcontext() does not return; program execution resumes at the point specified by the ucp argument passed to setcontext(). The ucp argument should be created either by a prior call to getcontext(), or by being passed as an argument to a signal handler. If the ucp argument was created with getcontext(), program execu- tion continues as if the corresponding call of getcontext() had just returned. If the ucp argument was created with makecontext(3C), program execution continues with the func- tion passed to makecontext(3C). When that function returns, the process continues as if after a call to setcontext() with the ucp argument that was input to makecontext(3C). If the ucp argument was passed to a signal handler, program execution continues with the program instruction following

the instruction interrupted by the signal. If the uc_link member of the ucontext_t structure pointed to by the ucp argument is equal to 0, then this context is the main con- text, and the process will exit when this context returns. The effects of passing a ucp argument obtained from any other source are unspecified.

RETURN VALUES On successful completion, setcontext() does not return and getcontext() returns 0. Otherwise, -1 is returned.

ERRORS No errors are defined.

USAGE When a signal handler is executed, the current user context is saved and a new context is created. If the process leaves the signal handler via longjmp(3B), then it is unspecified whether the context at the time of the

SunOS 5.7 Last change: 8 Oct 1996 1

System Calls getcontext(2)

corresponding setjmp(3B) call is restored and thus whether future calls to getcontext() will provide an accurate representation of the current context, since the context restored by longjmp(3B) may not contain all the information that setcontext() requires. Signal handlers should use siglongjmp(3C) or setcontext() instead.

Portable applications should not modify or access the uc_mcontext member of ucontext_t. A portable application cannot assume that context includes any process-wide static

data, possibly including errno. Users manipulating contexts should take care to handle these explicitly when required.

SEE ALSO sigaction(2), sigaltstack(2), sigprocmask(2), bsd_signal(3C), makecontext(3C), setjmp(3B), sigsetjmp(3C), ucontext(5)

Recommended