reorganize

This commit is contained in:
Bassem Girgis
2018-10-17 23:41:43 -05:00
parent 6ba9180a62
commit 558e8e22da
57 changed files with 80 additions and 37 deletions

66
src/ch05/cancel.c Normal file
View File

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

130
src/ch05/cancel_async.c Normal file
View File

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

100
src/ch05/cancel_cleanup.c Normal file
View File

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

68
src/ch05/cancel_disable.c Normal file
View File

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

View File

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

34
src/ch05/cond_attr.c Normal file
View File

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

35
src/ch05/mutex_attr.c Normal file
View File

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

71
src/ch05/once.c Normal file
View File

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

139
src/ch05/sched_attr.c Normal file
View File

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

88
src/ch05/sched_thread.c Normal file
View File

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

69
src/ch05/thread_attr.c Normal file
View File

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

118
src/ch05/tsd_destructor.c Normal file
View File

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

81
src/ch05/tsd_once.c Normal file
View File

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