reorganize
This commit is contained in:
198
src/ch03/alarm_cond.c
Normal file
198
src/ch03/alarm_cond.c
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* alarm_cond.c
|
||||
*
|
||||
* This is an enhancement to the alarm_mutex.c program, which
|
||||
* used only a mutex to synchronize access to the shared alarm
|
||||
* list. This version adds a condition variable. The alarm
|
||||
* thread waits on this condition variable, with a timeout that
|
||||
* corresponds to the earliest timer request. If the main thread
|
||||
* enters an earlier timeout, it signals the condition variable
|
||||
* so that the alarm thread will wake up and process the earlier
|
||||
* timeout first, requeueing the later request.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include "errors.h"
|
||||
|
||||
/*
|
||||
* The "alarm" structure now contains the time_t (time since the
|
||||
* Epoch, in seconds) for each alarm, so that they can be
|
||||
* sorted. Storing the requested number of seconds would not be
|
||||
* enough, since the "alarm thread" cannot tell how long it has
|
||||
* been on the list.
|
||||
*/
|
||||
typedef struct alarm_tag {
|
||||
struct alarm_tag *link;
|
||||
int seconds;
|
||||
time_t time; /* seconds from EPOCH */
|
||||
char message[64];
|
||||
} alarm_t;
|
||||
|
||||
pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_cond_t alarm_cond = PTHREAD_COND_INITIALIZER;
|
||||
alarm_t *alarm_list = NULL;
|
||||
time_t current_alarm = 0;
|
||||
|
||||
/*
|
||||
* Insert alarm entry on list, in order.
|
||||
*/
|
||||
void alarm_insert (alarm_t *alarm)
|
||||
{
|
||||
int status;
|
||||
alarm_t **last, *next;
|
||||
|
||||
/*
|
||||
* LOCKING PROTOCOL:
|
||||
*
|
||||
* This routine requires that the caller have locked the
|
||||
* alarm_mutex!
|
||||
*/
|
||||
last = &alarm_list;
|
||||
next = *last;
|
||||
while (next != NULL) {
|
||||
if (next->time >= alarm->time) {
|
||||
alarm->link = next;
|
||||
*last = alarm;
|
||||
break;
|
||||
}
|
||||
last = &next->link;
|
||||
next = next->link;
|
||||
}
|
||||
/*
|
||||
* If we reached the end of the list, insert the new alarm
|
||||
* there. ("next" is NULL, and "last" points to the link
|
||||
* field of the last item, or to the list header.)
|
||||
*/
|
||||
if (next == NULL) {
|
||||
*last = alarm;
|
||||
alarm->link = NULL;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf ("[list: ");
|
||||
for (next = alarm_list; next != NULL; next = next->link)
|
||||
printf ("%d(%d)[\"%s\"] ", next->time,
|
||||
next->time - time (NULL), next->message);
|
||||
printf ("]\n");
|
||||
#endif
|
||||
/*
|
||||
* Wake the alarm thread if it is not busy (that is, if
|
||||
* current_alarm is 0, signifying that it's waiting for
|
||||
* work), or if the new alarm comes before the one on
|
||||
* which the alarm thread is waiting.
|
||||
*/
|
||||
if (current_alarm == 0 || alarm->time < current_alarm) {
|
||||
current_alarm = alarm->time;
|
||||
status = pthread_cond_signal (&alarm_cond);
|
||||
if (status != 0)
|
||||
err_abort (status, "Signal cond");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The alarm thread's start routine.
|
||||
*/
|
||||
void *alarm_thread (void *arg)
|
||||
{
|
||||
alarm_t *alarm;
|
||||
struct timespec cond_time;
|
||||
time_t now;
|
||||
int status, expired;
|
||||
|
||||
/*
|
||||
* Loop forever, processing commands. The alarm thread will
|
||||
* be disintegrated when the process exits. Lock the mutex
|
||||
* at the start -- it will be unlocked during condition
|
||||
* waits, so the main thread can insert alarms.
|
||||
*/
|
||||
status = pthread_mutex_lock (&alarm_mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Lock mutex");
|
||||
while (1) {
|
||||
/*
|
||||
* If the alarm list is empty, wait until an alarm is
|
||||
* added. Setting current_alarm to 0 informs the insert
|
||||
* routine that the thread is not busy.
|
||||
*/
|
||||
current_alarm = 0;
|
||||
while (alarm_list == NULL) {
|
||||
status = pthread_cond_wait (&alarm_cond, &alarm_mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Wait on cond");
|
||||
}
|
||||
alarm = alarm_list;
|
||||
alarm_list = alarm->link;
|
||||
now = time (NULL);
|
||||
expired = 0;
|
||||
if (alarm->time > now) {
|
||||
#ifdef DEBUG
|
||||
printf ("[waiting: %d(%d)\"%s\"]\n", alarm->time,
|
||||
alarm->time - time (NULL), alarm->message);
|
||||
#endif
|
||||
cond_time.tv_sec = alarm->time;
|
||||
cond_time.tv_nsec = 0;
|
||||
current_alarm = alarm->time;
|
||||
while (current_alarm == alarm->time) {
|
||||
status = pthread_cond_timedwait (
|
||||
&alarm_cond, &alarm_mutex, &cond_time);
|
||||
if (status == ETIMEDOUT) {
|
||||
expired = 1;
|
||||
break;
|
||||
}
|
||||
if (status != 0)
|
||||
err_abort (status, "Cond timedwait");
|
||||
}
|
||||
if (!expired)
|
||||
alarm_insert (alarm);
|
||||
} else
|
||||
expired = 1;
|
||||
if (expired) {
|
||||
printf ("(%d) %s\n", alarm->seconds, alarm->message);
|
||||
free (alarm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
int status;
|
||||
char line[128];
|
||||
alarm_t *alarm;
|
||||
pthread_t thread;
|
||||
|
||||
status = pthread_create (
|
||||
&thread, NULL, alarm_thread, NULL);
|
||||
if (status != 0)
|
||||
err_abort (status, "Create alarm thread");
|
||||
while (1) {
|
||||
printf ("Alarm> ");
|
||||
if (fgets (line, sizeof (line), stdin) == NULL) exit (0);
|
||||
if (strlen (line) <= 1) continue;
|
||||
alarm = (alarm_t*)malloc (sizeof (alarm_t));
|
||||
if (alarm == NULL)
|
||||
errno_abort ("Allocate alarm");
|
||||
|
||||
/*
|
||||
* Parse input line into seconds (%d) and a message
|
||||
* (%64[^\n]), consisting of up to 64 characters
|
||||
* separated from the seconds by whitespace.
|
||||
*/
|
||||
if (sscanf (line, "%d %64[^\n]",
|
||||
&alarm->seconds, alarm->message) < 2) {
|
||||
fprintf (stderr, "Bad command\n");
|
||||
free (alarm);
|
||||
} else {
|
||||
status = pthread_mutex_lock (&alarm_mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Lock mutex");
|
||||
alarm->time = time (NULL) + alarm->seconds;
|
||||
/*
|
||||
* Insert the new alarm into the list of alarms,
|
||||
* sorted by expiration time.
|
||||
*/
|
||||
alarm_insert (alarm);
|
||||
status = pthread_mutex_unlock (&alarm_mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Unlock mutex");
|
||||
}
|
||||
}
|
||||
}
|
||||
175
src/ch03/alarm_mutex.c
Normal file
175
src/ch03/alarm_mutex.c
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* alarm_mutex.c
|
||||
*
|
||||
* This is an enhancement to the alarm_thread.c program, which
|
||||
* created an "alarm thread" for each alarm command. This new
|
||||
* version uses a single alarm thread, which reads the next
|
||||
* entry in a list. The main thread places new requests onto the
|
||||
* list, in order of absolute expiration time. The list is
|
||||
* protected by a mutex, and the alarm thread sleeps for at
|
||||
* least 1 second, each iteration, to ensure that the main
|
||||
* thread can lock the mutex to add new work to the list.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include "errors.h"
|
||||
|
||||
/*
|
||||
* The "alarm" structure now contains the time_t (time since the
|
||||
* Epoch, in seconds) for each alarm, so that they can be
|
||||
* sorted. Storing the requested number of seconds would not be
|
||||
* enough, since the "alarm thread" cannot tell how long it has
|
||||
* been on the list.
|
||||
*/
|
||||
typedef struct alarm_tag {
|
||||
struct alarm_tag *link;
|
||||
int seconds;
|
||||
time_t time; /* seconds from EPOCH */
|
||||
char message[64];
|
||||
} alarm_t;
|
||||
|
||||
pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
alarm_t *alarm_list = NULL;
|
||||
|
||||
/*
|
||||
* The alarm thread's start routine.
|
||||
*/
|
||||
void *alarm_thread (void *arg)
|
||||
{
|
||||
alarm_t *alarm;
|
||||
int sleep_time;
|
||||
time_t now;
|
||||
int status;
|
||||
|
||||
/*
|
||||
* Loop forever, processing commands. The alarm thread will
|
||||
* be disintegrated when the process exits.
|
||||
*/
|
||||
while (1) {
|
||||
status = pthread_mutex_lock (&alarm_mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Lock mutex");
|
||||
alarm = alarm_list;
|
||||
|
||||
/*
|
||||
* If the alarm list is empty, wait for one second. This
|
||||
* allows the main thread to run, and read another
|
||||
* command. If the list is not empty, remove the first
|
||||
* item. Compute the number of seconds to wait -- if the
|
||||
* result is less than 0 (the time has passed), then set
|
||||
* the sleep_time to 0.
|
||||
*/
|
||||
if (alarm == NULL)
|
||||
sleep_time = 1;
|
||||
else {
|
||||
alarm_list = alarm->link;
|
||||
now = time (NULL);
|
||||
if (alarm->time <= now)
|
||||
sleep_time = 0;
|
||||
else
|
||||
sleep_time = alarm->time - now;
|
||||
#ifdef DEBUG
|
||||
printf ("[waiting: %d(%d)\"%s\"]\n", alarm->time,
|
||||
sleep_time, alarm->message);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlock the mutex before waiting, so that the main
|
||||
* thread can lock it to insert a new alarm request. If
|
||||
* the sleep_time is 0, then call sched_yield, giving
|
||||
* the main thread a chance to run if it has been
|
||||
* readied by user input, without delaying the message
|
||||
* if there's no input.
|
||||
*/
|
||||
status = pthread_mutex_unlock (&alarm_mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Unlock mutex");
|
||||
if (sleep_time > 0)
|
||||
sleep (sleep_time);
|
||||
else
|
||||
sched_yield ();
|
||||
|
||||
/*
|
||||
* If a timer expired, print the message and free the
|
||||
* structure.
|
||||
*/
|
||||
if (alarm != NULL) {
|
||||
printf ("(%d) %s\n", alarm->seconds, alarm->message);
|
||||
free (alarm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
int status;
|
||||
char line[128];
|
||||
alarm_t *alarm, **last, *next;
|
||||
pthread_t thread;
|
||||
|
||||
status = pthread_create (
|
||||
&thread, NULL, alarm_thread, NULL);
|
||||
if (status != 0)
|
||||
err_abort (status, "Create alarm thread");
|
||||
while (1) {
|
||||
printf ("alarm> ");
|
||||
if (fgets (line, sizeof (line), stdin) == NULL) exit (0);
|
||||
if (strlen (line) <= 1) continue;
|
||||
alarm = (alarm_t*)malloc (sizeof (alarm_t));
|
||||
if (alarm == NULL)
|
||||
errno_abort ("Allocate alarm");
|
||||
|
||||
/*
|
||||
* Parse input line into seconds (%d) and a message
|
||||
* (%64[^\n]), consisting of up to 64 characters
|
||||
* separated from the seconds by whitespace.
|
||||
*/
|
||||
if (sscanf (line, "%d %64[^\n]",
|
||||
&alarm->seconds, alarm->message) < 2) {
|
||||
fprintf (stderr, "Bad command\n");
|
||||
free (alarm);
|
||||
} else {
|
||||
status = pthread_mutex_lock (&alarm_mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Lock mutex");
|
||||
alarm->time = time (NULL) + alarm->seconds;
|
||||
|
||||
/*
|
||||
* Insert the new alarm into the list of alarms,
|
||||
* sorted by expiration time.
|
||||
*/
|
||||
last = &alarm_list;
|
||||
next = *last;
|
||||
while (next != NULL) {
|
||||
if (next->time >= alarm->time) {
|
||||
alarm->link = next;
|
||||
*last = alarm;
|
||||
break;
|
||||
}
|
||||
last = &next->link;
|
||||
next = next->link;
|
||||
}
|
||||
/*
|
||||
* If we reached the end of the list, insert the new
|
||||
* alarm there. ("next" is NULL, and "last" points
|
||||
* to the link field of the last item, or to the
|
||||
* list header).
|
||||
*/
|
||||
if (next == NULL) {
|
||||
*last = alarm;
|
||||
alarm->link = NULL;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf ("[list: ");
|
||||
for (next = alarm_list; next != NULL; next = next->link)
|
||||
printf ("%d(%d)[\"%s\"] ", next->time,
|
||||
next->time - time (NULL), next->message);
|
||||
printf ("]\n");
|
||||
#endif
|
||||
status = pthread_mutex_unlock (&alarm_mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Unlock mutex");
|
||||
}
|
||||
}
|
||||
}
|
||||
127
src/ch03/alarm_mutex_cpp.cpp
Normal file
127
src/ch03/alarm_mutex_cpp.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* alarm_mutex.c
|
||||
*
|
||||
* This is an enhancement to the alarm_thread.c program, which
|
||||
* created an "alarm thread" for each alarm command. This new
|
||||
* version uses a single alarm thread, which reads the next
|
||||
* entry in a list. The main thread places new requests onto the
|
||||
* list, in order of absolute expiration time. The list is
|
||||
* protected by a mutex, and the alarm thread sleeps for at
|
||||
* least 1 second, each iteration, to ensure that the main
|
||||
* thread can lock the mutex to add new work to the list.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include "errors.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
|
||||
|
||||
/*
|
||||
* The "alarm" structure now contains the time_t (time since the
|
||||
* Epoch, in seconds) for each alarm, so that they can be
|
||||
* sorted. Storing the requested number of seconds would not be
|
||||
* enough, since the "alarm thread" cannot tell how long it has
|
||||
* been on the list.
|
||||
*/
|
||||
struct alarm_tag {
|
||||
int seconds;
|
||||
time_t time; /* seconds from EPOCH */
|
||||
char message[64];
|
||||
};
|
||||
|
||||
using alarm_t = alarm_tag;
|
||||
|
||||
pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
std::list<alarm_t> alarm_list;
|
||||
|
||||
/*
|
||||
* The alarm thread's start routine.
|
||||
*/
|
||||
void *alarm_thread(void *arg) {
|
||||
int sleep_time;
|
||||
time_t now;
|
||||
int status;
|
||||
|
||||
/*
|
||||
* Loop forever, processing commands. The alarm thread will
|
||||
* be disintegrated when the process exits.
|
||||
*/
|
||||
while (1) {
|
||||
status = pthread_mutex_lock(&alarm_mutex);
|
||||
if (status != 0) err_abort(status, "Lock mutex");
|
||||
|
||||
now = time(NULL);
|
||||
sleep_time = 0;
|
||||
|
||||
for (auto it = alarm_list.begin(); it != alarm_list.end();) {
|
||||
if (it->time <= now) {
|
||||
printf("(%d) %s\n", it->seconds, it->message);
|
||||
it = alarm_list.erase(it);
|
||||
} else {
|
||||
sleep_time = MIN(it->time - now, sleep_time);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlock the mutex before waiting, so that the main
|
||||
* thread can lock it to insert a new alarm request. If
|
||||
* the sleep_time is 0, then call sched_yield, giving
|
||||
* the main thread a chance to run if it has been
|
||||
* readied by user input, without delaying the message
|
||||
* if there's no input.
|
||||
*/
|
||||
status = pthread_mutex_unlock(&alarm_mutex);
|
||||
if (status != 0) err_abort(status, "Unlock mutex");
|
||||
if (sleep_time > 0)
|
||||
sleep(sleep_time);
|
||||
else
|
||||
sched_yield();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int status;
|
||||
char line[128];
|
||||
pthread_t thread;
|
||||
|
||||
status = pthread_create(&thread, NULL, alarm_thread, NULL);
|
||||
if (status != 0) err_abort(status, "Create alarm thread");
|
||||
while (1) {
|
||||
printf("alarm> ");
|
||||
if (fgets(line, sizeof(line), stdin) == NULL) exit(0);
|
||||
if (strlen(line) <= 1) continue;
|
||||
alarm_t alarm;
|
||||
|
||||
/*
|
||||
* Parse input line into seconds (%d) and a message
|
||||
* (%64[^\n]), consisting of up to 64 characters
|
||||
* separated from the seconds by whitespace.
|
||||
*/
|
||||
if (sscanf(line, "%d %64[^\n]", &alarm.seconds, alarm.message) < 2) {
|
||||
fprintf(stderr, "Bad command\n");
|
||||
} else {
|
||||
alarm.time = time(NULL) + alarm.seconds;
|
||||
|
||||
status = pthread_mutex_lock(&alarm_mutex);
|
||||
if (status != 0) err_abort(status, "Lock mutex");
|
||||
|
||||
/*
|
||||
* Insert the new alarm into the list of alarms.
|
||||
*/
|
||||
alarm_list.push_back(alarm);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[list: ");
|
||||
for (const auto &iAlarm : alarm_list)
|
||||
printf("%d(%d)[\"%s\"] ", iAlarm.time, iAlarm.time - time(NULL),
|
||||
iAlarm.message);
|
||||
printf("]\n");
|
||||
#endif
|
||||
status = pthread_mutex_unlock(&alarm_mutex);
|
||||
if (status != 0) err_abort(status, "Unlock mutex");
|
||||
}
|
||||
}
|
||||
}
|
||||
199
src/ch03/backoff.c
Normal file
199
src/ch03/backoff.c
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* backoff.c
|
||||
*
|
||||
* Demonstrate deadlock avoidance using "mutex backoff".
|
||||
*
|
||||
* Special notes: On a Solaris 2.5 uniprocessor, this test will
|
||||
* not produce interleaved output unless extra LWPs are created
|
||||
* by calling thr_setconcurrency(), because threads are not
|
||||
* timesliced.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include "errors.h"
|
||||
|
||||
#define ITERATIONS 10
|
||||
|
||||
/*
|
||||
* Initialize a static array of 3 mutexes.
|
||||
*/
|
||||
pthread_mutex_t mutex[3] = {
|
||||
PTHREAD_MUTEX_INITIALIZER,
|
||||
PTHREAD_MUTEX_INITIALIZER,
|
||||
PTHREAD_MUTEX_INITIALIZER
|
||||
};
|
||||
|
||||
int backoff = 1; /* Whether to backoff or deadlock */
|
||||
int yield_flag = 0; /* 0: no yield, >0: yield, <0: sleep */
|
||||
|
||||
/*
|
||||
* This is a thread start routine that locks all mutexes in
|
||||
* order, to ensure a conflict with lock_reverse, which does the
|
||||
* opposite.
|
||||
*/
|
||||
void *lock_forward (void *arg)
|
||||
{
|
||||
int i, iterate, backoffs;
|
||||
int status;
|
||||
|
||||
for (iterate = 0; iterate < ITERATIONS; iterate++) {
|
||||
backoffs = 0;
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (i == 0) {
|
||||
status = pthread_mutex_lock (&mutex[i]);
|
||||
if (status != 0)
|
||||
err_abort (status, "First lock");
|
||||
} else {
|
||||
if (backoff)
|
||||
status = pthread_mutex_trylock (&mutex[i]);
|
||||
else
|
||||
status = pthread_mutex_lock (&mutex[i]);
|
||||
if (status == EBUSY) {
|
||||
backoffs++;
|
||||
DPRINTF ((
|
||||
" [forward locker backing off at %d]\n",
|
||||
i));
|
||||
for (; i >= 0; i--) {
|
||||
status = pthread_mutex_unlock (&mutex[i]);
|
||||
if (status != 0)
|
||||
err_abort (status, "Backoff");
|
||||
}
|
||||
} else {
|
||||
if (status != 0)
|
||||
err_abort (status, "Lock mutex");
|
||||
DPRINTF ((" forward locker got %d\n", i));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Yield processor, if needed to be sure locks get
|
||||
* interleaved on a uniprocessor.
|
||||
*/
|
||||
if (yield_flag) {
|
||||
if (yield_flag > 0)
|
||||
sched_yield ();
|
||||
else
|
||||
sleep (1);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Report that we got 'em, and unlock to try again.
|
||||
*/
|
||||
printf (
|
||||
"lock forward got all locks, %d backoffs\n", backoffs);
|
||||
pthread_mutex_unlock (&mutex[2]);
|
||||
pthread_mutex_unlock (&mutex[1]);
|
||||
pthread_mutex_unlock (&mutex[0]);
|
||||
sched_yield ();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a thread start routine that locks all mutexes in
|
||||
* reverse order, to ensure a conflict with lock_forward, which
|
||||
* does the opposite.
|
||||
*/
|
||||
void *lock_backward (void *arg)
|
||||
{
|
||||
int i, iterate, backoffs;
|
||||
int status;
|
||||
|
||||
for (iterate = 0; iterate < ITERATIONS; iterate++) {
|
||||
backoffs = 0;
|
||||
for (i = 2; i >= 0; i--) {
|
||||
if (i == 2) {
|
||||
status = pthread_mutex_lock (&mutex[i]);
|
||||
if (status != 0)
|
||||
err_abort (status, "First lock");
|
||||
} else {
|
||||
if (backoff)
|
||||
status = pthread_mutex_trylock (&mutex[i]);
|
||||
else
|
||||
status = pthread_mutex_lock (&mutex[i]);
|
||||
if (status == EBUSY) {
|
||||
backoffs++;
|
||||
DPRINTF ((
|
||||
" [backward locker backing off at %d]\n",
|
||||
i));
|
||||
for (; i < 3; i++) {
|
||||
status = pthread_mutex_unlock (&mutex[i]);
|
||||
if (status != 0)
|
||||
err_abort (status, "Backoff");
|
||||
}
|
||||
} else {
|
||||
if (status != 0)
|
||||
err_abort (status, "Lock mutex");
|
||||
DPRINTF ((" backward locker got %d\n", i));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Yield processor, if needed to be sure locks get
|
||||
* interleaved on a uniprocessor.
|
||||
*/
|
||||
if (yield_flag) {
|
||||
if (yield_flag > 0)
|
||||
sched_yield ();
|
||||
else
|
||||
sleep (1);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Report that we got 'em, and unlock to try again.
|
||||
*/
|
||||
printf (
|
||||
"lock backward got all locks, %d backoffs\n", backoffs);
|
||||
pthread_mutex_unlock (&mutex[0]);
|
||||
pthread_mutex_unlock (&mutex[1]);
|
||||
pthread_mutex_unlock (&mutex[2]);
|
||||
sched_yield ();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
pthread_t forward, backward;
|
||||
int status;
|
||||
|
||||
#ifdef sun
|
||||
/*
|
||||
* On Solaris 2.5, threads are not timesliced. To ensure
|
||||
* that our threads can run concurrently, we need to
|
||||
* increase the concurrency level.
|
||||
*/
|
||||
DPRINTF (("Setting concurrency level to 2\n"));
|
||||
thr_setconcurrency (2);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If the first argument is absent, or nonzero, a backoff
|
||||
* algorithm will be used to avoid deadlock. If the first
|
||||
* argument is zero, the program will deadlock on a lock
|
||||
* "collision."
|
||||
*/
|
||||
if (argc > 1)
|
||||
backoff = atoi (argv[1]);
|
||||
|
||||
/*
|
||||
* If the second argument is absent, or zero, the two
|
||||
* threads run "at speed." On some systems, especially
|
||||
* uniprocessors, one thread may complete before the other
|
||||
* has a chance to run, and you won't see a deadlock or
|
||||
* backoffs. In that case, try running with the argument set
|
||||
* to a positive number to cause the threads to call
|
||||
* sched_yield() at each lock; or, to make it even more
|
||||
* obvious, set to a negative number to cause the threads to
|
||||
* call sleep(1) instead.
|
||||
*/
|
||||
if (argc > 2)
|
||||
yield_flag = atoi (argv[2]);
|
||||
status = pthread_create (
|
||||
&forward, NULL, lock_forward, NULL);
|
||||
if (status != 0)
|
||||
err_abort (status, "Create forward");
|
||||
status = pthread_create (
|
||||
&backward, NULL, lock_backward, NULL);
|
||||
if (status != 0)
|
||||
err_abort (status, "Create backward");
|
||||
pthread_exit (NULL);
|
||||
}
|
||||
95
src/ch03/cond.c
Normal file
95
src/ch03/cond.c
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* cond.c
|
||||
*
|
||||
* Demonstrate a simple condition variable wait.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include "errors.h"
|
||||
|
||||
typedef struct my_struct_tag {
|
||||
pthread_mutex_t mutex; /* Protects access to value */
|
||||
pthread_cond_t cond; /* Signals change to value */
|
||||
int value; /* Access protected by mutex */
|
||||
} my_struct_t;
|
||||
|
||||
my_struct_t data = {
|
||||
PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
|
||||
|
||||
int hibernation = 1; /* Default to 1 second */
|
||||
|
||||
/*
|
||||
* Thread start routine. It will set the main thread's predicate
|
||||
* and signal the condition variable.
|
||||
*/
|
||||
void *
|
||||
wait_thread (void *arg)
|
||||
{
|
||||
int status;
|
||||
|
||||
sleep (hibernation);
|
||||
status = pthread_mutex_lock (&data.mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Lock mutex");
|
||||
data.value = 1; /* Set predicate */
|
||||
status = pthread_cond_signal (&data.cond);
|
||||
if (status != 0)
|
||||
err_abort (status, "Signal condition");
|
||||
status = pthread_mutex_unlock (&data.mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Unlock mutex");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
int status;
|
||||
pthread_t wait_thread_id;
|
||||
struct timespec timeout;
|
||||
|
||||
/*
|
||||
* If an argument is specified, interpret it as the number
|
||||
* of seconds for wait_thread to sleep before signaling the
|
||||
* condition variable. You can play with this to see the
|
||||
* condition wait below time out or wake normally.
|
||||
*/
|
||||
if (argc > 1)
|
||||
hibernation = atoi (argv[1]);
|
||||
|
||||
/*
|
||||
* Create wait_thread.
|
||||
*/
|
||||
status = pthread_create (&wait_thread_id, NULL, wait_thread, NULL);
|
||||
if (status != 0)
|
||||
err_abort (status, "Create wait thread");
|
||||
|
||||
/*
|
||||
* Wait on the condition variable for 2 seconds, or until
|
||||
* signaled by the wait_thread. Normally, wait_thread
|
||||
* should signal. If you raise "hibernation" above 2
|
||||
* seconds, it will time out.
|
||||
*/
|
||||
timeout.tv_sec = time (NULL) + 2;
|
||||
timeout.tv_nsec = 0;
|
||||
status = pthread_mutex_lock (&data.mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Lock mutex");
|
||||
|
||||
while (data.value == 0) {
|
||||
status = pthread_cond_timedwait (
|
||||
&data.cond, &data.mutex, &timeout);
|
||||
if (status == ETIMEDOUT) {
|
||||
printf ("Condition wait timed out.\n");
|
||||
break;
|
||||
}
|
||||
else if (status != 0)
|
||||
err_abort (status, "Wait on condition");
|
||||
}
|
||||
|
||||
if (data.value != 0)
|
||||
printf ("Condition was signaled.\n");
|
||||
status = pthread_mutex_unlock (&data.mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Unlock mutex");
|
||||
return 0;
|
||||
}
|
||||
40
src/ch03/cond_dynamic.c
Normal file
40
src/ch03/cond_dynamic.c
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* cond_dynamic.c
|
||||
*
|
||||
* Demonstrate dynamic initialization of a condition variable.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include "errors.h"
|
||||
|
||||
/*
|
||||
* Define a structure, with a mutex and condition variable.
|
||||
*/
|
||||
typedef struct my_struct_tag {
|
||||
pthread_mutex_t mutex; /* Protects access to value */
|
||||
pthread_cond_t cond; /* Signals change to value */
|
||||
int value; /* Access protected by mutex */
|
||||
} my_struct_t;
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
my_struct_t *data;
|
||||
int status;
|
||||
|
||||
data = malloc (sizeof (my_struct_t));
|
||||
if (data == NULL)
|
||||
errno_abort ("Allocate structure");
|
||||
status = pthread_mutex_init (&data->mutex, NULL);
|
||||
if (status != 0)
|
||||
err_abort (status, "Init mutex");
|
||||
status = pthread_cond_init (&data->cond, NULL);
|
||||
if (status != 0)
|
||||
err_abort (status, "Init condition");
|
||||
status = pthread_cond_destroy (&data->cond);
|
||||
if (status != 0)
|
||||
err_abort (status, "Destroy condition");
|
||||
status = pthread_mutex_destroy (&data->mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Destroy mutex");
|
||||
(void)free (data);
|
||||
return status;
|
||||
}
|
||||
27
src/ch03/cond_static.c
Normal file
27
src/ch03/cond_static.c
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* cond_static.c
|
||||
*
|
||||
* Demonstrate static initialization of a condition variable.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include "errors.h"
|
||||
|
||||
/*
|
||||
* Declare a structure, with a mutex and condition variable,
|
||||
* statically initialized. This is the same as using
|
||||
* pthread_mutex_init and pthread_cond_init, with the default
|
||||
* attributes.
|
||||
*/
|
||||
typedef struct my_struct_tag {
|
||||
pthread_mutex_t mutex; /* Protects access to value */
|
||||
pthread_cond_t cond; /* Signals change to value */
|
||||
int value; /* Access protected by mutex */
|
||||
} my_struct_t;
|
||||
|
||||
my_struct_t data = {
|
||||
PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
33
src/ch03/mutex_dynamic.c
Normal file
33
src/ch03/mutex_dynamic.c
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* mutex_dynamic.c
|
||||
*
|
||||
* Demonstrate dynamic initialization of a mutex.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include "errors.h"
|
||||
|
||||
/*
|
||||
* Define a structure, with a mutex.
|
||||
*/
|
||||
typedef struct my_struct_tag {
|
||||
pthread_mutex_t mutex; /* Protects access to value */
|
||||
int value; /* Access protected by mutex */
|
||||
} my_struct_t;
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
my_struct_t *data;
|
||||
int status;
|
||||
|
||||
data = malloc (sizeof (my_struct_t));
|
||||
if (data == NULL)
|
||||
errno_abort ("Allocate structure");
|
||||
status = pthread_mutex_init (&data->mutex, NULL);
|
||||
if (status != 0)
|
||||
err_abort (status, "Init mutex");
|
||||
status = pthread_mutex_destroy (&data->mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Destroy mutex");
|
||||
(void)free (data);
|
||||
return status;
|
||||
}
|
||||
23
src/ch03/mutex_static.c
Normal file
23
src/ch03/mutex_static.c
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* mutex_static.c
|
||||
*
|
||||
* Demonstrate static initialization of a mutex.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include "errors.h"
|
||||
|
||||
/*
|
||||
* Declare a structure, with a mutex, statically initialized. This is the
|
||||
* same as using pthread_mutex_init, with the default attributes.
|
||||
*/
|
||||
typedef struct my_struct_tag {
|
||||
pthread_mutex_t mutex; /* Protects access to value */
|
||||
int value; /* Access protected by mutex */
|
||||
} my_struct_t;
|
||||
|
||||
my_struct_t data = {PTHREAD_MUTEX_INITIALIZER, 0};
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
103
src/ch03/trylock.c
Normal file
103
src/ch03/trylock.c
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* trylock.c
|
||||
*
|
||||
* Demonstrate a simple use of pthread_mutex_trylock. The
|
||||
* counter_thread updates a shared counter at intervals, and a
|
||||
* monitor_thread occasionally reports the current value of the
|
||||
* counter -- but only if the mutex is not already locked by
|
||||
* counter_thread.
|
||||
*
|
||||
* Special notes: On a Solaris system, call thr_setconcurrency()
|
||||
* to allow interleaved thread execution, since threads are not
|
||||
* timesliced.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include "errors.h"
|
||||
|
||||
#define SPIN 10000000
|
||||
|
||||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
long counter;
|
||||
time_t end_time;
|
||||
|
||||
/*
|
||||
* Thread start routine that repeatedly locks a mutex and
|
||||
* increments a counter.
|
||||
*/
|
||||
void *counter_thread(void *arg) {
|
||||
int status;
|
||||
int spin;
|
||||
|
||||
/*
|
||||
* Until end_time, increment the counter each
|
||||
* second. Instead of just incrementing the counter, it
|
||||
* sleeps for another second with the mutex locked, to give
|
||||
* monitor_thread a reasonable chance of running.
|
||||
*/
|
||||
while (time(NULL) < end_time) {
|
||||
status = pthread_mutex_lock(&mutex);
|
||||
if (status != 0) err_abort(status, "Lock mutex");
|
||||
for (spin = 0; spin < SPIN; spin++) counter++;
|
||||
sleep(4);
|
||||
status = pthread_mutex_unlock(&mutex);
|
||||
if (status != 0) err_abort(status, "Unlock mutex");
|
||||
sleep(1);
|
||||
}
|
||||
printf("Counter is %#lx\n", counter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Thread start routine to "monitor" the counter. Every 3
|
||||
* seconds, try to lock the mutex and read the counter. If the
|
||||
* trylock fails, skip this cycle.
|
||||
*/
|
||||
void *monitor_thread(void *arg) {
|
||||
int status;
|
||||
int misses = 0;
|
||||
|
||||
/*
|
||||
* Loop until end_time, checking the counter every 3
|
||||
* seconds.
|
||||
*/
|
||||
while (time(NULL) < end_time) {
|
||||
sleep(3);
|
||||
status = pthread_mutex_trylock(&mutex);
|
||||
if (status != EBUSY) {
|
||||
if (status != 0) err_abort(status, "Trylock mutex");
|
||||
printf("Counter is %ld\n", counter / SPIN);
|
||||
status = pthread_mutex_unlock(&mutex);
|
||||
if (status != 0) err_abort(status, "Unlock mutex");
|
||||
} else
|
||||
misses++; /* Count "misses" on the lock */
|
||||
}
|
||||
printf("Monitor thread missed update %d times.\n", misses);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int status;
|
||||
pthread_t counter_thread_id;
|
||||
pthread_t monitor_thread_id;
|
||||
|
||||
#ifdef sun
|
||||
/*
|
||||
* On Solaris 2.5, threads are not timesliced. To ensure
|
||||
* that our threads can run concurrently, we need to
|
||||
* increase the concurrency level to 2.
|
||||
*/
|
||||
DPRINTF(("Setting concurrency level to 2\n"));
|
||||
thr_setconcurrency(2);
|
||||
#endif
|
||||
|
||||
end_time = time(NULL) + 60; /* Run for 1 minute */
|
||||
status = pthread_create(&counter_thread_id, NULL, counter_thread, NULL);
|
||||
if (status != 0) err_abort(status, "Create counter thread");
|
||||
status = pthread_create(&monitor_thread_id, NULL, monitor_thread, NULL);
|
||||
if (status != 0) err_abort(status, "Create monitor thread");
|
||||
status = pthread_join(counter_thread_id, NULL);
|
||||
if (status != 0) err_abort(status, "Join counter thread");
|
||||
status = pthread_join(monitor_thread_id, NULL);
|
||||
if (status != 0) err_abort(status, "Join monitor thread");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user