Upload
abigayle-dixon
View
240
Download
2
Embed Size (px)
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)