Hailin
2013-03-28 10:34:22 +08:00
commit a5ec64177d
55 changed files with 6183 additions and 0 deletions

50
Makefile Normal file
View File

@@ -0,0 +1,50 @@
# Digital UNIX 4.0 compilation flags:
CFLAGS=-std1 -pthread -g -w1 $(DEBUGFLAGS)
RTFLAGS=-lrt
# Solaris 2.5 compilation flags:
#CFLAGS=-D_POSIX_C_SOURCE=199506 -D_REENTRANT -Xa -lpthread -g $(DEBUGFLAGS)
#RTFLAGS=-lposix4
SOURCES=alarm.c alarm_cond.c alarm_fork.c alarm_mutex.c \
alarm_thread.c atfork.c backoff.c \
barrier_main.c cancel.c cancel_async.c cancel_cleanup\
cancel_disable.c cancel_subcontract.c cond.c cond_attr.c \
crew.c cond_dynamic.c cond_static.c flock.c getlogin.c hello.c \
inertia.c lifecycle.c mutex_attr.c \
mutex_dynamic.c mutex_static.c once.c pipe.c putchar.c \
rwlock_main.c rwlock_try_main.c \
sched_attr.c sched_thread.c semaphore_signal.c \
semaphore_wait.c server.c sigev_thread.c \
sigwait.c susp.c thread.c \
thread_attr.c thread_error.c trylock.c tsd_destructor.c \
tsd_once.c workq_main.c
PROGRAMS=$(SOURCES:.c=)
all: ${PROGRAMS}
alarm_mutex:
${CC} ${CFLAGS} ${RTFLAGS} ${LDFLAGS} -o $@ alarm_mutex.c
backoff:
${CC} ${CFLAGS} ${RTFLAGS} ${LDFLAGS} -o $@ backoff.c
sched_attr:
${CC} ${CFLAGS} ${RTFLAGS} ${LDFLAGS} -o $@ sched_attr.c
sched_thread:
${CC} ${CFLAGS} ${RTFLAGS} ${LDFLAGS} -o $@ sched_thread.c
semaphore_signal:
${CC} ${CFLAGS} ${RTFLAGS} ${LDFLAGS} -o $@ semaphore_signal.c
semaphore_wait:
${CC} ${CFLAGS} ${RTFLAGS} ${LDFLAGS} -o $@ semaphore_wait.c
sigev_thread:
${CC} ${CFLAGS} ${RTFLAGS} ${LDFLAGS} -o $@ sigev_thread.c
susp:
${CC} ${CFLAGS} ${RTFLAGS} ${LDFLAGS} -o $@ susp.c
rwlock_main: rwlock.c rwlock.h rwlock_main.c
${CC} ${CFLAGS} ${LDFLAGS} -o $@ rwlock_main.c rwlock.c
rwlock_try_main: rwlock.h rwlock.c rwlock_try_main.c
${CC} ${CFLAGS} ${LDFLAGS} -o $@ rwlock_try_main.c rwlock.c
barrier_main: barrier.h barrier.c barrier_main.c
${CC} ${CFLAGS} ${LDFLAGS} -o $@ barrier_main.c barrier.c
workq_main: workq.h workq.c workq_main.c
${CC} ${CFLAGS} ${RTFLAGS} ${LDFLAGS} -o $@ workq_main.c workq.c
clean:
@rm -rf $(PROGRAMS) *.o
recompile: clean all

100
README Normal file
View File

@@ -0,0 +1,100 @@
These are the source files for the programming examples in
"Programming With POSIX(r) Threads". The Makefile is pre-configured
for Digital UNIX, but includes the appropriate definitions to build on
Solaris (uncomment the Solaris lines and comment the Digital UNIX
lines).
alarm.c Simple synchronous alarm clock
alarm_cond.c Threaded alarm clock using condition variable
alarm_fork.c Alarm clock using fork asychrony
alarm_mutex.c Threaded alarm clock using mutex
alarm_thread.c Alarm clock using thread asynchrony
atfork.c Demonstrate pthread_atfork()
backoff.c Demonstrate mutex hierarchy backoff
barrier.c Implementation of barrier package
barrier_main.c Demonstrate use of barrier package
cancel.c Demonstrate cancellation
cancel_async.c Demonstrate asyncronous cancellation
cancel_cleanup.c Demonstrate cancellation cleanup
cancel_disable.c Demonstrate disabling cancellation
cancel_subcontract.c Demonstrate cancellation of "subcontractors"
cond.c Demonstrate use of condition variables
cond_attr.c Demonstrate condition variable attributes
cond_dynamic.c Demonstrate dynamic init of condition variable
cond_static.c Demonstrate static init of condition variable
crew.c A simple threaded work crew
flock.c Demonstrate use of file locking
getlogin.c Demonstrate reentrant user functions
hello.c Demonstrate thread creation
inertia.c Demonstrate "thread inertia" errors
lifecycle.c Demonstrate "thread lifecycle"
mutex_attr.c Demonstrate mutex attributes
mutex_dynamic.c Demonstrate dynamic initialization of mutex
mutex_static.c Demonstrate static initialization of mutex
once.c Demonstrate use of pthread_once()
pipe.c A simple threaded pipeline
putchar.c Demonstrate thread-safe use of putchar()
rwlock.c Implementation of read/write lock package
rwlock_main.c Demonstrate use of read/write lock package
rwlock_try_main.c Demonstrate use of read/write lock package
sched_attr.c Demonstrate thread scheduling attributes
sched_thread.c Demonstrate use of thread scheduling functions
semaphore_signal.c Demonstrate use of semaphores with signals
semaphore_wait.c Demonstrate use of semaphores
server.c A simple threaded client/server program
sigev_thread.c Demonstrate use of SIGEV_THREAD mechanism
sigwait.c Demonstrate use of sigwait()
susp.c Demonstrate use of pthread_kill()
thread.c Demonstrate simple concurrent I/O
thread_attr.c Demonstrate thread attributes
thread_error.c Demonstrate POSIX thread error mechanism
trylock.c Demonstrate use of pthread_mutex_trylock()
tsd_destructor.c Demonstrate thread-specific data destructors
tsd_once.c Demonstrate thread-specific data key creation
workq.c Implementation of work queue package
workq_main.c Demonstrate use of work queue package
Header files:
barrier.h Definitions for barrier package
errors.h General headers and error macros
rwlock.h Definitions for read/write lock package
workq.h Definitions for work queue package
Programs with arguments or special behavior:
alarm, alarm_fork, The alarm programs will prompt for
alarm_thread, alarm_mutex, commands until terminated by ^D
alarm_cond (EOF). Commands are "<n> <s>" where <s>
(the remainder of the line) is a
message to print after <n> seconds.
atfork [hang] Run with an argument of 0 to omit
atfork handlers -- program will hang.
backoff [backoff [delay]] Run with first argument of 0, will not
back off on mutex collision, and will
usually hang. Second argument can be
specified greater than 0 to yield
(increasing chances of hang on
uniprocessor), or less than 0 to sleep
for a second.
crew string path First argument is a search string,
second is a file path.
flock Threads will prompt alternately for
input.
pipe Prompts for integers to feed to
pipeline; enter "=" to pop a result.
putchar [unsync] Run with argument of 0 to concurrently
call putchar_unlocked from multiple
threads.
server Threads each prompt for input, and
echo it 3 times -- server prevents
output while waiting for input.
sigwait Waits for 5 SIGINT signals (^C)
thread One thread writes to stdout while
another waits for input from
stdin. (Satisfy the read to exit.)
/---[ Dave Butenhof ]-----------------------[ butenhof@zko.dec.com ]---\
| Digital Equipment Corporation 110 Spit Brook Rd ZKO2-3/Q18 |
| 603.881.2218, FAX 603.881.0120 Nashua NH 03062-2698 |
\-----------------[ Better Living Through Concurrency ]----------------/

34
alarm.c Normal file
View File

@@ -0,0 +1,34 @@
/*
* alarm.c
*
* Simple synchronous alarm program. This is used as a
* reference for progressive examples of asynchronous
* alarm programs.
*/
#include "errors.h"
int main (int argc, char *argv[])
{
int seconds;
char line[128];
char message[64];
while (1) {
printf ("Alarm> ");
if (fgets (line, sizeof (line), stdin) == NULL) exit (0);
if (strlen (line) <= 1) continue;
/*
* 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]",
&seconds, message) < 2) {
fprintf (stderr, "Bad command\n");
} else {
sleep (seconds);
printf ("(%d) %s\n", seconds, message);
}
}
}

198
alarm_cond.c Normal file
View 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");
}
}
}

56
alarm_fork.c Normal file
View File

@@ -0,0 +1,56 @@
/*
* alarm_fork.c
*
* This version of alarm.c uses fork to create a new process to
* wait for each alarm to expire.
*/
#include <sys/types.h>
#include <wait.h>
#include "errors.h"
int main (int argc, char *argv[])
{
int status;
char line[128];
int seconds;
pid_t pid;
char message[64];
while (1) {
printf ("Alarm> ");
if (fgets (line, sizeof (line), stdin) == NULL) exit (0);
if (strlen (line) <= 1) continue;
/*
* 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]",
&seconds, message) < 2) {
fprintf (stderr, "Bad command\n");
} else {
pid = fork ();
if (pid == (pid_t)-1)
errno_abort ("Fork");
if (pid == (pid_t)0) {
/*
* If we're in the child, wait and then print a message
*/
sleep (seconds);
printf ("(%d) %s\n", seconds, message);
exit (0);
} else {
/*
* In the parent, call waitpid() to collect any children that
* have already terminated.
*/
do {
pid = waitpid ((pid_t)-1, NULL, WNOHANG);
if (pid == (pid_t)-1)
errno_abort ("Wait for child");
} while (pid != (pid_t)0);
}
}
}
}

175
alarm_mutex.c Normal file
View 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");
}
}
}

59
alarm_thread.c Normal file
View File

@@ -0,0 +1,59 @@
/*
* alarm_fork.c
*
* This version of alarm.c uses pthread_create to create a
* separate thread to wait for each alarm to expire.
*/
#include <pthread.h>
#include "errors.h"
typedef struct alarm_tag {
int seconds;
char message[64];
} alarm_t;
void *alarm_thread (void *arg)
{
alarm_t *alarm = (alarm_t*)arg;
int status;
status = pthread_detach (pthread_self ());
if (status != 0)
err_abort (status, "Detach thread");
sleep (alarm->seconds);
printf ("(%d) %s\n", alarm->seconds, alarm->message);
free (alarm);
return NULL;
}
int main (int argc, char *argv[])
{
int status;
char line[128];
alarm_t *alarm;
pthread_t 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_create (
&thread, NULL, alarm_thread, alarm);
if (status != 0)
err_abort (status, "Create alarm thread");
}
}
}

130
atfork.c Normal file
View File

@@ -0,0 +1,130 @@
/*
* atfork.c
*
* Demonstrate the use of "fork handlers" to protect data
* invariants across a fork.
*/
#include <sys/types.h>
#include <pthread.h>
#include <sys/wait.h>
#include "errors.h"
pid_t self_pid; /* pid of current process */
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
/*
* This routine will be called prior to executing the fork,
* within the parent process.
*/
void fork_prepare (void)
{
int status;
/*
* Lock the mutex in the parent before creating the child,
* to ensure that no other thread can lock it (or change any
* associated shared state) until after the fork completes.
*/
status = pthread_mutex_lock (&mutex);
if (status != 0)
err_abort (status, "Lock in prepare handler");
}
/*
* This routine will be called after executing the fork, within
* the parent process
*/
void fork_parent (void)
{
int status;
/*
* Unlock the mutex in the parent after the child has been created.
*/
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock in parent handler");
}
/*
* This routine will be called after executing the fork, within
* the child process
*/
void fork_child (void)
{
int status;
/*
* Update the file scope "self_pid" within the child process, and unlock
* the mutex.
*/
self_pid = getpid ();
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock in child handler");
}
/*
* Thread start routine, which will fork a new child process.
*/
void *thread_routine (void *arg)
{
pid_t child_pid;
int status;
child_pid = fork ();
if (child_pid == (pid_t)-1)
errno_abort ("Fork");
/*
* Lock the mutex -- without the atfork handlers, the mutex will remain
* locked in the child process and this lock attempt will hang (or fail
* with EDEADLK) in the child.
*/
status = pthread_mutex_lock (&mutex);
if (status != 0)
err_abort (status, "Lock in child");
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock in child");
printf ("After fork: %d (%d)\n", child_pid, self_pid);
if (child_pid != 0) {
if ((pid_t)-1 == waitpid (child_pid, (int*)0, 0))
errno_abort ("Wait for child");
}
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t fork_thread;
int atfork_flag = 1;
int status;
if (argc > 1)
atfork_flag = atoi (argv[1]);
if (atfork_flag) {
status = pthread_atfork (fork_prepare, fork_parent, fork_child);
if (status != 0)
err_abort (status, "Register fork handlers");
}
self_pid = getpid ();
status = pthread_mutex_lock (&mutex);
if (status != 0)
err_abort (status, "Lock mutex");
/*
* Create a thread while the mutex is locked. It will fork a process,
* which (without atfork handlers) will run with the mutex locked.
*/
status = pthread_create (&fork_thread, NULL, thread_routine, NULL);
if (status != 0)
err_abort (status, "Create thread");
sleep (5);
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
status = pthread_join (fork_thread, NULL);
if (status != 0)
err_abort (status, "Join thread");
return 0;
}

199
backoff.c Normal file
View 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);
}

144
barrier.c Normal file
View File

@@ -0,0 +1,144 @@
/*
* barrier.c
*
* This file implements the "barrier" synchronization construct.
*
* A barrier causes threads to wait until a set of threads has
* all "reached" the barrier. The number of threads required is
* set when the barrier is initialized, and cannot be changed
* except by reinitializing.
*
* The barrier_init() and barrier_destroy() functions,
* respectively, allow you to initialize and destroy the
* barrier.
*
* The barrier_wait() function allows a thread to wait for a
* barrier to be completed. One thread (the one that happens to
* arrive last) will return from barrier_wait() with the status
* -1 on success -- others will return with 0. The special
* status makes it easy for the calling code to cause one thread
* to do something in a serial region before entering another
* parallel section of code.
*/
#include <pthread.h>
#include "errors.h"
#include "barrier.h"
/*
* Initialize a barrier for use.
*/
int barrier_init (barrier_t *barrier, int count)
{
int status;
barrier->threshold = barrier->counter = count;
barrier->cycle = 0;
status = pthread_mutex_init (&barrier->mutex, NULL);
if (status != 0)
return status;
status = pthread_cond_init (&barrier->cv, NULL);
if (status != 0) {
pthread_mutex_destroy (&barrier->mutex);
return status;
}
barrier->valid = BARRIER_VALID;
return 0;
}
/*
* Destroy a barrier when done using it.
*/
int barrier_destroy (barrier_t *barrier)
{
int status, status2;
if (barrier->valid != BARRIER_VALID)
return EINVAL;
status = pthread_mutex_lock (&barrier->mutex);
if (status != 0)
return status;
/*
* Check whether any threads are known to be waiting; report
* "BUSY" if so.
*/
if (barrier->counter != barrier->threshold) {
pthread_mutex_unlock (&barrier->mutex);
return EBUSY;
}
barrier->valid = 0;
status = pthread_mutex_unlock (&barrier->mutex);
if (status != 0)
return status;
/*
* If unable to destroy either 1003.1c synchronization
* object, return the error status.
*/
status = pthread_mutex_destroy (&barrier->mutex);
status2 = pthread_cond_destroy (&barrier->cv);
return (status == 0 ? status : status2);
}
/*
* Wait for all members of a barrier to reach the barrier. When
* the count (of remaining members) reaches 0, broadcast to wake
* all threads waiting.
*/
int barrier_wait (barrier_t *barrier)
{
int status, cancel, tmp, cycle;
if (barrier->valid != BARRIER_VALID)
return EINVAL;
status = pthread_mutex_lock (&barrier->mutex);
if (status != 0)
return status;
cycle = barrier->cycle; /* Remember which cycle we're on */
if (--barrier->counter == 0) {
barrier->cycle = !barrier->cycle;
barrier->counter = barrier->threshold;
status = pthread_cond_broadcast (&barrier->cv);
/*
* The last thread into the barrier will return status
* -1 rather than 0, so that it can be used to perform
* some special serial code following the barrier.
*/
if (status == 0)
status = -1;
} else {
/*
* Wait with cancellation disabled, because barrier_wait
* should not be a cancellation point.
*/
pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &cancel);
/*
* Wait until the barrier's cycle changes, which means
* that it has been broadcast, and we don't want to wait
* anymore.
*/
while (cycle == barrier->cycle) {
status = pthread_cond_wait (
&barrier->cv, &barrier->mutex);
if (status != 0) break;
}
pthread_setcancelstate (cancel, &tmp);
}
/*
* Ignore an error in unlocking. It shouldn't happen, and
* reporting it here would be misleading -- the barrier wait
* completed, after all, whereas returning, for example,
* EINVAL would imply the wait had failed. The next attempt
* to use the barrier *will* return an error, or hang, due
* to whatever happened to the mutex.
*/
pthread_mutex_unlock (&barrier->mutex);
return status; /* error, -1 for waker, or 0 */
}

42
barrier.h Normal file
View File

@@ -0,0 +1,42 @@
/*
* barrier.h
*
* This header file describes the "barrier" synchronization
* construct. The type barrier_t describes the full state of the
* barrier including the POSIX 1003.1c synchronization objects
* necessary.
*
* A barrier causes threads to wait until a set of threads has
* all "reached" the barrier. The number of threads required is
* set when the barrier is initialized, and cannot be changed
* except by reinitializing.
*/
#include <pthread.h>
/*
* Structure describing a barrier.
*/
typedef struct barrier_tag {
pthread_mutex_t mutex; /* Control access to barrier */
pthread_cond_t cv; /* wait for barrier */
int valid; /* set when valid */
int threshold; /* number of threads required */
int counter; /* current number of threads */
int cycle; /* alternate wait cycles (0 or 1) */
} barrier_t;
#define BARRIER_VALID 0xdbcafe
/*
* Support static initialization of barriers
*/
#define BARRIER_INITIALIZER(cnt) \
{PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, \
BARRIER_VALID, cnt, cnt, 0}
/*
* Define barrier functions
*/
extern int barrier_init (barrier_t *barrier, int count);
extern int barrier_destroy (barrier_t *barrier);
extern int barrier_wait (barrier_t *barrier);

115
barrier_main.c Normal file
View File

@@ -0,0 +1,115 @@
/*
* barrier_main.c
*
* Demonstrate use of barriers, using the barrier implementation
* in barrier.c.
*/
#include <pthread.h>
#include "barrier.h"
#include "errors.h"
#define THREADS 5
#define ARRAY 6
#define INLOOPS 1000
#define OUTLOOPS 10
/*
* Keep track of each thread
*/
typedef struct thread_tag {
pthread_t thread_id;
int number;
int increment;
int array[ARRAY];
} thread_t;
barrier_t barrier;
thread_t thread[THREADS];
/*
* Start routine for threads.
*/
void *thread_routine (void *arg)
{
thread_t *self = (thread_t*)arg; /* Thread's thread_t */
int in_loop, out_loop, count, status;
/*
* Loop through OUTLOOPS barrier cycles.
*/
for (out_loop = 0; out_loop < OUTLOOPS; out_loop++) {
status = barrier_wait (&barrier);
if (status > 0)
err_abort (status, "Wait on barrier");
/*
* This inner loop just adds a value to each element in
* the working array.
*/
for (in_loop = 0; in_loop < INLOOPS; in_loop++)
for (count = 0; count < ARRAY; count++)
self->array[count] += self->increment;
status = barrier_wait (&barrier);
if (status > 0)
err_abort (status, "Wait on barrier");
/*
* The barrier causes one thread to return with the
* special return status -1. The thread receiving this
* value increments each element in the shared array.
*/
if (status == -1) {
int thread_num;
for (thread_num = 0; thread_num < THREADS; thread_num++)
thread[thread_num].increment += 1;
}
}
return NULL;
}
int main (int arg, char *argv[])
{
int thread_count, array_count;
int status;
barrier_init (&barrier, THREADS);
/*
* Create a set of threads that will use the barrier.
*/
for (thread_count = 0; thread_count < THREADS; thread_count++) {
thread[thread_count].increment = thread_count;
thread[thread_count].number = thread_count;
for (array_count = 0; array_count < ARRAY; array_count++)
thread[thread_count].array[array_count] = array_count + 1;
status = pthread_create (&thread[thread_count].thread_id,
NULL, thread_routine, (void*)&thread[thread_count]);
if (status != 0)
err_abort (status, "Create thread");
}
/*
* Now join with each of the threads.
*/
for (thread_count = 0; thread_count < THREADS; thread_count++) {
status = pthread_join (thread[thread_count].thread_id, NULL);
if (status != 0)
err_abort (status, "Join thread");
printf ("%02d: (%d) ", thread_count, thread[thread_count].increment);
for (array_count = 0; array_count < ARRAY; array_count++)
printf ("%010u ", thread[thread_count].array[array_count]);
printf ("\n");
}
/*
* To be thorough, destroy the barrier.
*/
barrier_destroy (&barrier);
return 0;
}

66
cancel.c Normal file
View File

@@ -0,0 +1,66 @@
/*
* cancel.c
*
* Demonstrate use of synchronous cancellation using
* pthread_testcancel.
*
* Special notes: On a Solaris 2.5 uniprocessor, this test will
* hang unless a second LWP is created by calling
* thr_setconcurrency() because threads are not timesliced.
*/
#include <pthread.h>
#include "errors.h"
static int counter;
/*
* Loop until cancelled. The thread can be cancelled only
* when it calls pthread_testcancel, which it does each 1000
* iterations.
*/
void *thread_routine (void *arg)
{
DPRINTF (("thread_routine starting\n"));
for (counter = 0; ; counter++)
if ((counter % 1000) == 0) {
DPRINTF (("calling testcancel\n"));
pthread_testcancel ();
}
}
int main (int argc, char *argv[])
{
pthread_t thread_id;
void *result;
int status;
#ifdef sun
/*
* On Solaris 2.5, threads are not timesliced. To ensure
* that our two threads can run concurrently, we need to
* increase the concurrency level to 2.
*/
DPRINTF (("Setting concurrency level to 2\n"));
thr_setconcurrency (2);
#endif
status = pthread_create (
&thread_id, NULL, thread_routine, NULL);
if (status != 0)
err_abort (status, "Create thread");
sleep (2);
DPRINTF (("calling cancel\n"));
status = pthread_cancel (thread_id);
if (status != 0)
err_abort (status, "Cancel thread");
DPRINTF (("calling join\n"));
status = pthread_join (thread_id, &result);
if (status != 0)
err_abort (status, "Join thread");
if (result == PTHREAD_CANCELED)
printf ("Thread cancelled at iteration %d\n", counter);
else
printf ("Thread was not cancelled\n");
return 0;
}

130
cancel_async.c Normal file
View File

@@ -0,0 +1,130 @@
/*
* cancel_async.c
*
* Demonstrate asynchronous cancellation of a compute-bound
* thread.
*
* Special notes: On a Solaris 2.5 uniprocessor, this test will
* hang unless a second LWP is created by calling
* thr_setconcurrency() because threads are not timesliced.
*/
#include <pthread.h>
#include "errors.h"
#define SIZE 10 /* array size */
static int matrixa[SIZE][SIZE];
static int matrixb[SIZE][SIZE];
static int matrixc[SIZE][SIZE];
#ifdef DEBUG
void print_array (int matrix[SIZE][SIZE])
{
int i, j;
int first;
for (i = 0; i < SIZE; i++) {
printf ("[");
first = 1;
for (j = 0; j < SIZE; j++) {
if (!first)
printf (",");
printf ("%x", matrix[i][j]);
first = 0;
}
printf ("]\n");
}
}
#endif
/*
* Loop until cancelled. The thread can be cancelled at any
* point within the inner loop, where asynchronous cancellation
* is enabled. The loop multiplies the two matrices matrixa
* and matrixb.
*/
void *thread_routine (void *arg)
{
int cancel_type, status;
int i, j, k, value = 1;
/*
* Initialize the matrices to something arbitrary.
*/
for (i = 0; i < SIZE; i++)
for (j = 0; j < SIZE; j++) {
matrixa[i][j] = i;
matrixb[i][j] = j;
}
while (1) {
/*
* Compute the matrix product of matrixa and matrixb.
*/
status = pthread_setcanceltype (
PTHREAD_CANCEL_ASYNCHRONOUS,
&cancel_type);
if (status != 0)
err_abort (status, "Set cancel type");
for (i = 0; i < SIZE; i++)
for (j = 0; j < SIZE; j++) {
matrixc[i][j] = 0;
for (k = 0; k < SIZE; k++)
matrixc[i][j] += matrixa[i][k] * matrixb[k][j];
}
status = pthread_setcanceltype (
cancel_type,
&cancel_type);
if (status != 0)
err_abort (status, "Set cancel type");
/*
* Copy the result (matrixc) into matrixa to start again
*/
for (i = 0; i < SIZE; i++)
for (j = 0; j < SIZE; j++)
matrixa[i][j] = matrixc[i][j];
}
}
int main (int argc, char *argv[])
{
pthread_t thread_id;
void *result;
int status;
#ifdef sun
/*
* On Solaris 2.5, threads are not timesliced. To ensure
* that our two threads can run concurrently, we need to
* increase the concurrency level to 2.
*/
DPRINTF (("Setting concurrency level to 2\n"));
thr_setconcurrency (2);
#endif
status = pthread_create (
&thread_id, NULL, thread_routine, NULL);
if (status != 0)
err_abort (status, "Create thread");
sleep (1);
status = pthread_cancel (thread_id);
if (status != 0)
err_abort (status, "Cancel thread");
status = pthread_join (thread_id, &result);
if (status != 0)
err_abort (status, "Join thread");
if (result == PTHREAD_CANCELED)
printf ("Thread cancelled\n");
else
printf ("Thread was not cancelled\n");
#ifdef DEBUG
printf ("Matrix a:\n");
print_array (matrixa);
printf ("\nMatrix b:\n");
print_array (matrixb);
printf ("\nMatrix c:\n");
print_array (matrixc);
#endif
return 0;
}

100
cancel_cleanup.c Normal file
View File

@@ -0,0 +1,100 @@
/*
* cancel_cleanup.c
*
* Demonstrate use of a cleanup handler to release resources and
* restore invariants on cancellation of a wait.
*/
#include <pthread.h>
#include "errors.h"
#define THREADS 5
/*
* Control structure shared by the test threads, containing
* the synchronization and invariant data.
*/
typedef struct control_tag {
int counter, busy;
pthread_mutex_t mutex;
pthread_cond_t cv;
} control_t;
control_t control =
{0, 1, PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER};
/*
* This routine is installed as the cancellation cleanup
* handler around the cancellable condition wait. It will
* be called by the system when the thread is cancelled.
*/
void cleanup_handler (void *arg)
{
control_t *st = (control_t *)arg;
int status;
st->counter--;
printf ("cleanup_handler: counter == %d\n", st->counter);
status = pthread_mutex_unlock (&st->mutex);
if (status != 0)
err_abort (status, "Unlock in cleanup handler");
}
/*
* Multiple threads are created running this routine (controlled
* by the THREADS macro). They maintain a "counter" invariant,
* which expresses the number of running threads. They specify a
* nonzero value to pthread_cleanup_pop to run the same
* "finalization" action when cancellation does not occur.
*/
void *thread_routine (void *arg)
{
int status;
pthread_cleanup_push (cleanup_handler, (void*)&control);
status = pthread_mutex_lock (&control.mutex);
if (status != 0)
err_abort (status, "Mutex lock");
control.counter++;
while (control.busy) {
status = pthread_cond_wait (&control.cv, &control.mutex);
if (status != 0)
err_abort (status, "Wait on condition");
}
pthread_cleanup_pop (1);
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t thread_id[THREADS];
int count;
void *result;
int status;
for (count = 0; count < THREADS; count++) {
status = pthread_create (
&thread_id[count], NULL, thread_routine, NULL);
if (status != 0)
err_abort (status, "Create thread");
}
sleep (2);
for (count = 0; count < THREADS; count++) {
status = pthread_cancel (thread_id[count]);
if (status != 0)
err_abort (status, "Cancel thread");
status = pthread_join (thread_id[count], &result);
if (status != 0)
err_abort (status, "Join thread");
if (result == PTHREAD_CANCELED)
printf ("thread %d cancelled\n", count);
else
printf ("thread %d was not cancelled\n", count);
}
return 0;
}

68
cancel_disable.c Normal file
View File

@@ -0,0 +1,68 @@
/*
* cancel_disable.c
*
* Demonstrate running a section of code with cancellation
* disabled.
*/
#include <pthread.h>
#include "errors.h"
static int counter;
/*
* Thread start routine.
*/
void *thread_routine (void *arg)
{
int state;
int status;
for (counter = 0; ; counter++) {
/*
* Each 755 iterations, disable cancellation and sleep
* for one second.
*
* Each 1000 iterations, test for a pending cancel by
* calling pthread_testcancel().
*/
if ((counter % 755) == 0) {
status = pthread_setcancelstate (
PTHREAD_CANCEL_DISABLE, &state);
if (status != 0)
err_abort (status, "Disable cancel");
sleep (1);
status = pthread_setcancelstate (
state, &state);
if (status != 0)
err_abort (status, "Restore cancel");
} else
if ((counter % 1000) == 0)
pthread_testcancel ();
}
}
int main (int argc, char *argv[])
{
pthread_t thread_id;
void *result;
int status;
status = pthread_create (
&thread_id, NULL, thread_routine, NULL);
if (status != 0)
err_abort (status, "Create thread");
sleep (2);
status = pthread_cancel (thread_id);
if (status != 0)
err_abort (status, "Cancel thread");
status = pthread_join (thread_id, &result);
if (status != 0)
err_abort (status, "Join thread");
if (result == PTHREAD_CANCELED)
printf ("Thread cancelled at iteration %d\n", counter);
else
printf ("Thread was not cancelled\n");
return 0;
}

115
cancel_subcontract.c Normal file
View File

@@ -0,0 +1,115 @@
/*
* cancel_subcontract.c
*
* Demonstrate how a thread can handle cancellation and in turn
* cancel a set of worker ("subcontractor") threads.
*
* Special notes: On a Solaris 2.5 uniprocessor, this test will
* hang unless an LWP is created for each worker thread by
* calling thr_setconcurrency(), because threads are not
* timesliced.
*/
#include <pthread.h>
#include "errors.h"
#define THREADS 5
/*
* Structure that defines the threads in a "team".
*/
typedef struct team_tag {
int join_i; /* join index */
pthread_t workers[THREADS]; /* thread identifiers */
} team_t;
/*
* Start routine for worker threads. They loop waiting for a
* cancellation request.
*/
void *worker_routine (void *arg)
{
int counter;
for (counter = 0; ; counter++)
if ((counter % 1000) == 0)
pthread_testcancel ();
}
/*
* Cancellation cleanup handler for the contractor thread. It
* will cancel and detach each worker in the team.
*/
void cleanup (void *arg)
{
team_t *team = (team_t *)arg;
int count, status;
for (count = team->join_i; count < THREADS; count++) {
status = pthread_cancel (team->workers[count]);
if (status != 0)
err_abort (status, "Cancel worker");
status = pthread_detach (team->workers[count]);
if (status != 0)
err_abort (status, "Detach worker");
printf ("Cleanup: cancelled %d\n", count);
}
}
/*
* Thread start routine for the contractor. It creates a team of
* worker threads, and then joins with them. When cancelled, the
* cleanup handler will cancel and detach the remaining threads.
*/
void *thread_routine (void *arg)
{
team_t team; /* team info */
int count;
void *result; /* Return status */
int status;
for (count = 0; count < THREADS; count++) {
status = pthread_create (
&team.workers[count], NULL, worker_routine, NULL);
if (status != 0)
err_abort (status, "Create worker");
}
pthread_cleanup_push (cleanup, (void*)&team);
for (team.join_i = 0; team.join_i < THREADS; team.join_i++) {
status = pthread_join (team.workers[team.join_i], &result);
if (status != 0)
err_abort (status, "Join worker");
}
pthread_cleanup_pop (0);
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t thread_id;
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 to at least 2 plus THREADS
* (the number of workers).
*/
DPRINTF (("Setting concurrency level to %d\n", THREADS+2));
thr_setconcurrency (THREADS+2);
#endif
status = pthread_create (&thread_id, NULL, thread_routine, NULL);
if (status != 0)
err_abort (status, "Create team");
sleep (5);
printf ("Cancelling...\n");
status = pthread_cancel (thread_id);
if (status != 0)
err_abort (status, "Cancel team");
status = pthread_join (thread_id, NULL);
if (status != 0)
err_abort (status, "Join team");
}

95
cond.c Normal file
View 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;
}

34
cond_attr.c Normal file
View File

@@ -0,0 +1,34 @@
/*
* cond_attr.c
*
* main() creates a condition variable using a non-default attributes object,
* cond_attr. If the implementation supports the pshared attribute, the
* condition variable is created "process private". (Note that, to create a
* "process shared" condition variable, the pthread_cond_t itself must be
* placed in shared memory that is accessible to all threads using the
* condition variable.)
*/
#include <pthread.h>
#include "errors.h"
pthread_cond_t cond;
int main (int argc, char *argv[])
{
pthread_condattr_t cond_attr;
int status;
status = pthread_condattr_init (&cond_attr);
if (status != 0)
err_abort (status, "Create attr");
#ifdef _POSIX_THREAD_PROCESS_SHARED
status = pthread_condattr_setpshared (
&cond_attr, PTHREAD_PROCESS_PRIVATE);
if (status != 0)
err_abort (status, "Set pshared");
#endif
status = pthread_cond_init (&cond, &cond_attr);
if (status != 0)
err_abort (status, "Init cond");
return 0;
}

40
cond_dynamic.c Normal file
View 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
cond_static.c Normal file
View 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;
}

485
crew.c Normal file
View File

@@ -0,0 +1,485 @@
/*
* crew.c
*
* Demonstrate a work crew implementing a simple parallel search
* through a directory tree.
*
* 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 <sys/types.h>
#include <pthread.h>
#include <sys/stat.h>
#include <dirent.h>
#include "errors.h"
#define CREW_SIZE 4
/*
* Queued items of work for the crew. One is queued by
* crew_start, and each worker may queue additional items.
*/
typedef struct work_tag {
struct work_tag *next; /* Next work item */
char *path; /* Directory or file */
char *string; /* Search string */
} work_t, *work_p;
/*
* One of these is initialized for each worker thread in the
* crew. It contains the "identity" of each worker.
*/
typedef struct worker_tag {
int index; /* Thread's index */
pthread_t thread; /* Thread for stage */
struct crew_tag *crew; /* Pointer to crew */
} worker_t, *worker_p;
/*
* The external "handle" for a work crew. Contains the
* crew synchronization state and staging area.
*/
typedef struct crew_tag {
int crew_size; /* Size of array */
worker_t crew[CREW_SIZE];/* Crew members */
long work_count; /* Count of work items */
work_t *first, *last; /* First & last work item */
pthread_mutex_t mutex; /* Mutex for crew data */
pthread_cond_t done; /* Wait for crew done */
pthread_cond_t go; /* Wait for work */
} crew_t, *crew_p;
size_t path_max; /* Filepath length */
size_t name_max; /* Name length */
/*
* The thread start routine for crew threads. Waits until "go"
* command, processes work items until requested to shut down.
*/
void *worker_routine (void *arg)
{
worker_p mine = (worker_t*)arg;
crew_p crew = mine->crew;
work_p work, new_work;
struct stat filestat;
struct dirent *entry;
int status;
/*
* "struct dirent" is funny, because POSIX doesn't require
* the definition to be more than a header for a variable
* buffer. Thus, allocate a "big chunk" of memory, and use
* it as a buffer.
*/
entry = (struct dirent*)malloc (
sizeof (struct dirent) + name_max);
if (entry == NULL)
errno_abort ("Allocating dirent");
status = pthread_mutex_lock (&crew->mutex);
if (status != 0)
err_abort (status, "Lock crew mutex");
/*
* There won't be any work when the crew is created, so wait
* until something's put on the queue.
*/
while (crew->work_count == 0) {
status = pthread_cond_wait (&crew->go, &crew->mutex);
if (status != 0)
err_abort (status, "Wait for go");
}
status = pthread_mutex_unlock (&crew->mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
DPRINTF (("Crew %d starting\n", mine->index));
/*
* Now, as long as there's work, keep doing it.
*/
while (1) {
/*
* Wait while there is nothing to do, and
* the hope of something coming along later. If
* crew->first is NULL, there's no work. But if
* crew->work_count goes to zero, we're done.
*/
status = pthread_mutex_lock (&crew->mutex);
if (status != 0)
err_abort (status, "Lock crew mutex");
DPRINTF (("Crew %d top: first is %#lx, count is %d\n",
mine->index, crew->first, crew->work_count));
while (crew->first == NULL) {
status = pthread_cond_wait (&crew->go, &crew->mutex);
if (status != 0)
err_abort (status, "Wait for work");
}
DPRINTF (("Crew %d woke: %#lx, %d\n",
mine->index, crew->first, crew->work_count));
/*
* Remove and process a work item
*/
work = crew->first;
crew->first = work->next;
if (crew->first == NULL)
crew->last = NULL;
DPRINTF (("Crew %d took %#lx, leaves first %#lx, last %#lx\n",
mine->index, work, crew->first, crew->last));
status = pthread_mutex_unlock (&crew->mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
/*
* We have a work item. Process it, which may involve
* queuing new work items.
*/
status = lstat (work->path, &filestat);
if (S_ISLNK (filestat.st_mode))
printf (
"Thread %d: %s is a link, skipping.\n",
mine->index,
work->path);
else if (S_ISDIR (filestat.st_mode)) {
DIR *directory;
struct dirent *result;
/*
* If the file is a directory, search it and place
* all files onto the queue as new work items.
*/
directory = opendir (work->path);
if (directory == NULL) {
fprintf (
stderr, "Unable to open directory %s: %d (%s)\n",
work->path,
errno, strerror (errno));
continue;
}
while (1) {
status = readdir_r (directory, entry, &result);
if (status != 0) {
fprintf (
stderr,
"Unable to read directory %s: %d (%s)\n",
work->path,
status, strerror (status));
break;
}
if (result == NULL)
break; /* End of directory */
/*
* Ignore "." and ".." entries.
*/
if (strcmp (entry->d_name, ".") == 0)
continue;
if (strcmp (entry->d_name, "..") == 0)
continue;
new_work = (work_p)malloc (sizeof (work_t));
if (new_work == NULL)
errno_abort ("Unable to allocate space");
new_work->path = (char*)malloc (path_max);
if (new_work->path == NULL)
errno_abort ("Unable to allocate path");
strcpy (new_work->path, work->path);
strcat (new_work->path, "/");
strcat (new_work->path, entry->d_name);
new_work->string = work->string;
new_work->next = NULL;
status = pthread_mutex_lock (&crew->mutex);
if (status != 0)
err_abort (status, "Lock mutex");
if (crew->first == NULL) {
crew->first = new_work;
crew->last = new_work;
} else {
crew->last->next = new_work;
crew->last = new_work;
}
crew->work_count++;
DPRINTF ((
"Crew %d: add work %#lx, first %#lx, last %#lx, %d\n",
mine->index, new_work, crew->first,
crew->last, crew->work_count));
status = pthread_cond_signal (&crew->go);
status = pthread_mutex_unlock (&crew->mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
}
closedir (directory);
} else if (S_ISREG (filestat.st_mode)) {
FILE *search;
char buffer[256], *bufptr, *search_ptr;
/*
* If this is a file, not a directory, then search
* it for the string.
*/
search = fopen (work->path, "r");
if (search == NULL)
fprintf (
stderr, "Unable to open %s: %d (%s)\n",
work->path,
errno, strerror (errno));
else {
while (1) {
bufptr = fgets (
buffer, sizeof (buffer), search);
if (bufptr == NULL) {
if (feof (search))
break;
if (ferror (search)) {
fprintf (
stderr,
"Unable to read %s: %d (%s)\n",
work->path,
errno, strerror (errno));
break;
}
}
search_ptr = strstr (buffer, work->string);
if (search_ptr != NULL) {
flockfile (stdout);
printf (
"Thread %d found \"%s\" in %s\n",
mine->index, work->string, work->path);
#if 0
printf ("%s\n", buffer);
#endif
funlockfile (stdout);
break;
}
}
fclose (search);
}
} else
fprintf (
stderr,
"Thread %d: %s is type %o (%s))\n",
mine->index,
work->path,
filestat.st_mode & S_IFMT,
(S_ISFIFO (filestat.st_mode) ? "FIFO"
: (S_ISCHR (filestat.st_mode) ? "CHR"
: (S_ISBLK (filestat.st_mode) ? "BLK"
: (S_ISSOCK (filestat.st_mode) ? "SOCK"
: "unknown")))));
free (work->path); /* Free path buffer */
free (work); /* We're done with this */
/*
* Decrement count of outstanding work items, and wake
* waiters (trying to collect results or start a new
* calculation) if the crew is now idle.
*
* It's important that the count be decremented AFTER
* processing the current work item. That ensures the
* count won't go to 0 until we're really done.
*/
status = pthread_mutex_lock (&crew->mutex);
if (status != 0)
err_abort (status, "Lock crew mutex");
crew->work_count--;
DPRINTF (("Crew %d decremented work to %d\n", mine->index,
crew->work_count));
if (crew->work_count <= 0) {
DPRINTF (("Crew thread %d done\n", mine->index));
status = pthread_cond_broadcast (&crew->done);
if (status != 0)
err_abort (status, "Wake waiters");
status = pthread_mutex_unlock (&crew->mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
break;
}
status = pthread_mutex_unlock (&crew->mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
}
free (entry);
return NULL;
}
/*
* Create a work crew.
*/
int crew_create (crew_t *crew, int crew_size)
{
int crew_index;
int status;
/*
* We won't create more than CREW_SIZE members
*/
if (crew_size > CREW_SIZE)
return EINVAL;
crew->crew_size = crew_size;
crew->work_count = 0;
crew->first = NULL;
crew->last = NULL;
/*
* Initialize synchronization objects
*/
status = pthread_mutex_init (&crew->mutex, NULL);
if (status != 0)
return status;
status = pthread_cond_init (&crew->done, NULL);
if (status != 0)
return status;
status = pthread_cond_init (&crew->go, NULL);
if (status != 0)
return status;
/*
* Create the worker threads.
*/
for (crew_index = 0; crew_index < CREW_SIZE; crew_index++) {
crew->crew[crew_index].index = crew_index;
crew->crew[crew_index].crew = crew;
status = pthread_create (&crew->crew[crew_index].thread,
NULL, worker_routine, (void*)&crew->crew[crew_index]);
if (status != 0)
err_abort (status, "Create worker");
}
return 0;
}
/*
* Pass a file path to a work crew previously created
* using crew_create
*/
int crew_start (
crew_p crew,
char *filepath,
char *search)
{
work_p request;
int status;
status = pthread_mutex_lock (&crew->mutex);
if (status != 0)
return status;
/*
* If the crew is busy, wait for them to finish.
*/
while (crew->work_count > 0) {
status = pthread_cond_wait (&crew->done, &crew->mutex);
if (status != 0) {
pthread_mutex_unlock (&crew->mutex);
return status;
}
}
errno = 0;
path_max = pathconf (filepath, _PC_PATH_MAX);
if (path_max == -1) {
if (errno == 0)
path_max = 1024; /* "No limit" */
else
errno_abort ("Unable to get PATH_MAX");
}
errno = 0;
name_max = pathconf (filepath, _PC_NAME_MAX);
if (name_max == -1) {
if (errno == 0)
name_max = 256; /* "No limit" */
else
errno_abort ("Unable to get NAME_MAX");
}
DPRINTF ((
"PATH_MAX for %s is %ld, NAME_MAX is %ld\n",
filepath, path_max, name_max));
path_max++; /* Add null byte */
name_max++; /* Add null byte */
request = (work_p)malloc (sizeof (work_t));
if (request == NULL)
errno_abort ("Unable to allocate request");
DPRINTF (("Requesting %s\n", filepath));
request->path = (char*)malloc (path_max);
if (request->path == NULL)
errno_abort ("Unable to allocate path");
strcpy (request->path, filepath);
request->string = search;
request->next = NULL;
if (crew->first == NULL) {
crew->first = request;
crew->last = request;
} else {
crew->last->next = request;
crew->last = request;
}
crew->work_count++;
status = pthread_cond_signal (&crew->go);
if (status != 0) {
free (crew->first);
crew->first = NULL;
crew->work_count = 0;
pthread_mutex_unlock (&crew->mutex);
return status;
}
while (crew->work_count > 0) {
status = pthread_cond_wait (&crew->done, &crew->mutex);
if (status != 0)
err_abort (status, "waiting for crew to finish");
}
status = pthread_mutex_unlock (&crew->mutex);
if (status != 0)
err_abort (status, "Unlock crew mutex");
return 0;
}
/*
* The main program to "drive" the crew...
*/
int main (int argc, char *argv[])
{
crew_t my_crew;
char line[128], *next;
int status;
if (argc < 3) {
fprintf (stderr, "Usage: %s string path\n", argv[0]);
return -1;
}
#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 CREW_SIZE.
*/
DPRINTF (("Setting concurrency level to %d\n", CREW_SIZE));
thr_setconcurrency (CREW_SIZE);
#endif
status = crew_create (&my_crew, CREW_SIZE);
if (status != 0)
err_abort (status, "Create crew");
status = crew_start (&my_crew, argv[2], argv[1]);
if (status != 0)
err_abort (status, "Start crew");
return 0;
}

49
errors.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef __errors_h
#define __errors_h
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* Define a macro that can be used for diagnostic output from
* examples. When compiled -DDEBUG, it results in calling printf
* with the specified argument list. When DEBUG is not defined, it
* expands to nothing.
*/
#ifdef DEBUG
# define DPRINTF(arg) printf arg
#else
# define DPRINTF(arg)
#endif
/*
* NOTE: the "do {" ... "} while (0);" bracketing around the macros
* allows the err_abort and errno_abort macros to be used as if they
* were function calls, even in contexts where a trailing ";" would
* generate a null statement. For example,
*
* if (status != 0)
* err_abort (status, "message");
* else
* return status;
*
* will not compile if err_abort is a macro ending with "}", because
* C does not expect a ";" to follow the "}". Because C does expect
* a ";" following the ")" in the do...while construct, err_abort and
* errno_abort can be used as if they were function calls.
*/
#define err_abort(code,text) do { \
fprintf (stderr, "%s at \"%s\":%d: %s\n", \
text, __FILE__, __LINE__, strerror (code)); \
abort (); \
} while (0)
#define errno_abort(text) do { \
fprintf (stderr, "%s at \"%s\":%d: %s\n", \
text, __FILE__, __LINE__, strerror (errno)); \
abort (); \
} while (0)
#endif

84
flock.c Normal file
View File

@@ -0,0 +1,84 @@
/*
* flock.c
*
* Demonstrate use of stdio file locking to generate an "atomic"
* prompting sequence. The write to stdout and the read from
* stdin cannot be separated.
*/
#include <pthread.h>
#include "errors.h"
/*
* This routine writes a prompt to stdout (passed as the thread's
* "arg"), and reads a response. All other I/O to stdin and stdout
* is prevented by the file locks until both prompt and fgets are
* complete.
*/
void *prompt_routine (void *arg)
{
char *prompt = (char*)arg;
char *string;
int len;
string = (char*)malloc (128);
if (string == NULL)
errno_abort ("Alloc string");
flockfile (stdin);
flockfile (stdout);
printf (prompt);
if (fgets (string, 128, stdin) == NULL)
string[0] = '\0';
else {
len = strlen (string);
if (len > 0 && string[len-1] == '\n')
string[len-1] = '\0';
}
funlockfile (stdout);
funlockfile (stdin);
return (void*)string;
}
int main (int argc, char *argv[])
{
pthread_t thread1, thread2, thread3;
char *string;
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 4\n"));
thr_setconcurrency (4);
#endif
status = pthread_create (
&thread1, NULL, prompt_routine, "Thread 1> ");
if (status != 0)
err_abort (status, "Create thread");
status = pthread_create (
&thread2, NULL, prompt_routine, "Thread 2> ");
if (status != 0)
err_abort (status, "Create thread");
status = pthread_create (
&thread3, NULL, prompt_routine, "Thread 3> ");
if (status != 0)
err_abort (status, "Create thread");
status = pthread_join (thread1, (void**)&string);
if (status != 0)
err_abort (status, "Join thread");
printf ("Thread 1: \"%s\"\n", string);
free (string);
status = pthread_join (thread2, (void**)&string);
if (status != 0)
err_abort (status, "Join thread");
printf ("Thread 1: \"%s\"\n", string);
free (string);
status = pthread_join (thread3, (void**)&string);
if (status != 0)
err_abort (status, "Join thread");
printf ("Thread 1: \"%s\"\n", string);
free (string);
return 0;
}

42
getlogin.c Normal file
View File

@@ -0,0 +1,42 @@
/*
* getlogin.c
*
* Print information on login name and terminal, to demonstrate
* the thread-safe functions.
*/
#include <limits.h>
#include "errors.h"
/*
* If either TTY_NAME_MAX or LOGIN_NAME_MAX are undefined
* (this means they are "indeterminate" values), assume a
* reasonable size (for simplicity) rather than using sysconf
* and dynamically allocating the buffers.
*/
#ifndef TTY_NAME_MAX
# define TTY_NAME_MAX 128
#endif
#ifndef LOGIN_NAME_MAX
# define LOGIN_NAME_MAX 32
#endif
int main (int argc, char *argv[])
{
char login_str[LOGIN_NAME_MAX];
char stdin_str[TTY_NAME_MAX];
char cterm_str[L_ctermid], *cterm_str_ptr;
int status;
status = getlogin_r (login_str, sizeof (login_str));
if (status != 0)
err_abort (status, "Get login");
cterm_str_ptr = ctermid (cterm_str);
if (cterm_str_ptr == NULL)
errno_abort ("Get cterm");
status = ttyname_r (0, stdin_str, sizeof (stdin_str));
if (status != 0)
err_abort (status, "Get stdin");
printf ("User: %s, cterm: %s, fd 0: %s\n",
login_str, cterm_str, stdin_str);
return 0;
}

28
hello.c Normal file
View File

@@ -0,0 +1,28 @@
/*
* hello.c
*
* Create a very simple thread that says "Hello world", just
* because it has to be here.
*/
#include <pthread.h>
#include "errors.h"
void *hello_world (void *arg)
{
printf ("Hello world\n");
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t hello_id;
int status;
status = pthread_create (&hello_id, NULL, hello_world, NULL);
if (status != 0)
err_abort (status, "Create thread");
status = pthread_join (hello_id, NULL);
if (status != 0)
err_abort (status, "Join thread");
return 0;
}

56
inertia.c Normal file
View File

@@ -0,0 +1,56 @@
/*
* inertia.c
*
* Demonstrate a class of programming error, assuming that
* threads can't start right away.
*
* Special notes: On a Solaris 2.5 uniprocessor, this test can't
* fail unless a second LWP is created by calling
* thr_setconcurrency() because threads are not timesliced.
*/
#include <pthread.h>
#include "errors.h"
void *printer_thread (void *arg)
{
char *string = *(char**)arg;
printf ("%s\n", string);
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t printer_id;
char *string_ptr;
int i, status;
#ifdef sun
/*
* On Solaris 2.5, threads are not timesliced. To ensure
* that our two threads can run concurrently, we need to
* increase the concurrency level to 2.
*/
DPRINTF (("Setting concurrency level to 2\n"));
thr_setconcurrency (2);
#endif
string_ptr = "Before value";
status = pthread_create (
&printer_id, NULL, printer_thread, (void*)&string_ptr);
if (status != 0)
err_abort (status, "Create thread");
/*
* Give the thread a chance to get started if it's going to run
* in parallel, but not enough that the current thread is likely
* to be timesliced. (This is a tricky balance, and the loop may
* need to be adjusted on your system before you can see the bug.)
*/
for (i = 0; i < 10000000; i++);
string_ptr = "After value";
status = pthread_join (printer_id, NULL);
if (status != 0)
err_abort (status, "Join thread");
return 0;
}

36
lifecycle.c Normal file
View File

@@ -0,0 +1,36 @@
/*
* lifecycle.c
*
* Demonstrate the "life cycle" of a typical thread. A thread is
* created, and then joined.
*/
#include <pthread.h>
#include "errors.h"
/*
* Thread start routine.
*/
void *thread_routine (void *arg)
{
return arg;
}
main (int argc, char *argv[])
{
pthread_t thread_id;
void *thread_result;
int status;
status = pthread_create (
&thread_id, NULL, thread_routine, NULL);
if (status != 0)
err_abort (status, "Create thread");
status = pthread_join (thread_id, &thread_result);
if (status != 0)
err_abort (status, "Join thread");
if (thread_result == NULL)
return 0;
else
return 1;
}

35
mutex_attr.c Normal file
View File

@@ -0,0 +1,35 @@
/*
* mutex_attr.c
*
* Create a mutex using a non-default attributes object,
* mutex_attr. If the implementation supports the pshared
* attribute, the mutex is created "process private" so that it
* could be used to synchronize between threads in separate
* address spaces. (Note that, to create a "process shared"
* mutex, the pthread_mutex_t itself must be placed in shared
* memory that is accessible to all threads using the mutex.)
*/
#include <pthread.h>
#include "errors.h"
pthread_mutex_t mutex;
int main (int argc, char *argv[])
{
pthread_mutexattr_t mutex_attr;
int status;
status = pthread_mutexattr_init (&mutex_attr);
if (status != 0)
err_abort (status, "Create attr");
#ifdef _POSIX_THREAD_PROCESS_SHARED
status = pthread_mutexattr_setpshared (
&mutex_attr, PTHREAD_PROCESS_PRIVATE);
if (status != 0)
err_abort (status, "Set pshared");
#endif
status = pthread_mutex_init (&mutex, &mutex_attr);
if (status != 0)
err_abort (status, "Init mutex");
return 0;
}

33
mutex_dynamic.c Normal file
View 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
mutex_static.c Normal file
View 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;
}

71
once.c Normal file
View File

@@ -0,0 +1,71 @@
/*
* once.c
*
* Demonstrate the use of pthread_once() one-time
* initialization.
*/
#include <pthread.h>
#include "errors.h"
pthread_once_t once_block = PTHREAD_ONCE_INIT;
pthread_mutex_t mutex;
/*
* This is the one-time initialization routine. It will be
* called exactly once, no matter how many calls to pthread_once
* with the same control structure are made during the course of
* the program.
*/
void once_init_routine (void)
{
int status;
status = pthread_mutex_init (&mutex, NULL);
if (status != 0)
err_abort (status, "Init Mutex");
}
/*
* Thread start routine that calls pthread_once.
*/
void *thread_routine (void *arg)
{
int status;
status = pthread_once (&once_block, once_init_routine);
if (status != 0)
err_abort (status, "Once init");
status = pthread_mutex_lock (&mutex);
if (status != 0)
err_abort (status, "Lock mutex");
printf ("thread_routine has locked the mutex.\n");
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t thread_id;
char *input, buffer[64];
int status;
status = pthread_create (&thread_id, NULL, thread_routine, NULL);
if (status != 0)
err_abort (status, "Create thread");
status = pthread_once (&once_block, once_init_routine);
if (status != 0)
err_abort (status, "Once init");
status = pthread_mutex_lock (&mutex);
if (status != 0)
err_abort (status, "Lock mutex");
printf ("Main has locked the mutex.\n");
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
status = pthread_join (thread_id, NULL);
if (status != 0)
err_abort (status, "Join thread");
return 0;
}

263
pipe.c Normal file
View File

@@ -0,0 +1,263 @@
/*
* pipe.c
*
* Simple demonstration of a pipeline. main() is a loop that
* feeds the pipeline with integer values. Each stage of the
* pipeline increases the integer by one before passing it along
* to the next. Entering the command "=" reads the pipeline
* result. (Notice that too many '=' commands will hang.)
*/
#include <pthread.h>
#include "errors.h"
/*
* Internal structure describing a "stage" in the
* pipeline. One for each thread, plus a "result
* stage" where the final thread can stash the value.
*/
typedef struct stage_tag {
pthread_mutex_t mutex; /* Protect data */
pthread_cond_t avail; /* Data available */
pthread_cond_t ready; /* Ready for data */
int data_ready; /* Data present */
long data; /* Data to process */
pthread_t thread; /* Thread for stage */
struct stage_tag *next; /* Next stage */
} stage_t;
/*
* External structure representing the entire
* pipeline.
*/
typedef struct pipe_tag {
pthread_mutex_t mutex; /* Mutex to protect pipe */
stage_t *head; /* First stage */
stage_t *tail; /* Final stage */
int stages; /* Number of stages */
int active; /* Active data elements */
} pipe_t;
/*
* Internal function to send a "message" to the
* specified pipe stage. Threads use this to pass
* along the modified data item.
*/
int pipe_send (stage_t *stage, long data)
{
int status;
status = pthread_mutex_lock (&stage->mutex);
if (status != 0)
return status;
/*
* If there's data in the pipe stage, wait for it
* to be consumed.
*/
while (stage->data_ready) {
status = pthread_cond_wait (&stage->ready, &stage->mutex);
if (status != 0) {
pthread_mutex_unlock (&stage->mutex);
return status;
}
}
/*
* Send the new data
*/
stage->data = data;
stage->data_ready = 1;
status = pthread_cond_signal (&stage->avail);
if (status != 0) {
pthread_mutex_unlock (&stage->mutex);
return status;
}
status = pthread_mutex_unlock (&stage->mutex);
return status;
}
/*
* The thread start routine for pipe stage threads.
* Each will wait for a data item passed from the
* caller or the previous stage, modify the data
* and pass it along to the next (or final) stage.
*/
void *pipe_stage (void *arg)
{
stage_t *stage = (stage_t*)arg;
stage_t *next_stage = stage->next;
int status;
status = pthread_mutex_lock (&stage->mutex);
if (status != 0)
err_abort (status, "Lock pipe stage");
while (1) {
while (stage->data_ready != 1) {
status = pthread_cond_wait (&stage->avail, &stage->mutex);
if (status != 0)
err_abort (status, "Wait for previous stage");
}
pipe_send (next_stage, stage->data + 1);
stage->data_ready = 0;
status = pthread_cond_signal (&stage->ready);
if (status != 0)
err_abort (status, "Wake next stage");
}
/*
* Notice that the routine never unlocks the stage->mutex.
* The call to pthread_cond_wait implicitly unlocks the
* mutex while the thread is waiting, allowing other threads
* to make progress. Because the loop never terminates, this
* function has no need to unlock the mutex explicitly.
*/
}
/*
* External interface to create a pipeline. All the
* data is initialized and the threads created. They'll
* wait for data.
*/
int pipe_create (pipe_t *pipe, int stages)
{
int pipe_index;
stage_t **link = &pipe->head, *new_stage, *stage;
int status;
status = pthread_mutex_init (&pipe->mutex, NULL);
if (status != 0)
err_abort (status, "Init pipe mutex");
pipe->stages = stages;
pipe->active = 0;
for (pipe_index = 0; pipe_index <= stages; pipe_index++) {
new_stage = (stage_t*)malloc (sizeof (stage_t));
if (new_stage == NULL)
errno_abort ("Allocate stage");
status = pthread_mutex_init (&new_stage->mutex, NULL);
if (status != 0)
err_abort (status, "Init stage mutex");
status = pthread_cond_init (&new_stage->avail, NULL);
if (status != 0)
err_abort (status, "Init avail condition");
status = pthread_cond_init (&new_stage->ready, NULL);
if (status != 0)
err_abort (status, "Init ready condition");
new_stage->data_ready = 0;
*link = new_stage;
link = &new_stage->next;
}
*link = (stage_t*)NULL; /* Terminate list */
pipe->tail = new_stage; /* Record the tail */
/*
* Create the threads for the pipe stages only after all
* the data is initialized (including all links). Note
* that the last stage doesn't get a thread, it's just
* a receptacle for the final pipeline value.
*
* At this point, proper cleanup on an error would take up
* more space than worthwhile in a "simple example", so
* instead of cancelling and detaching all the threads
* already created, plus the synchronization object and
* memory cleanup done for earlier errors, it will simply
* abort.
*/
for ( stage = pipe->head;
stage->next != NULL;
stage = stage->next) {
status = pthread_create (
&stage->thread, NULL, pipe_stage, (void*)stage);
if (status != 0)
err_abort (status, "Create pipe stage");
}
return 0;
}
/*
* External interface to start a pipeline by passing
* data to the first stage. The routine returns while
* the pipeline processes in parallel. Call the
* pipe_result return to collect the final stage values
* (note that the pipe will stall when each stage fills,
* until the result is collected).
*/
int pipe_start (pipe_t *pipe, long value)
{
int status;
status = pthread_mutex_lock (&pipe->mutex);
if (status != 0)
err_abort (status, "Lock pipe mutex");
pipe->active++;
status = pthread_mutex_unlock (&pipe->mutex);
if (status != 0)
err_abort (status, "Unlock pipe mutex");
pipe_send (pipe->head, value);
return 0;
}
/*
* Collect the result of the pipeline. Wait for a
* result if the pipeline hasn't produced one.
*/
int pipe_result (pipe_t *pipe, long *result)
{
stage_t *tail = pipe->tail;
long value;
int empty = 0;
int status;
status = pthread_mutex_lock (&pipe->mutex);
if (status != 0)
err_abort (status, "Lock pipe mutex");
if (pipe->active <= 0)
empty = 1;
else
pipe->active--;
status = pthread_mutex_unlock (&pipe->mutex);
if (status != 0)
err_abort (status, "Unlock pipe mutex");
if (empty)
return 0;
pthread_mutex_lock (&tail->mutex);
while (!tail->data_ready)
pthread_cond_wait (&tail->avail, &tail->mutex);
*result = tail->data;
tail->data_ready = 0;
pthread_cond_signal (&tail->ready);
pthread_mutex_unlock (&tail->mutex);
return 1;
}
/*
* The main program to "drive" the pipeline...
*/
int main (int argc, char *argv[])
{
pipe_t my_pipe;
long value, result;
int status;
char line[128];
pipe_create (&my_pipe, 10);
printf ("Enter integer values, or \"=\" for next result\n");
while (1) {
printf ("Data> ");
if (fgets (line, sizeof (line), stdin) == NULL) exit (0);
if (strlen (line) <= 1) continue;
if (strlen (line) <= 2 && line[0] == '=') {
if (pipe_result (&my_pipe, &result))
printf ("Result is %ld\n", result);
else
printf ("Pipe is empty\n");
} else {
if (sscanf (line, "%ld", &value) < 1)
fprintf (stderr, "Enter an integer value\n");
else
pipe_start (&my_pipe, value);
}
}
}

96
putchar.c Normal file
View File

@@ -0,0 +1,96 @@
/*
* putchar.c
*
* Demonstrate use of stdio file locking to generate an "atomic"
* sequence of character writes (using putchar). If run with an
* argument of "1", or no argument, the program uses a sequence
* of putchar_unlocked calls within flockfile/funlockfile to
* ensure that one threads writes cannot be interleaved with
* another's.
*
* With an argument of "0", the program uses putchar, without
* file locks, to show that the writes may be interleaved.
*
* The putchar[_unlocked] loop is punctuated with sleep(1) calls
* to ensure that the desired behavior is demonstrated. Without
* some delay, even on a multiprocessor the program may often
* fail to display the interleaved output in this simplified
* case.
*
* With file locking, you can expect to see the following output:
*
* thread 1
* thread 2
* thread 3
*
* While without file locking, you can expect to see something
* much less predictable, but probably resembling this:
*
* ttthhhiiisss iiisss ttthhhrrreeeaaaddd 123
*
*/
#include <pthread.h>
#include "errors.h"
/*
* This function writes a string (the function's arg) to stdout,
* by locking the file stream and using putchar_unlocked to
* write each character individually.
*/
void *lock_routine (void *arg)
{
char *pointer;
flockfile (stdout);
for (pointer = arg; *pointer != '\0'; pointer++) {
putchar_unlocked (*pointer);
sleep (1);
}
funlockfile (stdout);
return NULL;
}
/*
* This function writes a string (the function's arg) to stdout,
* by using putchar to write each character individually.
* Although the internal locking of putchar prevents file stream
* corruption, the writes of various threads may be interleaved.
*/
void *unlock_routine (void *arg)
{
char *pointer;
for (pointer = arg; *pointer != '\0'; pointer++) {
putchar (*pointer);
sleep (1);
}
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t thread1, thread2, thread3;
int flock_flag = 1;
void *(*thread_func)(void *);
int status;
if (argc > 1)
flock_flag = atoi (argv[1]);
if (flock_flag)
thread_func = lock_routine;
else
thread_func = unlock_routine;
status = pthread_create (
&thread1, NULL, thread_func, "this is thread 1\n");
if (status != 0)
err_abort (status, "Create thread");
status = pthread_create (
&thread2, NULL, thread_func, "this is thread 2\n");
if (status != 0)
err_abort (status, "Create thread");
status = pthread_create (
&thread3, NULL, thread_func, "this is thread 3\n");
if (status != 0)
err_abort (status, "Create thread");
pthread_exit (NULL);
}

276
rwlock.c Normal file
View File

@@ -0,0 +1,276 @@
/*
* rwlock.c
*
* This file implements the "read-write lock" synchronization
* construct.
*
* A read-write lock allows a thread to lock shared data either
* for shared read access or exclusive write access.
*
* The rwl_init() and rwl_destroy() functions, respectively,
* allow you to initialize/create and destroy/free the
* read-write lock.
*
* The rwl_readlock() function locks a read-write lock for
* shared read access, and rwl_readunlock() releases the
* lock. rwl_readtrylock() attempts to lock a read-write lock
* for read access, and returns EBUSY instead of blocking.
*
* The rwl_writelock() function locks a read-write lock for
* exclusive write access, and rwl_writeunlock() releases the
* lock. rwl_writetrylock() attempts to lock a read-write lock
* for write access, and returns EBUSY instead of blocking.
*/
#include <pthread.h>
#include "errors.h"
#include "rwlock.h"
/*
* Initialize a read-write lock
*/
int rwl_init (rwlock_t *rwl)
{
int status;
rwl->r_active = 0;
rwl->r_wait = rwl->w_wait = 0;
rwl->w_active = 0;
status = pthread_mutex_init (&rwl->mutex, NULL);
if (status != 0)
return status;
status = pthread_cond_init (&rwl->read, NULL);
if (status != 0) {
/* if unable to create read CV, destroy mutex */
pthread_mutex_destroy (&rwl->mutex);
return status;
}
status = pthread_cond_init (&rwl->write, NULL);
if (status != 0) {
/* if unable to create write CV, destroy read CV and mutex */
pthread_cond_destroy (&rwl->read);
pthread_mutex_destroy (&rwl->mutex);
return status;
}
rwl->valid = RWLOCK_VALID;
return 0;
}
/*
* Destroy a read-write lock
*/
int rwl_destroy (rwlock_t *rwl)
{
int status, status1, status2;
if (rwl->valid != RWLOCK_VALID)
return EINVAL;
status = pthread_mutex_lock (&rwl->mutex);
if (status != 0)
return status;
/*
* Check whether any threads own the lock; report "BUSY" if
* so.
*/
if (rwl->r_active > 0 || rwl->w_active) {
pthread_mutex_unlock (&rwl->mutex);
return EBUSY;
}
/*
* Check whether any threads are known to be waiting; report
* EBUSY if so.
*/
if (rwl->r_wait != 0 || rwl->w_wait != 0) {
pthread_mutex_unlock (&rwl->mutex);
return EBUSY;
}
rwl->valid = 0;
status = pthread_mutex_unlock (&rwl->mutex);
if (status != 0)
return status;
status = pthread_mutex_destroy (&rwl->mutex);
status1 = pthread_cond_destroy (&rwl->read);
status2 = pthread_cond_destroy (&rwl->write);
return (status == 0 ? status : (status1 == 0 ? status1 : status2));
}
/*
* Handle cleanup when the read lock condition variable
* wait is cancelled.
*
* Simply record that the thread is no longer waiting,
* and unlock the mutex.
*/
static void rwl_readcleanup (void *arg)
{
rwlock_t *rwl = (rwlock_t *)arg;
rwl->r_wait--;
pthread_mutex_unlock (&rwl->mutex);
}
/*
* Lock a read-write lock for read access.
*/
int rwl_readlock (rwlock_t *rwl)
{
int status;
if (rwl->valid != RWLOCK_VALID)
return EINVAL;
status = pthread_mutex_lock (&rwl->mutex);
if (status != 0)
return status;
if (rwl->w_active) {
rwl->r_wait++;
pthread_cleanup_push (rwl_readcleanup, (void*)rwl);
while (rwl->w_active) {
status = pthread_cond_wait (&rwl->read, &rwl->mutex);
if (status != 0)
break;
}
pthread_cleanup_pop (0);
rwl->r_wait--;
}
if (status == 0)
rwl->r_active++;
pthread_mutex_unlock (&rwl->mutex);
return status;
}
/*
* Attempt to lock a read-write lock for read access (don't
* block if unavailable).
*/
int rwl_readtrylock (rwlock_t *rwl)
{
int status, status2;
if (rwl->valid != RWLOCK_VALID)
return EINVAL;
status = pthread_mutex_lock (&rwl->mutex);
if (status != 0)
return status;
if (rwl->w_active)
status = EBUSY;
else
rwl->r_active++;
status2 = pthread_mutex_unlock (&rwl->mutex);
return (status2 != 0 ? status2 : status);
}
/*
* Unlock a read-write lock from read access.
*/
int rwl_readunlock (rwlock_t *rwl)
{
int status, status2;
if (rwl->valid != RWLOCK_VALID)
return EINVAL;
status = pthread_mutex_lock (&rwl->mutex);
if (status != 0)
return status;
rwl->r_active--;
if (rwl->r_active == 0 && rwl->w_wait > 0)
status = pthread_cond_signal (&rwl->write);
status2 = pthread_mutex_unlock (&rwl->mutex);
return (status2 == 0 ? status : status2);
}
/*
* Handle cleanup when the write lock condition variable
* wait is cancelled.
*
* Simply record that the thread is no longer waiting,
* and unlock the mutex.
*/
static void rwl_writecleanup (void *arg)
{
rwlock_t *rwl = (rwlock_t *)arg;
rwl->w_wait--;
pthread_mutex_unlock (&rwl->mutex);
}
/*
* Lock a read-write lock for write access.
*/
int rwl_writelock (rwlock_t *rwl)
{
int status;
if (rwl->valid != RWLOCK_VALID)
return EINVAL;
status = pthread_mutex_lock (&rwl->mutex);
if (status != 0)
return status;
if (rwl->w_active || rwl->r_active > 0) {
rwl->w_wait++;
pthread_cleanup_push (rwl_writecleanup, (void*)rwl);
while (rwl->w_active || rwl->r_active > 0) {
status = pthread_cond_wait (&rwl->write, &rwl->mutex);
if (status != 0)
break;
}
pthread_cleanup_pop (0);
rwl->w_wait--;
}
if (status == 0)
rwl->w_active = 1;
pthread_mutex_unlock (&rwl->mutex);
return status;
}
/*
* Attempt to lock a read-write lock for write access. Don't
* block if unavailable.
*/
int rwl_writetrylock (rwlock_t *rwl)
{
int status, status2;
if (rwl->valid != RWLOCK_VALID)
return EINVAL;
status = pthread_mutex_lock (&rwl->mutex);
if (status != 0)
return status;
if (rwl->w_active || rwl->r_active > 0)
status = EBUSY;
else
rwl->w_active = 1;
status2 = pthread_mutex_unlock (&rwl->mutex);
return (status != 0 ? status : status2);
}
/*
* Unlock a read-write lock from write access.
*/
int rwl_writeunlock (rwlock_t *rwl)
{
int status;
if (rwl->valid != RWLOCK_VALID)
return EINVAL;
status = pthread_mutex_lock (&rwl->mutex);
if (status != 0)
return status;
rwl->w_active = 0;
if (rwl->r_wait > 0) {
status = pthread_cond_broadcast (&rwl->read);
if (status != 0) {
pthread_mutex_unlock (&rwl->mutex);
return status;
}
} else if (rwl->w_wait > 0) {
status = pthread_cond_signal (&rwl->write);
if (status != 0) {
pthread_mutex_unlock (&rwl->mutex);
return status;
}
}
status = pthread_mutex_unlock (&rwl->mutex);
return status;
}

49
rwlock.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* rwlock.h
*
* This header file describes the "reader/writer lock" synchronization
* construct. The type rwlock_t describes the full state of the lock
* including the POSIX 1003.1c synchronization objects necessary.
*
* A reader/writer lock allows a thread to lock shared data either for shared
* read access or exclusive write access.
*
* The rwl_init() and rwl_destroy() functions, respectively, allow you to
* initialize/create and destroy/free the reader/writer lock.
*/
#include <pthread.h>
/*
* Structure describing a read-write lock.
*/
typedef struct rwlock_tag {
pthread_mutex_t mutex;
pthread_cond_t read; /* wait for read */
pthread_cond_t write; /* wait for write */
int valid; /* set when valid */
int r_active; /* readers active */
int w_active; /* writer active */
int r_wait; /* readers waiting */
int w_wait; /* writers waiting */
} rwlock_t;
#define RWLOCK_VALID 0xfacade
/*
* Support static initialization of barriers
*/
#define RWL_INITIALIZER \
{PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, \
PTHREAD_COND_INITIALIZER, RWLOCK_VALID, 0, 0, 0, 0}
/*
* Define read-write lock functions
*/
extern int rwl_init (rwlock_t *rwlock);
extern int rwl_destroy (rwlock_t *rwlock);
extern int rwl_readlock (rwlock_t *rwlock);
extern int rwl_readtrylock (rwlock_t *rwlock);
extern int rwl_readunlock (rwlock_t *rwlock);
extern int rwl_writelock (rwlock_t *rwlock);
extern int rwl_writetrylock (rwlock_t *rwlock);
extern int rwl_writeunlock (rwlock_t *rwlock);

167
rwlock_main.c Normal file
View File

@@ -0,0 +1,167 @@
/*
* rwlock_main.c
*
* Demonstrate use of read-write locks as implemented by
* rwlock.c
*
* Special notes: On a Solaris system, call thr_setconcurrency()
* to allow interleaved thread execution, since threads are not
* timesliced.
*/
#include "rwlock.h"
#include "errors.h"
#define THREADS 5
#define DATASIZE 15
#define ITERATIONS 10000
/*
* Keep statistics for each thread.
*/
typedef struct thread_tag {
int thread_num;
pthread_t thread_id;
int updates;
int reads;
int interval;
} thread_t;
/*
* Read-write lock and shared data
*/
typedef struct data_tag {
rwlock_t lock;
int data;
int updates;
} data_t;
thread_t threads[THREADS];
data_t data[DATASIZE];
/*
* Thread start routine that uses read-write locks
*/
void *thread_routine (void *arg)
{
thread_t *self = (thread_t*)arg;
int repeats = 0;
int iteration;
int element = 0;
int status;
for (iteration = 0; iteration < ITERATIONS; iteration++) {
/*
* Each "self->interval" iterations, perform an
* update operation (write lock instead of read
* lock).
*/
if ((iteration % self->interval) == 0) {
status = rwl_writelock (&data[element].lock);
if (status != 0)
err_abort (status, "Write lock");
data[element].data = self->thread_num;
data[element].updates++;
self->updates++;
status = rwl_writeunlock (&data[element].lock);
if (status != 0)
err_abort (status, "Write unlock");
} else {
/*
* Look at the current data element to see whether
* the current thread last updated it. Count the
* times, to report later.
*/
status = rwl_readlock (&data[element].lock);
if (status != 0)
err_abort (status, "Read lock");
self->reads++;
if (data[element].data == self->thread_num)
repeats++;
status = rwl_readunlock (&data[element].lock);
if (status != 0)
err_abort (status, "Read unlock");
}
element++;
if (element >= DATASIZE)
element = 0;
}
if (repeats > 0)
printf (
"Thread %d found unchanged elements %d times\n",
self->thread_num, repeats);
return NULL;
}
int main (int argc, char *argv[])
{
int count;
int data_count;
int status;
unsigned int seed = 1;
int thread_updates = 0;
int data_updates = 0;
#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 THREADS.
*/
DPRINTF (("Setting concurrency level to %d\n", THREADS));
thr_setconcurrency (THREADS);
#endif
/*
* Initialize the shared data.
*/
for (data_count = 0; data_count < DATASIZE; data_count++) {
data[data_count].data = 0;
data[data_count].updates = 0;
status = rwl_init (&data[data_count].lock);
if (status != 0)
err_abort (status, "Init rw lock");
}
/*
* Create THREADS threads to access shared data.
*/
for (count = 0; count < THREADS; count++) {
threads[count].thread_num = count;
threads[count].updates = 0;
threads[count].reads = 0;
threads[count].interval = rand_r (&seed) % 71;
status = pthread_create (&threads[count].thread_id,
NULL, thread_routine, (void*)&threads[count]);
if (status != 0)
err_abort (status, "Create thread");
}
/*
* Wait for all threads to complete, and collect
* statistics.
*/
for (count = 0; count < THREADS; count++) {
status = pthread_join (threads[count].thread_id, NULL);
if (status != 0)
err_abort (status, "Join thread");
thread_updates += threads[count].updates;
printf ("%02d: interval %d, updates %d, reads %d\n",
count, threads[count].interval,
threads[count].updates, threads[count].reads);
}
/*
* Collect statistics for the data.
*/
for (data_count = 0; data_count < DATASIZE; data_count++) {
data_updates += data[data_count].updates;
printf ("data %02d: value %d, %d updates\n",
data_count, data[data_count].data, data[data_count].updates);
rwl_destroy (&data[data_count].lock);
}
printf ("%d thread updates, %d data updates\n",
thread_updates, data_updates);
return 0;
}

156
rwlock_try_main.c Normal file
View File

@@ -0,0 +1,156 @@
/*
* rwlock_try_main.c
*
* Demonstrate use of non-blocking read-write locks.
*
* Special notes: On a Solaris system, call thr_setconcurrency()
* to allow interleaved thread execution, since threads are not
* timesliced.
*/
#include <pthread.h>
#include "rwlock.h"
#include "errors.h"
#define THREADS 5
#define ITERATIONS 1000
#define DATASIZE 15
/*
* Keep statistics for each thread.
*/
typedef struct thread_tag {
int thread_num;
pthread_t thread_id;
int r_collisions;
int w_collisions;
int updates;
int interval;
} thread_t;
/*
* Read-write lock and shared data
*/
typedef struct data_tag {
rwlock_t lock;
int data;
int updates;
} data_t;
thread_t threads[THREADS];
data_t data[DATASIZE];
/*
* Thread start routine that uses read-write locks
*/
void *thread_routine (void *arg)
{
thread_t *self = (thread_t*)arg;
int iteration;
int element;
int status;
element = 0; /* Current data element */
for (iteration = 0; iteration < ITERATIONS; iteration++) {
if ((iteration % self->interval) == 0) {
status = rwl_writetrylock (&data[element].lock);
if (status == EBUSY)
self->w_collisions++;
else if (status == 0) {
data[element].data++;
data[element].updates++;
self->updates++;
rwl_writeunlock (&data[element].lock);
} else
err_abort (status, "Try write lock");
} else {
status = rwl_readtrylock (&data[element].lock);
if (status == EBUSY)
self->r_collisions++;
else if (status != 0) {
err_abort (status, "Try read lock");
} else {
if (data[element].data != data[element].updates)
printf ("%d: data[%d] %d != %d\n",
self->thread_num, element,
data[element].data, data[element].updates);
rwl_readunlock (&data[element].lock);
}
}
element++;
if (element >= DATASIZE)
element = 0;
}
return NULL;
}
int main (int argc, char *argv[])
{
int count, data_count;
unsigned int seed = 1;
int thread_updates = 0, data_updates = 0;
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 to THREADS.
*/
DPRINTF (("Setting concurrency level to %d\n", THREADS));
thr_setconcurrency (THREADS);
#endif
/*
* Initialize the shared data.
*/
for (data_count = 0; data_count < DATASIZE; data_count++) {
data[data_count].data = 0;
data[data_count].updates = 0;
rwl_init (&data[data_count].lock);
}
/*
* Create THREADS threads to access shared data.
*/
for (count = 0; count < THREADS; count++) {
threads[count].thread_num = count;
threads[count].r_collisions = 0;
threads[count].w_collisions = 0;
threads[count].updates = 0;
threads[count].interval = rand_r (&seed) % ITERATIONS;
status = pthread_create (&threads[count].thread_id,
NULL, thread_routine, (void*)&threads[count]);
if (status != 0)
err_abort (status, "Create thread");
}
/*
* Wait for all threads to complete, and collect
* statistics.
*/
for (count = 0; count < THREADS; count++) {
status = pthread_join (threads[count].thread_id, NULL);
if (status != 0)
err_abort (status, "Join thread");
thread_updates += threads[count].updates;
printf ("%02d: interval %d, updates %d, "
"r_collisions %d, w_collisions %d\n",
count, threads[count].interval,
threads[count].updates,
threads[count].r_collisions, threads[count].w_collisions);
}
/*
* Collect statistics for the data.
*/
for (data_count = 0; data_count < DATASIZE; data_count++) {
data_updates += data[data_count].updates;
printf ("data %02d: value %d, %d updates\n",
data_count, data[data_count].data, data[data_count].updates);
rwl_destroy (&data[data_count].lock);
}
return 0;
}

139
sched_attr.c Normal file
View File

@@ -0,0 +1,139 @@
/*
* sched_attr.c
*
* Demonstrate use of POSIX 1003.1c-1995 thread priority
* scheduling attributes, by creating an attributes object with
* realtime scheduling policy and priority.
*
* Special notes: Although Solaris 2.5 defines
* _POSIX_THREAD_PRIORITY_SCHEDULING, it does not support the
* SCHED_RR policy for threads.
*/
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
#include "errors.h"
/*
* Thread start routine. If priority scheduling is supported,
* report the thread's scheduling attributes.
*/
void *thread_routine (void *arg)
{
int my_policy;
struct sched_param my_param;
int status;
/*
* If the priority scheduling option is not defined, then we
* can do nothing with the output of pthread_getschedparam,
* so just report that the thread ran, and exit.
*/
#if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) && !defined (sun)
status = pthread_getschedparam (
pthread_self (), &my_policy, &my_param);
if (status != 0)
err_abort (status, "Get sched");
printf ("thread_routine running at %s/%d\n",
(my_policy == SCHED_FIFO ? "FIFO"
: (my_policy == SCHED_RR ? "RR"
: (my_policy == SCHED_OTHER ? "OTHER"
: "unknown"))),
my_param.sched_priority);
#else
printf ("thread_routine running\n");
#endif
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t thread_id;
pthread_attr_t thread_attr;
int thread_policy;
struct sched_param thread_param;
int status, rr_min_priority, rr_max_priority;
status = pthread_attr_init (&thread_attr);
if (status != 0)
err_abort (status, "Init attr");
/*
* If the priority scheduling option is defined, set various scheduling
* parameters. Note that it is particularly important that you remember
* to set the inheritsched attribute to PTHREAD_EXPLICIT_SCHED, or the
* policy and priority that you've set will be ignored! The default
* behavior is to inherit scheduling information from the creating
* thread.
*/
#if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) && !defined (sun)
status = pthread_attr_getschedpolicy (
&thread_attr, &thread_policy);
if (status != 0)
err_abort (status, "Get policy");
status = pthread_attr_getschedparam (
&thread_attr, &thread_param);
if (status != 0)
err_abort (status, "Get sched param");
printf (
"Default policy is %s, priority is %d\n",
(thread_policy == SCHED_FIFO ? "FIFO"
: (thread_policy == SCHED_RR ? "RR"
: (thread_policy == SCHED_OTHER ? "OTHER"
: "unknown"))),
thread_param.sched_priority);
status = pthread_attr_setschedpolicy (
&thread_attr, SCHED_RR);
if (status != 0)
printf ("Unable to set SCHED_RR policy.\n");
else {
/*
* Just for the sake of the exercise, we'll use the
* middle of the priority range allowed for
* SCHED_RR. This should ensure that the thread will be
* run, without blocking everything else. Because any
* assumptions about how a thread's priority interacts
* with other threads (even in other processes) are
* nonportable, especially on an implementation that
* defaults to System contention scope, you may have to
* adjust this code before it will work on some systems.
*/
rr_min_priority = sched_get_priority_min (SCHED_RR);
if (rr_min_priority == -1)
errno_abort ("Get SCHED_RR min priority");
rr_max_priority = sched_get_priority_max (SCHED_RR);
if (rr_max_priority == -1)
errno_abort ("Get SCHED_RR max priority");
thread_param.sched_priority =
(rr_min_priority + rr_max_priority)/2;
printf (
"SCHED_RR priority range is %d to %d: using %d\n",
rr_min_priority,
rr_max_priority,
thread_param.sched_priority);
status = pthread_attr_setschedparam (
&thread_attr, &thread_param);
if (status != 0)
err_abort (status, "Set params");
printf (
"Creating thread at RR/%d\n",
thread_param.sched_priority);
status = pthread_attr_setinheritsched (
&thread_attr, PTHREAD_EXPLICIT_SCHED);
if (status != 0)
err_abort (status, "Set inherit");
}
#else
printf ("Priority scheduling not supported\n");
#endif
status = pthread_create (
&thread_id, &thread_attr, thread_routine, NULL);
if (status != 0)
err_abort (status, "Create thread");
status = pthread_join (thread_id, NULL);
if (status != 0)
err_abort (status, "Join thread");
printf ("Main exiting\n");
return 0;
}

88
sched_thread.c Normal file
View File

@@ -0,0 +1,88 @@
/*
* sched_thread.c
*
* Demonstrate dynamic scheduling policy use.
*
* Special note: This demonstration will fail on Solaris 2.5
* because it does not implement SCHED_RR.
*/
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
#include "errors.h"
#define THREADS 5
/*
* Structure describing each thread.
*/
typedef struct thread_tag {
int index;
pthread_t id;
} thread_t;
thread_t threads[THREADS];
int rr_min_priority;
/*
* Thread start routine that will set its own priority
*/
void *thread_routine (void *arg)
{
thread_t *self = (thread_t*)arg;
int my_policy;
struct sched_param my_param;
int status;
my_param.sched_priority = rr_min_priority + self->index;
DPRINTF ((
"Thread %d will set SCHED_FIFO, priority %d\n",
self->index, my_param.sched_priority));
status = pthread_setschedparam (
self->id, SCHED_RR, &my_param);
if (status != 0)
err_abort (status, "Set sched");
status = pthread_getschedparam (
self->id, &my_policy, &my_param);
if (status != 0)
err_abort (status, "Get sched");
printf ("thread_routine %d running at %s/%d\n",
self->index,
(my_policy == SCHED_FIFO ? "FIFO"
: (my_policy == SCHED_RR ? "RR"
: (my_policy == SCHED_OTHER ? "OTHER"
: "unknown"))),
my_param.sched_priority);
return NULL;
}
int main (int argc, char *argv[])
{
int count, status;
rr_min_priority = sched_get_priority_min (SCHED_RR);
if (rr_min_priority == -1) {
#ifdef sun
if (errno == ENOSYS) {
fprintf (stderr, "SCHED_RR is not supported.\n");
exit (0);
}
#endif
errno_abort ("Get SCHED_RR min priority");
}
for (count = 0; count < THREADS; count++) {
threads[count].index = count;
status = pthread_create (
&threads[count].id, NULL,
thread_routine, (void*)&threads[count]);
if (status != 0)
err_abort (status, "Create thread");
}
for (count = 0; count < THREADS; count++) {
status = pthread_join (threads[count].id, NULL);
if (status != 0)
err_abort (status, "Join thread");
}
printf ("Main exiting\n");
return 0;
}

116
semaphore_signal.c Normal file
View File

@@ -0,0 +1,116 @@
/*
* semaphore_signal.c
*
* Demonstrate use of POSIX semaphores to wake a thread from
* within a signal catching function.
*
* Special notes: This program depends upon the _POSIX_TIMERS
* and _POSIX_SEMAPHORES features of POSIX 1003.1b-1993. It will
* not run (and may not compile) without them.
*/
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <time.h>
#include "errors.h"
sem_t semaphore;
/*
* Signal catching function.
*/
void signal_catcher (int sig)
{
if (sem_post (&semaphore) == -1)
errno_abort ("Post semaphore");
}
/*
* Thread start routine which waits on the semaphore.
*/
void *sem_waiter (void *arg)
{
int number = (int)arg;
int counter;
/*
* Each thread waits 5 times.
*/
for (counter = 1; counter <= 5; counter++) {
while (sem_wait (&semaphore) == -1) {
if (errno != EINTR)
errno_abort ("Wait on semaphore");
}
printf ("%d waking (%d)...\n", number, counter);
}
return NULL;
}
int main (int argc, char *argv[])
{
int thread_count, status;
struct sigevent sig_event;
struct sigaction sig_action;
sigset_t sig_mask;
timer_t timer_id;
struct itimerspec timer_val;
pthread_t sem_waiters[5];
#if !defined(_POSIX_SEMAPHORES) || !defined(_POSIX_TIMERS)
# if !defined(_POSIX_SEMAPHORES)
printf ("This system does not support POSIX semaphores\n");
# endif
# if !defined(_POSIX_TIMERS)
printf ("This system does not support POSIX timers\n");
# endif
return -1;
#else
sem_init (&semaphore, 0, 0);
/*
* Create 5 threads to wait on a semaphore.
*/
for (thread_count = 0; thread_count < 5; thread_count++) {
status = pthread_create (
&sem_waiters[thread_count], NULL,
sem_waiter, (void*)thread_count);
if (status != 0)
err_abort (status, "Create thread");
}
/*
* Set up a repeating timer using signal number SIGRTMIN,
* set to occur every 2 seconds.
*/
sig_event.sigev_value.sival_int = 0;
sig_event.sigev_signo = SIGRTMIN;
sig_event.sigev_notify = SIGEV_SIGNAL;
if (timer_create (CLOCK_REALTIME, &sig_event, &timer_id) == -1)
errno_abort ("Create timer");
sigemptyset (&sig_mask);
sigaddset (&sig_mask, SIGRTMIN);
sig_action.sa_handler = signal_catcher;
sig_action.sa_mask = sig_mask;
sig_action.sa_flags = 0;
if (sigaction (SIGRTMIN, &sig_action, NULL) == -1)
errno_abort ("Set signal action");
timer_val.it_interval.tv_sec = 2;
timer_val.it_interval.tv_nsec = 0;
timer_val.it_value.tv_sec = 2;
timer_val.it_value.tv_nsec = 0;
if (timer_settime (timer_id, 0, &timer_val, NULL) == -1)
errno_abort ("Set timer");
/*
* Wait for all threads to complete.
*/
for (thread_count = 0; thread_count < 5; thread_count++) {
status = pthread_join (sem_waiters[thread_count], NULL);
if (status != 0)
err_abort (status, "Join thread");
}
return 0;
#endif
}

82
semaphore_wait.c Normal file
View File

@@ -0,0 +1,82 @@
/*
* semaphore_wait.c
*
* Demonstrate use of semaphores for synchronization.
*/
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include "errors.h"
sem_t semaphore;
/*
* Thread start routine to wait on a semaphore.
*/
void *sem_waiter (void *arg)
{
long num = (long)arg;
printf ("Thread %d waiting\n", num);
if (sem_wait (&semaphore) == -1)
errno_abort ("Wait on semaphore");
printf ("Thread %d resuming\n", num);
return NULL;
}
int main (int argc, char *argv[])
{
int thread_count;
pthread_t sem_waiters[5];
int status;
#if !defined(_POSIX_SEMAPHORES)
printf ("This system does not support POSIX semaphores\n");
return -1;
#else
if (sem_init (&semaphore, 0, 0) == -1)
errno_abort ("Init semaphore");
/*
* Create 5 threads to wait concurrently on the semaphore.
*/
for (thread_count = 0; thread_count < 5; thread_count++) {
status = pthread_create (
&sem_waiters[thread_count], NULL,
sem_waiter, (void*)thread_count);
if (status != 0)
err_abort (status, "Create thread");
}
sleep (2);
/*
* "Broadcast" the semaphore by repeatedly posting until the
* count of waiters goes to 0.
*/
while (1) {
int sem_value;
if (sem_getvalue (&semaphore, &sem_value) == -1)
errno_abort ("Get semaphore value");
if (sem_value >= 0)
break;
printf ("Posting from main: %d\n", sem_value);
if (sem_post (&semaphore) == -1)
errno_abort ("Post semaphore");
}
/*
* Wait for all threads to complete.
*/
for (thread_count = 0; thread_count < 5; thread_count++) {
status = pthread_join (sem_waiters[thread_count], NULL);
if (status != 0)
err_abort (status, "Join thread");
}
return 0;
#endif
}

328
server.c Normal file
View File

@@ -0,0 +1,328 @@
/*
* server.c
*
* Demonstrate a client/server threading model.
*
* Special notes: On a Solaris system, call thr_setconcurrency()
* to allow interleaved thread execution, since threads are not
* timesliced.
*/
#include <pthread.h>
#include <math.h>
#include "errors.h"
#define CLIENT_THREADS 4 /* Number of clients */
#define REQ_READ 1 /* Read with prompt */
#define REQ_WRITE 2 /* Write */
#define REQ_QUIT 3 /* Quit server */
/*
* Internal to server "package" -- one for each request.
*/
typedef struct request_tag {
struct request_tag *next; /* Link to next */
int operation; /* Function code */
int synchronous; /* Non-zero if synchronous */
int done_flag; /* Predicate for wait */
pthread_cond_t done; /* Wait for completion */
char prompt[32]; /* Prompt string for reads */
char text[128]; /* Read/write text */
} request_t;
/*
* Static context for the server
*/
typedef struct tty_server_tag {
request_t *first;
request_t *last;
int running;
pthread_mutex_t mutex;
pthread_cond_t request;
} tty_server_t;
tty_server_t tty_server = {
NULL, NULL, 0,
PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER};
/*
* Main program data
*/
int client_threads;
pthread_mutex_t client_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t clients_done = PTHREAD_COND_INITIALIZER;
/*
* The server start routine. It waits for a request to appear
* in tty_server.requests using the request condition variable.
* It processes requests in FIFO order. If a request is marked
* "synchronous" (synchronous != 0), the server will set done_flag
* and signal the request's condition variable. The client is
* responsible for freeing the request. If the request was not
* synchronous, the server will free the request on completion.
*/
void *tty_server_routine (void *arg)
{
static pthread_mutex_t prompt_mutex = PTHREAD_MUTEX_INITIALIZER;
request_t *request;
int operation, len;
int status;
while (1) {
status = pthread_mutex_lock (&tty_server.mutex);
if (status != 0)
err_abort (status, "Lock server mutex");
/*
* Wait for data
*/
while (tty_server.first == NULL) {
status = pthread_cond_wait (
&tty_server.request, &tty_server.mutex);
if (status != 0)
err_abort (status, "Wait for request");
}
request = tty_server.first;
tty_server.first = request->next;
if (tty_server.first == NULL)
tty_server.last = NULL;
status = pthread_mutex_unlock (&tty_server.mutex);
if (status != 0)
err_abort (status, "Unlock server mutex");
/*
* Process the data
*/
operation = request->operation;
switch (operation) {
case REQ_QUIT:
break;
case REQ_READ:
if (strlen (request->prompt) > 0)
printf (request->prompt);
if (fgets (request->text, 128, stdin) == NULL)
request->text[0] = '\0';
/*
* Because fgets returns the newline, and we don't want it,
* we look for it, and turn it into a null (truncating the
* input) if found. It should be the last character, if it is
* there.
*/
len = strlen (request->text);
if (len > 0 && request->text[len-1] == '\n')
request->text[len-1] = '\0';
break;
case REQ_WRITE:
puts (request->text);
break;
default:
break;
}
if (request->synchronous) {
status = pthread_mutex_lock (&tty_server.mutex);
if (status != 0)
err_abort (status, "Lock server mutex");
request->done_flag = 1;
status = pthread_cond_signal (&request->done);
if (status != 0)
err_abort (status, "Signal server condition");
status = pthread_mutex_unlock (&tty_server.mutex);
if (status != 0)
err_abort (status, "Unlock server mutex");
} else
free (request);
if (operation == REQ_QUIT)
break;
}
return NULL;
}
/*
* Request an operation
*/
void tty_server_request (
int operation,
int sync,
const char *prompt,
char *string)
{
request_t *request;
int status;
status = pthread_mutex_lock (&tty_server.mutex);
if (status != 0)
err_abort (status, "Lock server mutex");
if (!tty_server.running) {
pthread_t thread;
pthread_attr_t detached_attr;
status = pthread_attr_init (&detached_attr);
if (status != 0)
err_abort (status, "Init attributes object");
status = pthread_attr_setdetachstate (
&detached_attr, PTHREAD_CREATE_DETACHED);
if (status != 0)
err_abort (status, "Set detach state");
tty_server.running = 1;
status = pthread_create (&thread, &detached_attr,
tty_server_routine, NULL);
if (status != 0)
err_abort (status, "Create server");
/*
* Ignore an error in destroying the attributes object.
* It's unlikely to fail, there's nothing useful we can
* do about it, and it's not worth aborting the program
* over it.
*/
pthread_attr_destroy (&detached_attr);
}
/*
* Create and initialize a request structure.
*/
request = (request_t*)malloc (sizeof (request_t));
if (request == NULL)
errno_abort ("Allocate request");
request->next = NULL;
request->operation = operation;
request->synchronous = sync;
if (sync) {
request->done_flag = 0;
status = pthread_cond_init (&request->done, NULL);
if (status != 0)
err_abort (status, "Init request condition");
}
if (prompt != NULL)
strncpy (request->prompt, prompt, 32);
else
request->prompt[0] = '\0';
if (operation == REQ_WRITE && string != NULL)
strncpy (request->text, string, 128);
else
request->text[0] = '\0';
/*
* Add the request to the queue, maintaining the first and
* last pointers.
*/
if (tty_server.first == NULL) {
tty_server.first = request;
tty_server.last = request;
} else {
(tty_server.last)->next = request;
tty_server.last = request;
}
/*
* Tell the server that a request is available.
*/
status = pthread_cond_signal (&tty_server.request);
if (status != 0)
err_abort (status, "Wake server");
/*
* If the request was "synchronous", then wait for a reply.
*/
if (sync) {
while (!request->done_flag) {
status = pthread_cond_wait (
&request->done, &tty_server.mutex);
if (status != 0)
err_abort (status, "Wait for sync request");
}
if (operation == REQ_READ) {
if (strlen (request->text) > 0)
strcpy (string, request->text);
else
string[0] = '\0';
}
status = pthread_cond_destroy (&request->done);
if (status != 0)
err_abort (status, "Destroy request condition");
free (request);
}
status = pthread_mutex_unlock (&tty_server.mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
}
/*
* Client routine -- multiple copies will request server.
*/
void *client_routine (void *arg)
{
int my_number = (int)arg, loops;
char prompt[32];
char string[128], formatted[128];
int status;
sprintf (prompt, "Client %d> ", my_number);
while (1) {
tty_server_request (REQ_READ, 1, prompt, string);
if (strlen (string) == 0)
break;
for (loops = 0; loops < 4; loops++) {
sprintf (
formatted, "(%d#%d) %s", my_number, loops, string);
tty_server_request (REQ_WRITE, 0, NULL, formatted);
sleep (1);
}
}
status = pthread_mutex_lock (&client_mutex);
if (status != 0)
err_abort (status, "Lock client mutex");
client_threads--;
if (client_threads <= 0) {
status = pthread_cond_signal (&clients_done);
if (status != 0)
err_abort (status, "Signal clients done");
}
status = pthread_mutex_unlock (&client_mutex);
if (status != 0)
err_abort (status, "Unlock client mutex");
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t thread;
int count;
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 to CLIENT_THREADS.
*/
DPRINTF (("Setting concurrency level to %d\n", CLIENT_THREADS));
thr_setconcurrency (CLIENT_THREADS);
#endif
/*
* Create CLIENT_THREADS clients.
*/
client_threads = CLIENT_THREADS;
for (count = 0; count < client_threads; count++) {
status = pthread_create (&thread, NULL,
client_routine, (void*)count);
if (status != 0)
err_abort (status, "Create client thread");
}
status = pthread_mutex_lock (&client_mutex);
if (status != 0)
err_abort (status, "Lock client mutex");
while (client_threads > 0) {
status = pthread_cond_wait (&clients_done, &client_mutex);
if (status != 0)
err_abort (status, "Wait for clients to finish");
}
status = pthread_mutex_unlock (&client_mutex);
if (status != 0)
err_abort (status, "Unlock client mutex");
printf ("All clients done\n");
tty_server_request (REQ_QUIT, 1, NULL, NULL);
return 0;
}

106
sigev_thread.c Normal file
View File

@@ -0,0 +1,106 @@
/*
* sigev_thread.c
*
* Demonstrate use of the SIGEV_THREAD signal mode to handle
* signals by creating a new thread.
*
* Special notes: This program will not compile on Solaris 2.5.
* It will compile on Digital UNIX 4.0 but will not work.
* Digital UNIX 4.0c fixes SIGEV_THREAD, and sources inform me
* that Solaris 2.6 will also fix SIGEV_THREAD. To try this on
* Solaris 2.5, remove the "#ifdef sun" conditionals in main.
*/
#include <pthread.h>
#include <sys/signal.h>
#include <sys/time.h>
#include "errors.h"
timer_t timer_id;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int counter = 0;
/*
* Thread start routine to notify the application when the
* timer expires. This routine is run "as if" it were a new
* thread, each time the timer expires.
*
* When the timer has expired 5 times, the main thread will
* be awakened, and will terminate the program.
*/
void
timer_thread (void *arg)
{
int status;
status = pthread_mutex_lock (&mutex);
if (status != 0)
err_abort (status, "Lock mutex");
if (++counter >= 5) {
status = pthread_cond_signal (&cond);
if (status != 0)
err_abort (status, "Signal condition");
}
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
printf ("Timer %d\n", counter);
}
main()
{
int status;
struct itimerspec ts;
struct sigevent se;
#ifdef sun
fprintf (
stderr,
"This program cannot compile on Solaris 2.5.\n"
"To build and run on Solaris 2.6, remove the\n"
"\"#ifdef sun\" block in main().\n");
#else
/*
* Set the sigevent structure to cause the signal to be
* delivered by creating a new thread.
*/
se.sigev_notify = SIGEV_THREAD;
se.sigev_value.sival_ptr = &timer_id;
se.sigev_notify_function = timer_thread;
se.sigev_notify_attributes = NULL;
/*
* Specify a repeating timer that fires each 5 seconds.
*/
ts.it_value.tv_sec = 5;
ts.it_value.tv_nsec = 0;
ts.it_interval.tv_sec = 5;
ts.it_interval.tv_nsec = 0;
DPRINTF (("Creating timer\n"));
status = timer_create(CLOCK_REALTIME, &se, &timer_id);
if (status == -1)
errno_abort ("Create timer");
DPRINTF ((
"Setting timer %d for 5-second expiration...\n", timer_id));
status = timer_settime(timer_id, 0, &ts, 0);
if (status == -1)
errno_abort ("Set timer");
status = pthread_mutex_lock (&mutex);
if (status != 0)
err_abort (status, "Lock mutex");
while (counter < 5) {
status = pthread_cond_wait (&cond, &mutex);
if (status != 0)
err_abort (status, "Wait on condition");
}
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
#endif /* Sun */
return 0;
}

98
sigwait.c Normal file
View File

@@ -0,0 +1,98 @@
/*
* sigwait.c
*
* Demonstrate use of sigwait() to synchronously handle
* asynchrnous signals within a threaded program.
*/
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include "errors.h"
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int interrupted = 0;
sigset_t signal_set;
/*
* Wait for the SIGINT signal. When it has occurred 5 times, set
* the "interrupted" flag (the main thread's wait predicate) and
* signal a condition variable. The main thread will exit.
*/
void *signal_waiter (void *arg)
{
int sig_number;
int signal_count = 0;
int status;
while (1) {
sigwait (&signal_set, &sig_number);
if (sig_number == SIGINT) {
printf ("Got SIGINT (%d of 5)\n", signal_count+1);
if (++signal_count >= 5) {
status = pthread_mutex_lock (&mutex);
if (status != 0)
err_abort (status, "Lock mutex");
interrupted = 1;
status = pthread_cond_signal (&cond);
if (status != 0)
err_abort (status, "Signal condition");
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
break;
}
}
}
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t signal_thread_id;
int status;
/*
* Start by masking the "interesting" signal, SIGINT in the
* initial thread. Because all threads inherit the signal mask
* from their creator, all threads in the process will have
* SIGINT masked unless one explicitly unmasks it. The
* semantics of sigwait require that all threads (including
* the thread calling sigwait) have the signal masked, for
* reliable operation. Otherwise, a signal that arrives
* while the sigwaiter is not blocked in sigwait might be
* delivered to another thread.
*/
sigemptyset (&signal_set);
sigaddset (&signal_set, SIGINT);
status = pthread_sigmask (SIG_BLOCK, &signal_set, NULL);
if (status != 0)
err_abort (status, "Set signal mask");
/*
* Create the sigwait thread.
*/
status = pthread_create (&signal_thread_id, NULL,
signal_waiter, NULL);
if (status != 0)
err_abort (status, "Create sigwaiter");
/*
* Wait for the sigwait thread to receive SIGINT and signal
* the condition variable.
*/
status = pthread_mutex_lock (&mutex);
if (status != 0)
err_abort (status, "Lock mutex");
while (!interrupted) {
status = pthread_cond_wait (&cond, &mutex);
if (status != 0)
err_abort (status, "Wait for interrupt");
}
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
printf ("Main terminating with SIGINT\n");
return 0;
}

342
susp.c Normal file
View File

@@ -0,0 +1,342 @@
/*
* susp.c
*
* Demonstrate an implementation of thread suspend and resume
* (similar to the Solaris thr_suspend/thr_continue functions)
* using portable POSIX functions.
*
* Note 1: Use of suspend and resume requires extreme care. You
* can easily deadlock your application by suspending a thread
* that holds some resource -- for example, a thread calling
* printf to print a message may have libc mutexes locked, and
* no other thread will be able to return from printf until the
* suspended thread is resumed.
*
* Note 2: This program is called "susp" rather than "suspend"
* to avoid confusion (by your shell) with the suspend command.
*
* Note 3: This simple program will fail if any thread
* terminates during the test. The value of ITERATIONS must be
* adjusted to a value sufficiently large that the main thread can
* complete its two suspend/continue loops before any
* thread_routine threads terminate.
*/
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include "errors.h"
#define THREAD_COUNT 20
#define ITERATIONS 40000
unsigned long thread_count = THREAD_COUNT;
unsigned long iterations = ITERATIONS;
pthread_mutex_t the_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
volatile int sentinel = 0;
pthread_once_t once = PTHREAD_ONCE_INIT;
pthread_t *array = NULL, null_pthread = {0};
int bottom = 0;
int inited = 0;
/*
* Handle SIGUSR1 in the target thread, to suspend it until
* receiving SIGUSR2 (resume).
*/
void
suspend_signal_handler (int sig)
{
sigset_t signal_set;
/*
* Block all signals except SIGUSR2 while suspended.
*/
sigfillset (&signal_set);
sigdelset (&signal_set, SIGUSR2);
sentinel = 1;
sigsuspend (&signal_set);
/*
* Once I'm here, I've been resumed, and the resume signal
* handler has been run to completion.
*/
return;
}
/*
* Handle SIGUSR2 in the target thread, to resume it. Note that
* the signal handler does nothing. It exists only because we need
* to cause sigsuspend() to return.
*/
void
resume_signal_handler (int sig)
{
return;
}
/*
* Dynamically initialize the "suspend package" when first used
* (called by pthread_once).
*/
void
suspend_init_routine (void)
{
int status;
struct sigaction sigusr1, sigusr2;
/*
* Allocate the suspended threads array. This array is used
* to guarentee idempotency
*/
bottom = 10;
array = (pthread_t*) calloc (bottom, sizeof (pthread_t));
/*
* Install the signal handlers for suspend/resume.
*/
sigusr1.sa_flags = 0;
sigusr1.sa_handler = suspend_signal_handler;
sigemptyset (&sigusr1.sa_mask);
sigusr2.sa_flags = 0;
sigusr2.sa_handler = resume_signal_handler;
sigusr2.sa_mask = sigusr1.sa_mask;
status = sigaction (SIGUSR1, &sigusr1, NULL);
if (status == -1)
errno_abort ("Installing suspend handler");
status = sigaction (SIGUSR2, &sigusr2, NULL);
if (status == -1)
errno_abort ("Installing resume handler");
inited = 1;
return;
}
/*
* Suspend a thread by sending it a signal (SIGUSR1), which will
* block the thread until another signal (SIGUSR2) arrives.
*
* Multiple calls to thd_suspend for a single thread have no
* additional effect on the thread -- a single thd_continue
* call will cause it to resume execution.
*/
int
thd_suspend (pthread_t target_thread)
{
int status;
int i = 0;
/*
* The first call to thd_suspend will initialize the
* package.
*/
status = pthread_once (&once, suspend_init_routine);
if (status != 0)
return status;
/*
* Serialize access to suspend, makes life easier
*/
status = pthread_mutex_lock (&mut);
if (status != 0)
return status;
/*
* Threads that are suspended are added to the target_array;
* a request to suspend a thread already listed in the array
* is ignored. Sending a second SIGUSR1 would cause the
* thread to re-suspend itself as soon as it is resumed.
*/
while (i < bottom)
if (array[i++] == target_thread) {
status = pthread_mutex_unlock (&mut);
return status;
}
/*
* Ok, we really need to suspend this thread. So, lets find
* the location in the array that we'll use. If we run off
* the end, realloc the array for more space.
*/
i = 0;
while (array[i] != 0)
i++;
if (i == bottom) {
array = (pthread_t*) realloc (
array, (++bottom * sizeof (pthread_t)));
if (array == NULL) {
pthread_mutex_unlock (&mut);
return errno;
}
array[bottom] = null_pthread; /* Clear new entry */
}
/*
* Clear the sentinel and signal the thread to suspend.
*/
sentinel = 0;
status = pthread_kill (target_thread, SIGUSR1);
if (status != 0) {
pthread_mutex_unlock (&mut);
return status;
}
/*
* Wait for the sentinel to change.
*/
while (sentinel == 0)
sched_yield ();
array[i] = target_thread;
status = pthread_mutex_unlock (&mut);
return status;
}
/*
* Resume a suspended thread by sending it SIGUSR2 to break
* it out of the sigsuspend() in which it's waiting. If the
* target thread isn't suspended, return with success.
*/
int
thd_continue (pthread_t target_thread)
{
int status;
int i = 0;
/*
* Serialize access to suspend, makes life easier
*/
status = pthread_mutex_lock (&mut);
if (status != 0)
return status;
/*
* If we haven't been initialized, then the thread must be "resumed"
* it couldn't have been suspended!
*/
if (!inited) {
status = pthread_mutex_unlock (&mut);
return status;
}
/*
* Make sure the thread is in the suspend array. If not, it
* hasn't been suspended (or it has already been resumed) and
* we can just carry on.
*/
while (array[i] != target_thread && i < bottom)
i++;
if (i >= bottom) {
pthread_mutex_unlock (&mut);
return 0;
}
/*
* Signal the thread to continue, and remove the thread from
* the suspended array.
*/
status = pthread_kill (target_thread, SIGUSR2);
if (status != 0) {
pthread_mutex_unlock (&mut);
return status;
}
array[i] = 0; /* Clear array element */
status = pthread_mutex_unlock (&mut);
return status;
}
static void *
thread_routine (void *arg)
{
int number = (int)arg;
int status;
int i;
char buffer[128];
for (i = 1; i <= iterations; i++) {
/*
* Every time each thread does 5000 interations, print
* a progress report.
*/
if (i % 2000 == 0) {
sprintf (
buffer, "Thread %02d: %d\n",
number, i);
write (1, buffer, strlen (buffer));
}
sched_yield ();
}
return (void *)0;
}
int
main (int argc, char *argv[])
{
pthread_t threads[THREAD_COUNT];
pthread_attr_t detach;
int status;
void *result;
int i;
status = pthread_attr_init (&detach);
if (status != 0)
err_abort (status, "Init attributes object");
status = pthread_attr_setdetachstate (
&detach, PTHREAD_CREATE_DETACHED);
if (status != 0)
err_abort (status, "Set create-detached");
for (i = 0; i< THREAD_COUNT; i++) {
status = pthread_create (
&threads[i], &detach, thread_routine, (void *)i);
if (status != 0)
err_abort (status, "Create thread");
}
sleep (2);
for (i = 0; i < THREAD_COUNT/2; i++) {
printf ("Suspending thread %d.\n", i);
status = thd_suspend (threads[i]);
if (status != 0)
err_abort (status, "Suspend thread");
}
printf ("Sleeping ...\n");
sleep (2);
for (i = 0; i < THREAD_COUNT/2; i++) {
printf ("Continuing thread %d.\n", i);
status = thd_continue (threads[i]);
if (status != 0)
err_abort (status, "Suspend thread");
}
for (i = THREAD_COUNT/2; i < THREAD_COUNT; i++) {
printf ("Suspending thread %d.\n", i);
status = thd_suspend (threads[i]);
if (status != 0)
err_abort (status, "Suspend thread");
}
printf ("Sleeping ...\n");
sleep (2);
for (i = THREAD_COUNT/2; i < THREAD_COUNT; i++) {
printf ("Continuing thread %d.\n", i);
status = thd_continue (threads[i]);
if (status != 0)
err_abort (status, "Continue thread");
}
pthread_exit (NULL); /* Let threads finish */
}

31
thread.c Normal file
View File

@@ -0,0 +1,31 @@
/*
* thread.c
*
* Demonstrate a simple thread that writes to stdout while the
* initial thread is blocked in a read from stdin.
*/
#include <pthread.h>
#include <stdio.h>
/*
* Thread start routine that writes a message.
*/
void *writer_thread (void *arg)
{
sleep (5);
printf ("Thread I/O\n");
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t writer_id;
char *input, buffer[64];
pthread_create (&writer_id, NULL, writer_thread, NULL);
input = fgets (buffer, 64, stdin);
if (input != NULL)
printf ("You said %s", buffer);
pthread_exit (NULL);
return 0;
}

69
thread_attr.c Normal file
View File

@@ -0,0 +1,69 @@
/*
* thread_attr.c
*
* Create a thread using a non-default attributes object,
* thread_attr. The thread reports its existence, and exits. The
* attributes object specifies that the thread be created
* detached, and, if the stacksize attribute is supported, the
* thread is given a stacksize twice the minimum value.
*/
#include <limits.h>
#include <pthread.h>
#include "errors.h"
/*
* Thread start routine that reports it ran, and then exits.
*/
void *thread_routine (void *arg)
{
printf ("The thread is here\n");
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t thread_id;
pthread_attr_t thread_attr;
struct sched_param thread_param;
size_t stack_size;
int status;
status = pthread_attr_init (&thread_attr);
if (status != 0)
err_abort (status, "Create attr");
/*
* Create a detached thread.
*/
status = pthread_attr_setdetachstate (
&thread_attr, PTHREAD_CREATE_DETACHED);
if (status != 0)
err_abort (status, "Set detach");
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
/*
* If supported, determine the default stack size and report
* it, and then select a stack size for the new thread.
*
* Note that the standard does not specify the default stack
* size, and the default value in an attributes object need
* not be the size that will actually be used. Solaris 2.5
* uses a value of 0 to indicate the default.
*/
status = pthread_attr_getstacksize (&thread_attr, &stack_size);
if (status != 0)
err_abort (status, "Get stack size");
printf ("Default stack size is %u; minimum is %u\n",
stack_size, PTHREAD_STACK_MIN);
status = pthread_attr_setstacksize (
&thread_attr, PTHREAD_STACK_MIN*2);
if (status != 0)
err_abort (status, "Set stack size");
#endif
status = pthread_create (
&thread_id, &thread_attr, thread_routine, NULL);
if (status != 0)
err_abort (status, "Create thread");
printf ("Main exiting\n");
pthread_exit (NULL);
return 0;
}

29
thread_error.c Normal file
View File

@@ -0,0 +1,29 @@
/*
* thread_error.c
*
* Demonstrate detection of errors from a typical POSIX 1003.1c-1995
* function, pthread_join.
*/
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
int main (int argc, char *argv[])
{
pthread_t thread;
int status;
/*
* Attempt to join with an uninitialized thread ID. On most
* implementations, this will return an ESRCH error code. If
* the local (and uninitialized) pthread_t happens to be a valid
* thread ID, it is almost certainly that of the initial thread,
* which is running main(). In that case, your Pthreads
* implementation may either return EDEADLK (self-deadlock),
* or it may hang. If it hangs, quit and try again.
*/
status = pthread_join (thread, NULL);
if (status != 0)
fprintf (stderr, "error %d: %s\n", status, strerror (status));
return status;
}

120
trylock.c Normal file
View File

@@ -0,0 +1,120 @@
/*
* 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++;
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;
}

118
tsd_destructor.c Normal file
View File

@@ -0,0 +1,118 @@
/*
* tsd_destructor.c
*
* Demonstrate use of thread-specific data destructors.
*/
#include <pthread.h>
#include "errors.h"
/*
* Structure used as value of thread-specific data key.
*/
typedef struct private_tag {
pthread_t thread_id;
char *string;
} private_t;
pthread_key_t identity_key; /* Thread-specific data key */
pthread_mutex_t identity_key_mutex = PTHREAD_MUTEX_INITIALIZER;
long identity_key_counter = 0;
/*
* This routine is called as each thread terminates with a value
* for the thread-specific data key. It keeps track of how many
* threads still have values, and deletes the key when there are
* no more references.
*/
void identity_key_destructor (void *value)
{
private_t *private = (private_t*)value;
int status;
printf ("thread \"%s\" exiting...\n", private->string);
free (value);
status = pthread_mutex_lock (&identity_key_mutex);
if (status != 0)
err_abort (status, "Lock key mutex");
identity_key_counter--;
if (identity_key_counter <= 0) {
status = pthread_key_delete (identity_key);
if (status != 0)
err_abort (status, "Delete key");
printf ("key deleted...\n");
}
status = pthread_mutex_unlock (&identity_key_mutex);
if (status != 0)
err_abort (status, "Unlock key mutex");
}
/*
* Helper routine to allocate a new value for thread-specific
* data key if the thread doesn't already have one.
*/
void *identity_key_get (void)
{
void *value;
int status;
value = pthread_getspecific (identity_key);
if (value == NULL) {
value = malloc (sizeof (private_t));
if (value == NULL)
errno_abort ("Allocate key value");
status = pthread_setspecific (identity_key, (void*)value);
if (status != 0)
err_abort (status, "Set TSD");
}
return value;
}
/*
* Thread start routine to use thread-specific data.
*/
void *thread_routine (void *arg)
{
private_t *value;
value = (private_t*)identity_key_get ();
value->thread_id = pthread_self ();
value->string = (char*)arg;
printf ("thread \"%s\" starting...\n", value->string);
sleep (2);
return NULL;
}
void main (int argc, char *argv[])
{
pthread_t thread_1, thread_2;
private_t *value;
int status;
/*
* Create the TSD key, and set the reference counter to
* the number of threads that will use it (two thread_routine
* threads plus main). This must be done before creating
* the threads! Otherwise, if one thread runs the key's
* destructor before any other thread uses the key, it will
* be deleted.
*
* Note that there's rarely any good reason to delete a
* thread-specific data key.
*/
status = pthread_key_create (&identity_key, identity_key_destructor);
if (status != 0)
err_abort (status, "Create key");
identity_key_counter = 3;
value = (private_t*)identity_key_get ();
value->thread_id = pthread_self ();
value->string = "Main thread";
status = pthread_create (&thread_1, NULL,
thread_routine, "Thread 1");
if (status != 0)
err_abort (status, "Create thread 1");
status = pthread_create (&thread_2, NULL,
thread_routine, "Thread 2");
if (status != 0)
err_abort (status, "Create thread 2");
pthread_exit (NULL);
}

81
tsd_once.c Normal file
View File

@@ -0,0 +1,81 @@
/*
* tsd_once.c
*
* Demonstrate use of pthread_once to initialize something
* exactly once within a multithreaded program.
*
* Note that it is often easier to use a statically initialized
* mutex to accomplish the same result.
*/
#include <pthread.h>
#include "errors.h"
/*
* Structure used as the value for thread-specific data key.
*/
typedef struct tsd_tag {
pthread_t thread_id;
char *string;
} tsd_t;
pthread_key_t tsd_key; /* Thread-specific data key */
pthread_once_t key_once = PTHREAD_ONCE_INIT;
/*
* One-time initialization routine used with the pthread_once
* control block.
*/
void once_routine (void)
{
int status;
printf ("initializing key\n");
status = pthread_key_create (&tsd_key, NULL);
if (status != 0)
err_abort (status, "Create key");
}
/*
* Thread start routine that uses pthread_once to dynamically
* create a thread-specific data key.
*/
void *thread_routine (void *arg)
{
tsd_t *value;
int status;
status = pthread_once (&key_once, once_routine);
if (status != 0)
err_abort (status, "Once init");
value = (tsd_t*)malloc (sizeof (tsd_t));
if (value == NULL)
errno_abort ("Allocate key value");
status = pthread_setspecific (tsd_key, value);
if (status != 0)
err_abort (status, "Set tsd");
printf ("%s set tsd value %p\n", arg, value);
value->thread_id = pthread_self ();
value->string = (char*)arg;
value = (tsd_t*)pthread_getspecific (tsd_key);
printf ("%s starting...\n", value->string);
sleep (2);
value = (tsd_t*)pthread_getspecific (tsd_key);
printf ("%s done...\n", value->string);
return NULL;
}
void main (int argc, char *argv[])
{
pthread_t thread1, thread2;
int status;
status = pthread_create (
&thread1, NULL, thread_routine, "thread 1");
if (status != 0)
err_abort (status, "Create thread 1");
status = pthread_create (
&thread2, NULL, thread_routine, "thread 2");
if (status != 0)
err_abort (status, "Create thread 2");
pthread_exit (NULL);
}

307
workq.c Normal file
View File

@@ -0,0 +1,307 @@
/*
* workq.c
*
* This file implements the interfaces for a "work queue"
* manager. A "manager object" is created with several
* parameters, including the required size of a work queue
* entry, the maximum desired degree of parallelism (number of
* threads to service the queue), and the address of an
* execution engine routine.
*
* The application requests a work queue entry from the manager,
* fills in the application-specific fields, and returns it to
* the queue manager for processing. The manager will create a
* new thread to service the queue if all current threads are
* busy and the maximum level of parallelism has not yet been
* reached.
*
* The manager will dequeue items and present them to the
* processing engine until the queue is empty; at that point,
* processing threads will begin to shut down. (They will be
* restarted when work appears.)
*/
#include <pthread.h>
#include <stdlib.h>
#include <time.h>
#include "errors.h"
#include "workq.h"
/*
* Thread start routine to serve the work queue.
*/
static void *workq_server (void *arg)
{
struct timespec timeout;
workq_t *wq = (workq_t *)arg;
workq_ele_t *we;
int status, timedout;
/*
* We don't need to validate the workq_t here... we don't
* create server threads until requests are queued (the
* queue has been initialized by then!) and we wait for all
* server threads to terminate before destroying a work
* queue.
*/
DPRINTF (("A worker is starting\n"));
status = pthread_mutex_lock (&wq->mutex);
if (status != 0)
return NULL;
while (1) {
timedout = 0;
DPRINTF (("Worker waiting for work\n"));
clock_gettime (CLOCK_REALTIME, &timeout);
timeout.tv_sec += 2;
while (wq->first == NULL && !wq->quit) {
/*
* Server threads time out after spending 2 seconds
* waiting for new work, and exit.
*/
status = pthread_cond_timedwait (
&wq->cv, &wq->mutex, &timeout);
if (status == ETIMEDOUT) {
DPRINTF (("Worker wait timed out\n"));
timedout = 1;
break;
} else if (status != 0) {
/*
* This shouldn't happen, so the work queue
* package should fail. Because the work queue
* API is asynchronous, that would add
* complication. Because the chances of failure
* are slim, I choose to avoid that
* complication. The server thread will return,
* and allow another server thread to pick up
* the work later. Note that, if this was the
* only server thread, the queue won't be
* serviced until a new work item is
* queued. That could be fixed by creating a new
* server here.
*/
DPRINTF ((
"Worker wait failed, %d (%s)\n",
status, strerror (status)));
wq->counter--;
pthread_mutex_unlock (&wq->mutex);
return NULL;
}
}
DPRINTF (("Work queue: %#lx, quit: %d\n", wq->first, wq->quit));
we = wq->first;
if (we != NULL) {
wq->first = we->next;
if (wq->last == we)
wq->last = NULL;
status = pthread_mutex_unlock (&wq->mutex);
if (status != 0)
return NULL;
DPRINTF (("Worker calling engine\n"));
wq->engine (we->data);
free (we);
status = pthread_mutex_lock (&wq->mutex);
if (status != 0)
return NULL;
}
/*
* If there are no more work requests, and the servers
* have been asked to quit, then shut down.
*/
if (wq->first == NULL && wq->quit) {
DPRINTF (("Worker shutting down\n"));
wq->counter--;
/*
* NOTE: Just to prove that every rule has an
* exception, I'm using the "cv" condition for two
* separate predicates here. That's OK, since the
* case used here applies only once during the life
* of a work queue -- during rundown. The overhead
* is minimal and it's not worth creating a separate
* condition variable that would be waited and
* signaled exactly once!
*/
if (wq->counter == 0)
pthread_cond_broadcast (&wq->cv);
pthread_mutex_unlock (&wq->mutex);
return NULL;
}
/*
* If there's no more work, and we wait for as long as
* we're allowed, then terminate this server thread.
*/
if (wq->first == NULL && timedout) {
DPRINTF (("engine terminating due to timeout.\n"));
wq->counter--;
break;
}
}
pthread_mutex_unlock (&wq->mutex);
DPRINTF (("Worker exiting\n"));
return NULL;
}
/*
* Initialize a work queue.
*/
int workq_init (workq_t *wq, int threads, void (*engine)(void *arg))
{
int status;
status = pthread_attr_init (&wq->attr);
if (status != 0)
return status;
status = pthread_attr_setdetachstate (
&wq->attr, PTHREAD_CREATE_DETACHED);
if (status != 0) {
pthread_attr_destroy (&wq->attr);
return status;
}
status = pthread_mutex_init (&wq->mutex, NULL);
if (status != 0) {
pthread_attr_destroy (&wq->attr);
return status;
}
status = pthread_cond_init (&wq->cv, NULL);
if (status != 0) {
pthread_mutex_destroy (&wq->mutex);
pthread_attr_destroy (&wq->attr);
return status;
}
wq->quit = 0; /* not time to quit */
wq->first = wq->last = NULL; /* no queue entries */
wq->parallelism = threads; /* max servers */
wq->counter = 0; /* no server threads yet */
wq->idle = 0; /* no idle servers */
wq->engine = engine;
wq->valid = WORKQ_VALID;
return 0;
}
/*
* Destroy a work queue.
*/
int workq_destroy (workq_t *wq)
{
int status, status1, status2;
if (wq->valid != WORKQ_VALID)
return EINVAL;
status = pthread_mutex_lock (&wq->mutex);
if (status != 0)
return status;
wq->valid = 0; /* prevent any other operations */
/*
* Check whether any threads are active, and run them down:
*
* 1. set the quit flag
* 2. broadcast to wake any servers that may be asleep
* 4. wait for all threads to quit (counter goes to 0)
* Because we don't use join, we don't need to worry
* about tracking thread IDs.
*/
if (wq->counter > 0) {
wq->quit = 1;
/* if any threads are idling, wake them. */
if (wq->idle > 0) {
status = pthread_cond_broadcast (&wq->cv);
if (status != 0) {
pthread_mutex_unlock (&wq->mutex);
return status;
}
}
/*
* Just to prove that every rule has an exception, I'm
* using the "cv" condition for two separate predicates
* here. That's OK, since the case used here applies
* only once during the life of a work queue -- during
* rundown. The overhead is minimal and it's not worth
* creating a separate condition variable that would be
* waited and signalled exactly once!
*/
while (wq->counter > 0) {
status = pthread_cond_wait (&wq->cv, &wq->mutex);
if (status != 0) {
pthread_mutex_unlock (&wq->mutex);
return status;
}
}
}
status = pthread_mutex_unlock (&wq->mutex);
if (status != 0)
return status;
status = pthread_mutex_destroy (&wq->mutex);
status1 = pthread_cond_destroy (&wq->cv);
status2 = pthread_attr_destroy (&wq->attr);
return (status ? status : (status1 ? status1 : status2));
}
/*
* Add an item to a work queue.
*/
int workq_add (workq_t *wq, void *element)
{
workq_ele_t *item;
pthread_t id;
int status;
if (wq->valid != WORKQ_VALID)
return EINVAL;
/*
* Create and initialize a request structure.
*/
item = (workq_ele_t *)malloc (sizeof (workq_ele_t));
if (item == NULL)
return ENOMEM;
item->data = element;
item->next = NULL;
status = pthread_mutex_lock (&wq->mutex);
if (status != 0) {
free (item);
return status;
}
/*
* Add the request to the end of the queue, updating the
* first and last pointers.
*/
if (wq->first == NULL)
wq->first = item;
else
wq->last->next = item;
wq->last = item;
/*
* if any threads are idling, wake one.
*/
if (wq->idle > 0) {
status = pthread_cond_signal (&wq->cv);
if (status != 0) {
pthread_mutex_unlock (&wq->mutex);
return status;
}
} else if (wq->counter < wq->parallelism) {
/*
* If there were no idling threads, and we're allowed to
* create a new thread, do so.
*/
DPRINTF (("Creating new worker\n"));
status = pthread_create (
&id, &wq->attr, workq_server, (void*)wq);
if (status != 0) {
pthread_mutex_unlock (&wq->mutex);
return status;
}
wq->counter++;
}
pthread_mutex_unlock (&wq->mutex);
return 0;
}

59
workq.h Normal file
View File

@@ -0,0 +1,59 @@
/*
* workq.h
*
* This header file defines the interfaces for a "work queue"
* manager. A "manager object" is created with several
* parameters, including the required size of a work queue
* entry, the maximum desired degree of parallelism (number of
* threads to service the queue), and the address of an
* execution engine routine.
*
* The application requests a work queue entry from the manager,
* fills in the application-specific fields, and returns it to
* the queue manager for processing. The manager will create a
* new thread to service the queue if all current threads are
* busy and the maximum level of parallelism has not yet been
* reached.
*
* The manager will dequeue items and present them to the
* processing engine until the queue is empty; at that point,
* processing threads will begin to shut down. (They will be
* restarted when work appears.)
*/
#include <pthread.h>
/*
* Structure to keep track of work queue requests.
*/
typedef struct workq_ele_tag {
struct workq_ele_tag *next;
void *data;
} workq_ele_t;
/*
* Structure describing a work queue.
*/
typedef struct workq_tag {
pthread_mutex_t mutex;
pthread_cond_t cv; /* wait for work */
pthread_attr_t attr; /* create detached threads */
workq_ele_t *first, *last; /* work queue */
int valid; /* set when valid */
int quit; /* set when workq should quit */
int parallelism; /* number of threads required */
int counter; /* current number of threads */
int idle; /* number of idle threads */
void (*engine)(void *arg); /* user engine */
} workq_t;
#define WORKQ_VALID 0xdec1992
/*
* Define work queue functions
*/
extern int workq_init (
workq_t *wq,
int threads, /* maximum threads */
void (*engine)(void *)); /* engine routine */
extern int workq_destroy (workq_t *wq);
extern int workq_add (workq_t *wq, void *data);

144
workq_main.c Normal file
View File

@@ -0,0 +1,144 @@
/*
* workq_main.c
*
* Demonstrate a use of work queues.
*/
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "workq.h"
#include "errors.h"
#define ITERATIONS 25
typedef struct power_tag {
int value;
int power;
} power_t;
typedef struct engine_tag {
struct engine_tag *link;
pthread_t thread_id;
int calls;
} engine_t;
pthread_key_t engine_key; /* Keep track of active engines */
pthread_mutex_t engine_list_mutex = PTHREAD_MUTEX_INITIALIZER;
engine_t *engine_list_head = NULL;
workq_t workq;
/*
* Thread-specific data destructor routine for engine_key
*/
void destructor (void *value_ptr)
{
engine_t *engine = (engine_t*)value_ptr;
pthread_mutex_lock (&engine_list_mutex);
engine->link = engine_list_head;
engine_list_head = engine;
pthread_mutex_unlock (&engine_list_mutex);
}
/*
* This is the routine called by the work queue servers to
* perform operations in parallel.
*/
void engine_routine (void *arg)
{
engine_t *engine;
power_t *power = (power_t*)arg;
int result, count;
int status;
engine = pthread_getspecific (engine_key);
if (engine == NULL) {
engine = (engine_t*)malloc (sizeof (engine_t));
status = pthread_setspecific (
engine_key, (void*)engine);
if (status != 0)
err_abort (status, "Set tsd");
engine->thread_id = pthread_self ();
engine->calls = 1;
} else
engine->calls++;
result = 1;
printf (
"Engine: computing %d^%d\n",
power->value, power->power);
for (count = 1; count <= power->power; count++)
result *= power->value;
free (arg);
}
/*
* Thread start routine that issues work queue requests.
*/
void *thread_routine (void *arg)
{
power_t *element;
int count;
unsigned int seed = (unsigned int)time (NULL);
int status;
/*
* Loop, making requests.
*/
for (count = 0; count < ITERATIONS; count++) {
element = (power_t*)malloc (sizeof (power_t));
if (element == NULL)
errno_abort ("Allocate element");
element->value = rand_r (&seed) % 20;
element->power = rand_r (&seed) % 7;
DPRINTF ((
"Request: %d^%d\n",
element->value, element->power));
status = workq_add (&workq, (void*)element);
if (status != 0)
err_abort (status, "Add to work queue");
sleep (rand_r (&seed) % 5);
}
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t thread_id;
engine_t *engine;
int count = 0, calls = 0;
int status;
status = pthread_key_create (&engine_key, destructor);
if (status != 0)
err_abort (status, "Create key");
status = workq_init (&workq, 4, engine_routine);
if (status != 0)
err_abort (status, "Init work queue");
status = pthread_create (&thread_id, NULL, thread_routine, NULL);
if (status != 0)
err_abort (status, "Create thread");
(void)thread_routine (NULL);
status = pthread_join (thread_id, NULL);
if (status != 0)
err_abort (status, "Join thread");
status = workq_destroy (&workq);
if (status != 0)
err_abort (status, "Destroy work queue");
/*
* By now, all of the engine_t structures have been placed
* on the list (by the engine thread destructors), so we
* can count and summarize them.
*/
engine = engine_list_head;
while (engine != NULL) {
count++;
calls += engine->calls;
printf ("engine %d: %d calls\n", count, engine->calls);
engine = engine->link;
}
printf ("%d engine threads processed %d calls\n",
count, calls);
return 0;
}