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