reorganize
This commit is contained in:
66
src/ch05/cancel.c
Normal file
66
src/ch05/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
src/ch05/cancel_async.c
Normal file
130
src/ch05/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
src/ch05/cancel_cleanup.c
Normal file
100
src/ch05/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
src/ch05/cancel_disable.c
Normal file
68
src/ch05/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
src/ch05/cancel_subcontract.c
Normal file
115
src/ch05/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");
|
||||
}
|
||||
34
src/ch05/cond_attr.c
Normal file
34
src/ch05/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;
|
||||
}
|
||||
35
src/ch05/mutex_attr.c
Normal file
35
src/ch05/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;
|
||||
}
|
||||
71
src/ch05/once.c
Normal file
71
src/ch05/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;
|
||||
}
|
||||
139
src/ch05/sched_attr.c
Normal file
139
src/ch05/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
src/ch05/sched_thread.c
Normal file
88
src/ch05/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;
|
||||
}
|
||||
69
src/ch05/thread_attr.c
Normal file
69
src/ch05/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;
|
||||
}
|
||||
118
src/ch05/tsd_destructor.c
Normal file
118
src/ch05/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
src/ch05/tsd_once.c
Normal file
81
src/ch05/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);
|
||||
}
|
||||
Reference in New Issue
Block a user