initial import
from http://www.informit.com/store/programming-with-posix-threads-9780201633924
This commit is contained in:
50
Makefile
Normal file
50
Makefile
Normal 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
100
README
Normal 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
34
alarm.c
Normal 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
198
alarm_cond.c
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* alarm_cond.c
|
||||
*
|
||||
* This is an enhancement to the alarm_mutex.c program, which
|
||||
* used only a mutex to synchronize access to the shared alarm
|
||||
* list. This version adds a condition variable. The alarm
|
||||
* thread waits on this condition variable, with a timeout that
|
||||
* corresponds to the earliest timer request. If the main thread
|
||||
* enters an earlier timeout, it signals the condition variable
|
||||
* so that the alarm thread will wake up and process the earlier
|
||||
* timeout first, requeueing the later request.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include "errors.h"
|
||||
|
||||
/*
|
||||
* The "alarm" structure now contains the time_t (time since the
|
||||
* Epoch, in seconds) for each alarm, so that they can be
|
||||
* sorted. Storing the requested number of seconds would not be
|
||||
* enough, since the "alarm thread" cannot tell how long it has
|
||||
* been on the list.
|
||||
*/
|
||||
typedef struct alarm_tag {
|
||||
struct alarm_tag *link;
|
||||
int seconds;
|
||||
time_t time; /* seconds from EPOCH */
|
||||
char message[64];
|
||||
} alarm_t;
|
||||
|
||||
pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_cond_t alarm_cond = PTHREAD_COND_INITIALIZER;
|
||||
alarm_t *alarm_list = NULL;
|
||||
time_t current_alarm = 0;
|
||||
|
||||
/*
|
||||
* Insert alarm entry on list, in order.
|
||||
*/
|
||||
void alarm_insert (alarm_t *alarm)
|
||||
{
|
||||
int status;
|
||||
alarm_t **last, *next;
|
||||
|
||||
/*
|
||||
* LOCKING PROTOCOL:
|
||||
*
|
||||
* This routine requires that the caller have locked the
|
||||
* alarm_mutex!
|
||||
*/
|
||||
last = &alarm_list;
|
||||
next = *last;
|
||||
while (next != NULL) {
|
||||
if (next->time >= alarm->time) {
|
||||
alarm->link = next;
|
||||
*last = alarm;
|
||||
break;
|
||||
}
|
||||
last = &next->link;
|
||||
next = next->link;
|
||||
}
|
||||
/*
|
||||
* If we reached the end of the list, insert the new alarm
|
||||
* there. ("next" is NULL, and "last" points to the link
|
||||
* field of the last item, or to the list header.)
|
||||
*/
|
||||
if (next == NULL) {
|
||||
*last = alarm;
|
||||
alarm->link = NULL;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf ("[list: ");
|
||||
for (next = alarm_list; next != NULL; next = next->link)
|
||||
printf ("%d(%d)[\"%s\"] ", next->time,
|
||||
next->time - time (NULL), next->message);
|
||||
printf ("]\n");
|
||||
#endif
|
||||
/*
|
||||
* Wake the alarm thread if it is not busy (that is, if
|
||||
* current_alarm is 0, signifying that it's waiting for
|
||||
* work), or if the new alarm comes before the one on
|
||||
* which the alarm thread is waiting.
|
||||
*/
|
||||
if (current_alarm == 0 || alarm->time < current_alarm) {
|
||||
current_alarm = alarm->time;
|
||||
status = pthread_cond_signal (&alarm_cond);
|
||||
if (status != 0)
|
||||
err_abort (status, "Signal cond");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The alarm thread's start routine.
|
||||
*/
|
||||
void *alarm_thread (void *arg)
|
||||
{
|
||||
alarm_t *alarm;
|
||||
struct timespec cond_time;
|
||||
time_t now;
|
||||
int status, expired;
|
||||
|
||||
/*
|
||||
* Loop forever, processing commands. The alarm thread will
|
||||
* be disintegrated when the process exits. Lock the mutex
|
||||
* at the start -- it will be unlocked during condition
|
||||
* waits, so the main thread can insert alarms.
|
||||
*/
|
||||
status = pthread_mutex_lock (&alarm_mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Lock mutex");
|
||||
while (1) {
|
||||
/*
|
||||
* If the alarm list is empty, wait until an alarm is
|
||||
* added. Setting current_alarm to 0 informs the insert
|
||||
* routine that the thread is not busy.
|
||||
*/
|
||||
current_alarm = 0;
|
||||
while (alarm_list == NULL) {
|
||||
status = pthread_cond_wait (&alarm_cond, &alarm_mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Wait on cond");
|
||||
}
|
||||
alarm = alarm_list;
|
||||
alarm_list = alarm->link;
|
||||
now = time (NULL);
|
||||
expired = 0;
|
||||
if (alarm->time > now) {
|
||||
#ifdef DEBUG
|
||||
printf ("[waiting: %d(%d)\"%s\"]\n", alarm->time,
|
||||
alarm->time - time (NULL), alarm->message);
|
||||
#endif
|
||||
cond_time.tv_sec = alarm->time;
|
||||
cond_time.tv_nsec = 0;
|
||||
current_alarm = alarm->time;
|
||||
while (current_alarm == alarm->time) {
|
||||
status = pthread_cond_timedwait (
|
||||
&alarm_cond, &alarm_mutex, &cond_time);
|
||||
if (status == ETIMEDOUT) {
|
||||
expired = 1;
|
||||
break;
|
||||
}
|
||||
if (status != 0)
|
||||
err_abort (status, "Cond timedwait");
|
||||
}
|
||||
if (!expired)
|
||||
alarm_insert (alarm);
|
||||
} else
|
||||
expired = 1;
|
||||
if (expired) {
|
||||
printf ("(%d) %s\n", alarm->seconds, alarm->message);
|
||||
free (alarm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
int status;
|
||||
char line[128];
|
||||
alarm_t *alarm;
|
||||
pthread_t thread;
|
||||
|
||||
status = pthread_create (
|
||||
&thread, NULL, alarm_thread, NULL);
|
||||
if (status != 0)
|
||||
err_abort (status, "Create alarm thread");
|
||||
while (1) {
|
||||
printf ("Alarm> ");
|
||||
if (fgets (line, sizeof (line), stdin) == NULL) exit (0);
|
||||
if (strlen (line) <= 1) continue;
|
||||
alarm = (alarm_t*)malloc (sizeof (alarm_t));
|
||||
if (alarm == NULL)
|
||||
errno_abort ("Allocate alarm");
|
||||
|
||||
/*
|
||||
* Parse input line into seconds (%d) and a message
|
||||
* (%64[^\n]), consisting of up to 64 characters
|
||||
* separated from the seconds by whitespace.
|
||||
*/
|
||||
if (sscanf (line, "%d %64[^\n]",
|
||||
&alarm->seconds, alarm->message) < 2) {
|
||||
fprintf (stderr, "Bad command\n");
|
||||
free (alarm);
|
||||
} else {
|
||||
status = pthread_mutex_lock (&alarm_mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Lock mutex");
|
||||
alarm->time = time (NULL) + alarm->seconds;
|
||||
/*
|
||||
* Insert the new alarm into the list of alarms,
|
||||
* sorted by expiration time.
|
||||
*/
|
||||
alarm_insert (alarm);
|
||||
status = pthread_mutex_unlock (&alarm_mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Unlock mutex");
|
||||
}
|
||||
}
|
||||
}
|
||||
56
alarm_fork.c
Normal file
56
alarm_fork.c
Normal 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
175
alarm_mutex.c
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* alarm_mutex.c
|
||||
*
|
||||
* This is an enhancement to the alarm_thread.c program, which
|
||||
* created an "alarm thread" for each alarm command. This new
|
||||
* version uses a single alarm thread, which reads the next
|
||||
* entry in a list. The main thread places new requests onto the
|
||||
* list, in order of absolute expiration time. The list is
|
||||
* protected by a mutex, and the alarm thread sleeps for at
|
||||
* least 1 second, each iteration, to ensure that the main
|
||||
* thread can lock the mutex to add new work to the list.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include "errors.h"
|
||||
|
||||
/*
|
||||
* The "alarm" structure now contains the time_t (time since the
|
||||
* Epoch, in seconds) for each alarm, so that they can be
|
||||
* sorted. Storing the requested number of seconds would not be
|
||||
* enough, since the "alarm thread" cannot tell how long it has
|
||||
* been on the list.
|
||||
*/
|
||||
typedef struct alarm_tag {
|
||||
struct alarm_tag *link;
|
||||
int seconds;
|
||||
time_t time; /* seconds from EPOCH */
|
||||
char message[64];
|
||||
} alarm_t;
|
||||
|
||||
pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
alarm_t *alarm_list = NULL;
|
||||
|
||||
/*
|
||||
* The alarm thread's start routine.
|
||||
*/
|
||||
void *alarm_thread (void *arg)
|
||||
{
|
||||
alarm_t *alarm;
|
||||
int sleep_time;
|
||||
time_t now;
|
||||
int status;
|
||||
|
||||
/*
|
||||
* Loop forever, processing commands. The alarm thread will
|
||||
* be disintegrated when the process exits.
|
||||
*/
|
||||
while (1) {
|
||||
status = pthread_mutex_lock (&alarm_mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Lock mutex");
|
||||
alarm = alarm_list;
|
||||
|
||||
/*
|
||||
* If the alarm list is empty, wait for one second. This
|
||||
* allows the main thread to run, and read another
|
||||
* command. If the list is not empty, remove the first
|
||||
* item. Compute the number of seconds to wait -- if the
|
||||
* result is less than 0 (the time has passed), then set
|
||||
* the sleep_time to 0.
|
||||
*/
|
||||
if (alarm == NULL)
|
||||
sleep_time = 1;
|
||||
else {
|
||||
alarm_list = alarm->link;
|
||||
now = time (NULL);
|
||||
if (alarm->time <= now)
|
||||
sleep_time = 0;
|
||||
else
|
||||
sleep_time = alarm->time - now;
|
||||
#ifdef DEBUG
|
||||
printf ("[waiting: %d(%d)\"%s\"]\n", alarm->time,
|
||||
sleep_time, alarm->message);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlock the mutex before waiting, so that the main
|
||||
* thread can lock it to insert a new alarm request. If
|
||||
* the sleep_time is 0, then call sched_yield, giving
|
||||
* the main thread a chance to run if it has been
|
||||
* readied by user input, without delaying the message
|
||||
* if there's no input.
|
||||
*/
|
||||
status = pthread_mutex_unlock (&alarm_mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Unlock mutex");
|
||||
if (sleep_time > 0)
|
||||
sleep (sleep_time);
|
||||
else
|
||||
sched_yield ();
|
||||
|
||||
/*
|
||||
* If a timer expired, print the message and free the
|
||||
* structure.
|
||||
*/
|
||||
if (alarm != NULL) {
|
||||
printf ("(%d) %s\n", alarm->seconds, alarm->message);
|
||||
free (alarm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
int status;
|
||||
char line[128];
|
||||
alarm_t *alarm, **last, *next;
|
||||
pthread_t thread;
|
||||
|
||||
status = pthread_create (
|
||||
&thread, NULL, alarm_thread, NULL);
|
||||
if (status != 0)
|
||||
err_abort (status, "Create alarm thread");
|
||||
while (1) {
|
||||
printf ("alarm> ");
|
||||
if (fgets (line, sizeof (line), stdin) == NULL) exit (0);
|
||||
if (strlen (line) <= 1) continue;
|
||||
alarm = (alarm_t*)malloc (sizeof (alarm_t));
|
||||
if (alarm == NULL)
|
||||
errno_abort ("Allocate alarm");
|
||||
|
||||
/*
|
||||
* Parse input line into seconds (%d) and a message
|
||||
* (%64[^\n]), consisting of up to 64 characters
|
||||
* separated from the seconds by whitespace.
|
||||
*/
|
||||
if (sscanf (line, "%d %64[^\n]",
|
||||
&alarm->seconds, alarm->message) < 2) {
|
||||
fprintf (stderr, "Bad command\n");
|
||||
free (alarm);
|
||||
} else {
|
||||
status = pthread_mutex_lock (&alarm_mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Lock mutex");
|
||||
alarm->time = time (NULL) + alarm->seconds;
|
||||
|
||||
/*
|
||||
* Insert the new alarm into the list of alarms,
|
||||
* sorted by expiration time.
|
||||
*/
|
||||
last = &alarm_list;
|
||||
next = *last;
|
||||
while (next != NULL) {
|
||||
if (next->time >= alarm->time) {
|
||||
alarm->link = next;
|
||||
*last = alarm;
|
||||
break;
|
||||
}
|
||||
last = &next->link;
|
||||
next = next->link;
|
||||
}
|
||||
/*
|
||||
* If we reached the end of the list, insert the new
|
||||
* alarm there. ("next" is NULL, and "last" points
|
||||
* to the link field of the last item, or to the
|
||||
* list header).
|
||||
*/
|
||||
if (next == NULL) {
|
||||
*last = alarm;
|
||||
alarm->link = NULL;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf ("[list: ");
|
||||
for (next = alarm_list; next != NULL; next = next->link)
|
||||
printf ("%d(%d)[\"%s\"] ", next->time,
|
||||
next->time - time (NULL), next->message);
|
||||
printf ("]\n");
|
||||
#endif
|
||||
status = pthread_mutex_unlock (&alarm_mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Unlock mutex");
|
||||
}
|
||||
}
|
||||
}
|
||||
59
alarm_thread.c
Normal file
59
alarm_thread.c
Normal 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
130
atfork.c
Normal 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
199
backoff.c
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* backoff.c
|
||||
*
|
||||
* Demonstrate deadlock avoidance using "mutex backoff".
|
||||
*
|
||||
* Special notes: On a Solaris 2.5 uniprocessor, this test will
|
||||
* not produce interleaved output unless extra LWPs are created
|
||||
* by calling thr_setconcurrency(), because threads are not
|
||||
* timesliced.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include "errors.h"
|
||||
|
||||
#define ITERATIONS 10
|
||||
|
||||
/*
|
||||
* Initialize a static array of 3 mutexes.
|
||||
*/
|
||||
pthread_mutex_t mutex[3] = {
|
||||
PTHREAD_MUTEX_INITIALIZER,
|
||||
PTHREAD_MUTEX_INITIALIZER,
|
||||
PTHREAD_MUTEX_INITIALIZER
|
||||
};
|
||||
|
||||
int backoff = 1; /* Whether to backoff or deadlock */
|
||||
int yield_flag = 0; /* 0: no yield, >0: yield, <0: sleep */
|
||||
|
||||
/*
|
||||
* This is a thread start routine that locks all mutexes in
|
||||
* order, to ensure a conflict with lock_reverse, which does the
|
||||
* opposite.
|
||||
*/
|
||||
void *lock_forward (void *arg)
|
||||
{
|
||||
int i, iterate, backoffs;
|
||||
int status;
|
||||
|
||||
for (iterate = 0; iterate < ITERATIONS; iterate++) {
|
||||
backoffs = 0;
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (i == 0) {
|
||||
status = pthread_mutex_lock (&mutex[i]);
|
||||
if (status != 0)
|
||||
err_abort (status, "First lock");
|
||||
} else {
|
||||
if (backoff)
|
||||
status = pthread_mutex_trylock (&mutex[i]);
|
||||
else
|
||||
status = pthread_mutex_lock (&mutex[i]);
|
||||
if (status == EBUSY) {
|
||||
backoffs++;
|
||||
DPRINTF ((
|
||||
" [forward locker backing off at %d]\n",
|
||||
i));
|
||||
for (; i >= 0; i--) {
|
||||
status = pthread_mutex_unlock (&mutex[i]);
|
||||
if (status != 0)
|
||||
err_abort (status, "Backoff");
|
||||
}
|
||||
} else {
|
||||
if (status != 0)
|
||||
err_abort (status, "Lock mutex");
|
||||
DPRINTF ((" forward locker got %d\n", i));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Yield processor, if needed to be sure locks get
|
||||
* interleaved on a uniprocessor.
|
||||
*/
|
||||
if (yield_flag) {
|
||||
if (yield_flag > 0)
|
||||
sched_yield ();
|
||||
else
|
||||
sleep (1);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Report that we got 'em, and unlock to try again.
|
||||
*/
|
||||
printf (
|
||||
"lock forward got all locks, %d backoffs\n", backoffs);
|
||||
pthread_mutex_unlock (&mutex[2]);
|
||||
pthread_mutex_unlock (&mutex[1]);
|
||||
pthread_mutex_unlock (&mutex[0]);
|
||||
sched_yield ();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a thread start routine that locks all mutexes in
|
||||
* reverse order, to ensure a conflict with lock_forward, which
|
||||
* does the opposite.
|
||||
*/
|
||||
void *lock_backward (void *arg)
|
||||
{
|
||||
int i, iterate, backoffs;
|
||||
int status;
|
||||
|
||||
for (iterate = 0; iterate < ITERATIONS; iterate++) {
|
||||
backoffs = 0;
|
||||
for (i = 2; i >= 0; i--) {
|
||||
if (i == 2) {
|
||||
status = pthread_mutex_lock (&mutex[i]);
|
||||
if (status != 0)
|
||||
err_abort (status, "First lock");
|
||||
} else {
|
||||
if (backoff)
|
||||
status = pthread_mutex_trylock (&mutex[i]);
|
||||
else
|
||||
status = pthread_mutex_lock (&mutex[i]);
|
||||
if (status == EBUSY) {
|
||||
backoffs++;
|
||||
DPRINTF ((
|
||||
" [backward locker backing off at %d]\n",
|
||||
i));
|
||||
for (; i < 3; i++) {
|
||||
status = pthread_mutex_unlock (&mutex[i]);
|
||||
if (status != 0)
|
||||
err_abort (status, "Backoff");
|
||||
}
|
||||
} else {
|
||||
if (status != 0)
|
||||
err_abort (status, "Lock mutex");
|
||||
DPRINTF ((" backward locker got %d\n", i));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Yield processor, if needed to be sure locks get
|
||||
* interleaved on a uniprocessor.
|
||||
*/
|
||||
if (yield_flag) {
|
||||
if (yield_flag > 0)
|
||||
sched_yield ();
|
||||
else
|
||||
sleep (1);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Report that we got 'em, and unlock to try again.
|
||||
*/
|
||||
printf (
|
||||
"lock backward got all locks, %d backoffs\n", backoffs);
|
||||
pthread_mutex_unlock (&mutex[0]);
|
||||
pthread_mutex_unlock (&mutex[1]);
|
||||
pthread_mutex_unlock (&mutex[2]);
|
||||
sched_yield ();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
pthread_t forward, backward;
|
||||
int status;
|
||||
|
||||
#ifdef sun
|
||||
/*
|
||||
* On Solaris 2.5, threads are not timesliced. To ensure
|
||||
* that our threads can run concurrently, we need to
|
||||
* increase the concurrency level.
|
||||
*/
|
||||
DPRINTF (("Setting concurrency level to 2\n"));
|
||||
thr_setconcurrency (2);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If the first argument is absent, or nonzero, a backoff
|
||||
* algorithm will be used to avoid deadlock. If the first
|
||||
* argument is zero, the program will deadlock on a lock
|
||||
* "collision."
|
||||
*/
|
||||
if (argc > 1)
|
||||
backoff = atoi (argv[1]);
|
||||
|
||||
/*
|
||||
* If the second argument is absent, or zero, the two
|
||||
* threads run "at speed." On some systems, especially
|
||||
* uniprocessors, one thread may complete before the other
|
||||
* has a chance to run, and you won't see a deadlock or
|
||||
* backoffs. In that case, try running with the argument set
|
||||
* to a positive number to cause the threads to call
|
||||
* sched_yield() at each lock; or, to make it even more
|
||||
* obvious, set to a negative number to cause the threads to
|
||||
* call sleep(1) instead.
|
||||
*/
|
||||
if (argc > 2)
|
||||
yield_flag = atoi (argv[2]);
|
||||
status = pthread_create (
|
||||
&forward, NULL, lock_forward, NULL);
|
||||
if (status != 0)
|
||||
err_abort (status, "Create forward");
|
||||
status = pthread_create (
|
||||
&backward, NULL, lock_backward, NULL);
|
||||
if (status != 0)
|
||||
err_abort (status, "Create backward");
|
||||
pthread_exit (NULL);
|
||||
}
|
||||
144
barrier.c
Normal file
144
barrier.c
Normal 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
42
barrier.h
Normal 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
115
barrier_main.c
Normal 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
66
cancel.c
Normal 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
130
cancel_async.c
Normal 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
100
cancel_cleanup.c
Normal 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
68
cancel_disable.c
Normal 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
115
cancel_subcontract.c
Normal 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
95
cond.c
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* cond.c
|
||||
*
|
||||
* Demonstrate a simple condition variable wait.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include "errors.h"
|
||||
|
||||
typedef struct my_struct_tag {
|
||||
pthread_mutex_t mutex; /* Protects access to value */
|
||||
pthread_cond_t cond; /* Signals change to value */
|
||||
int value; /* Access protected by mutex */
|
||||
} my_struct_t;
|
||||
|
||||
my_struct_t data = {
|
||||
PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
|
||||
|
||||
int hibernation = 1; /* Default to 1 second */
|
||||
|
||||
/*
|
||||
* Thread start routine. It will set the main thread's predicate
|
||||
* and signal the condition variable.
|
||||
*/
|
||||
void *
|
||||
wait_thread (void *arg)
|
||||
{
|
||||
int status;
|
||||
|
||||
sleep (hibernation);
|
||||
status = pthread_mutex_lock (&data.mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Lock mutex");
|
||||
data.value = 1; /* Set predicate */
|
||||
status = pthread_cond_signal (&data.cond);
|
||||
if (status != 0)
|
||||
err_abort (status, "Signal condition");
|
||||
status = pthread_mutex_unlock (&data.mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Unlock mutex");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
int status;
|
||||
pthread_t wait_thread_id;
|
||||
struct timespec timeout;
|
||||
|
||||
/*
|
||||
* If an argument is specified, interpret it as the number
|
||||
* of seconds for wait_thread to sleep before signaling the
|
||||
* condition variable. You can play with this to see the
|
||||
* condition wait below time out or wake normally.
|
||||
*/
|
||||
if (argc > 1)
|
||||
hibernation = atoi (argv[1]);
|
||||
|
||||
/*
|
||||
* Create wait_thread.
|
||||
*/
|
||||
status = pthread_create (&wait_thread_id, NULL, wait_thread, NULL);
|
||||
if (status != 0)
|
||||
err_abort (status, "Create wait thread");
|
||||
|
||||
/*
|
||||
* Wait on the condition variable for 2 seconds, or until
|
||||
* signaled by the wait_thread. Normally, wait_thread
|
||||
* should signal. If you raise "hibernation" above 2
|
||||
* seconds, it will time out.
|
||||
*/
|
||||
timeout.tv_sec = time (NULL) + 2;
|
||||
timeout.tv_nsec = 0;
|
||||
status = pthread_mutex_lock (&data.mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Lock mutex");
|
||||
|
||||
while (data.value == 0) {
|
||||
status = pthread_cond_timedwait (
|
||||
&data.cond, &data.mutex, &timeout);
|
||||
if (status == ETIMEDOUT) {
|
||||
printf ("Condition wait timed out.\n");
|
||||
break;
|
||||
}
|
||||
else if (status != 0)
|
||||
err_abort (status, "Wait on condition");
|
||||
}
|
||||
|
||||
if (data.value != 0)
|
||||
printf ("Condition was signaled.\n");
|
||||
status = pthread_mutex_unlock (&data.mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Unlock mutex");
|
||||
return 0;
|
||||
}
|
||||
34
cond_attr.c
Normal file
34
cond_attr.c
Normal 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
40
cond_dynamic.c
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* cond_dynamic.c
|
||||
*
|
||||
* Demonstrate dynamic initialization of a condition variable.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include "errors.h"
|
||||
|
||||
/*
|
||||
* Define a structure, with a mutex and condition variable.
|
||||
*/
|
||||
typedef struct my_struct_tag {
|
||||
pthread_mutex_t mutex; /* Protects access to value */
|
||||
pthread_cond_t cond; /* Signals change to value */
|
||||
int value; /* Access protected by mutex */
|
||||
} my_struct_t;
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
my_struct_t *data;
|
||||
int status;
|
||||
|
||||
data = malloc (sizeof (my_struct_t));
|
||||
if (data == NULL)
|
||||
errno_abort ("Allocate structure");
|
||||
status = pthread_mutex_init (&data->mutex, NULL);
|
||||
if (status != 0)
|
||||
err_abort (status, "Init mutex");
|
||||
status = pthread_cond_init (&data->cond, NULL);
|
||||
if (status != 0)
|
||||
err_abort (status, "Init condition");
|
||||
status = pthread_cond_destroy (&data->cond);
|
||||
if (status != 0)
|
||||
err_abort (status, "Destroy condition");
|
||||
status = pthread_mutex_destroy (&data->mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Destroy mutex");
|
||||
(void)free (data);
|
||||
return status;
|
||||
}
|
||||
27
cond_static.c
Normal file
27
cond_static.c
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* cond_static.c
|
||||
*
|
||||
* Demonstrate static initialization of a condition variable.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include "errors.h"
|
||||
|
||||
/*
|
||||
* Declare a structure, with a mutex and condition variable,
|
||||
* statically initialized. This is the same as using
|
||||
* pthread_mutex_init and pthread_cond_init, with the default
|
||||
* attributes.
|
||||
*/
|
||||
typedef struct my_struct_tag {
|
||||
pthread_mutex_t mutex; /* Protects access to value */
|
||||
pthread_cond_t cond; /* Signals change to value */
|
||||
int value; /* Access protected by mutex */
|
||||
} my_struct_t;
|
||||
|
||||
my_struct_t data = {
|
||||
PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
485
crew.c
Normal file
485
crew.c
Normal 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
49
errors.h
Normal 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
84
flock.c
Normal 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
42
getlogin.c
Normal 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
28
hello.c
Normal 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
56
inertia.c
Normal 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
36
lifecycle.c
Normal 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
35
mutex_attr.c
Normal 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
33
mutex_dynamic.c
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* mutex_dynamic.c
|
||||
*
|
||||
* Demonstrate dynamic initialization of a mutex.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include "errors.h"
|
||||
|
||||
/*
|
||||
* Define a structure, with a mutex.
|
||||
*/
|
||||
typedef struct my_struct_tag {
|
||||
pthread_mutex_t mutex; /* Protects access to value */
|
||||
int value; /* Access protected by mutex */
|
||||
} my_struct_t;
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
my_struct_t *data;
|
||||
int status;
|
||||
|
||||
data = malloc (sizeof (my_struct_t));
|
||||
if (data == NULL)
|
||||
errno_abort ("Allocate structure");
|
||||
status = pthread_mutex_init (&data->mutex, NULL);
|
||||
if (status != 0)
|
||||
err_abort (status, "Init mutex");
|
||||
status = pthread_mutex_destroy (&data->mutex);
|
||||
if (status != 0)
|
||||
err_abort (status, "Destroy mutex");
|
||||
(void)free (data);
|
||||
return status;
|
||||
}
|
||||
23
mutex_static.c
Normal file
23
mutex_static.c
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* mutex_static.c
|
||||
*
|
||||
* Demonstrate static initialization of a mutex.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include "errors.h"
|
||||
|
||||
/*
|
||||
* Declare a structure, with a mutex, statically initialized. This is the
|
||||
* same as using pthread_mutex_init, with the default attributes.
|
||||
*/
|
||||
typedef struct my_struct_tag {
|
||||
pthread_mutex_t mutex; /* Protects access to value */
|
||||
int value; /* Access protected by mutex */
|
||||
} my_struct_t;
|
||||
|
||||
my_struct_t data = {PTHREAD_MUTEX_INITIALIZER, 0};
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
71
once.c
Normal file
71
once.c
Normal 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
263
pipe.c
Normal 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
96
putchar.c
Normal 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
276
rwlock.c
Normal 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
49
rwlock.h
Normal 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
167
rwlock_main.c
Normal 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
156
rwlock_try_main.c
Normal 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
139
sched_attr.c
Normal 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
88
sched_thread.c
Normal 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
116
semaphore_signal.c
Normal 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
82
semaphore_wait.c
Normal 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
328
server.c
Normal 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
106
sigev_thread.c
Normal 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
98
sigwait.c
Normal 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
342
susp.c
Normal 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
31
thread.c
Normal 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
69
thread_attr.c
Normal 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
29
thread_error.c
Normal 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
120
trylock.c
Normal 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
118
tsd_destructor.c
Normal 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
81
tsd_once.c
Normal 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
307
workq.c
Normal 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
59
workq.h
Normal 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
144
workq_main.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user