12.3 Thread Management

Team-FLY

A thread package usually includes functions for thread creation and thread destruction, scheduling, enforcement of mutual exclusion and conditional waiting. A typical thread package also contains a runtime system to manage threads transparently (i.e., the user is not aware of the runtime system). When a thread is created, the runtime system allocates data structures to hold the thread's ID, stack and program counter value. The thread's internal data structure might also contain scheduling and usage information. The threads for a process share the entire address space of that process. They can modify global variables , access open file descriptors, and cooperate or interfere with each other in other ways.

POSIX threads are sometimes called pthreads because all the thread functions start with pthread . Table 12.1 summarizes the basic POSIX thread management functions introduced in this section. The programs listed in Section 12.1 used pthread_create to create threads and pthread_join to wait for threads to complete. Other management functions deal with thread termination, signals and comparison of thread IDs. Section 12.6 introduces the functions related to thread attribute objects, and Chapter 13 covers thread synchronization functions.

Table 12.1. POSIX thread management functions.

POSIX function

description

pthread_cancel

terminate another thread

pthread_create

create a thread

pthread_detach

set thread to release resources

pthread_equal

test two thread IDs for equality

pthread_exit

exit a thread without exiting process

pthread_kill

send a signal to a thread

pthread_join

wait for a thread

pthread_self

find out own thread ID

Most POSIX thread functions return 0 if successful and a nonzero error code if unsuccessful . They do not set errno , so the caller cannot use perror to report errors. Programs can use strerror if the issues of thread safety discussed in Section 12.4 are addressed. The POSIX standard specifically states that none of the POSIX thread functions returns EINTR and that POSIX thread functions do not have to be restarted if interrupted by a signal.

12.3.1 Referencing threads by ID

POSIX threads are referenced by an ID of type pthread_t . A thread can find out its ID by calling pthread_self .

  SYNOPSIS  #include <pthread.h>   pthread_t pthread_self(void);  POSIX:THR  

The pthread_self function returns the thread ID of the calling thread. No errors are defined for pthread_self .

Since pthread_t may be a structure, use pthread_equal to compare thread IDs for equality. The parameters of pthread_equal are the thread IDs to be compared.

  SYNOPSIS  #include <pthread.h>    pthread_t pthread_equal(thread_t t1, pthread_t t2);  POSIX:THR  

If t1 equals t2 , pthread_equal returns a nonzero value. If the thread IDs are not equal, pthread_equal returns 0. No errors are defined for pthread_equal .

Example 12.3

In the following code segment, a thread outputs a message if its thread ID is mytid .

 pthread_t mytid; if (pthread_equal(pthread_self(), mytid))    printf("My thread ID matches mytid\n"); 

12.3.2 Creating a thread

The pthread_create function creates a thread. Unlike some thread facilities, such as those provided by the Java programming language, the POSIX pthread_create automatically makes the thread runnable without requiring a separate start operation. The thread parameter of pthread_create points to the ID of the newly created thread. The attr parameter represents an attribute object that encapsulates the attributes of a thread. If attr is NULL , the new thread has the default attributes. Section 12.6 discusses the setting of thread attributes. The third parameter, start_routine , is the name of a function that the thread calls when it begins execution. The start_routine takes a single parameter specified by arg , a pointer to void . The start_routine returns a pointer to void , which is treated as an exit status by pthread_join .

  SYNOPSIS  #include <pthread.h>   int pthread_create(pthread_t *restrict thread,                      const pthread_attr_t *restrict attr,                      void *(*start_routine)(void *), void *restrict arg);  POSIX:THR  

If successful, pthread_create returns 0. If unsuccessful, pthread_create returns a nonzero error code. The following table lists the mandatory errors for pthread_create .

error

cause

EAGAIN

system did not have the resources to create the thread, or would exceed system limit on total number of threads in a process

EINVAL

attr parameter is invalid

EPERM

caller does not have the appropriate permissions to set scheduling policy or parameters specified by attr

Do not let the prototype of pthread_create intimidate you ”threads are easy to create and use.

Example 12.4

The following code segment creates a thread to execute the function processfd after opening the my.dat file for reading.

 void *processfd(void *arg); int error; int fd; pthread_t tid; if ((fd = open("my.dat", O_RDONLY)) == -1)    perror("Failed to open my.dat"); else if (error = pthread_create(&tid, NULL, processfd, &fd))    fprintf(stderr, "Failed to create thread: %s\n", strerror(error)); else    printf("Thread created\n"); 

12.3.3 Detaching and joining

When a thread exits, it does not release its resources unless it is a detached thread. The pthread_detach function sets a thread's internal options to specify that storage for the thread can be reclaimed when the thread exits. Detached threads do not report their status when they exit. Threads that are not detached are joinable and do not release all their resources until another thread calls pthread_join for them or the entire process exits. The pthread_join function causes the caller to wait for the specified thread to exit, similar to waitpid at the process level. To prevent memory leaks, long-running programs should eventually call either pthread_detach or pthread_join for every thread.

The pthread_detach function has a single parameter, thread , the thread ID of the thread to be detached.

  SYNOPSIS  #include <pthread.h>    int pthread_detach(pthread_t thread);  POSIX:THR  

If successful, pthread_detach returns 0. If unsuccessful, pthread_detach returns a nonzero error code. The following table lists the mandatory errors for pthread_detach .

error

cause

EINVAL

thread does not correspond to a joinable thread

ESRCH

no thread with ID thread

Example 12.5

The following code segment creates and then detaches a thread to execute the function processfd .

 void *processfd(void *arg); int error; int fd pthread_t tid; if (error = pthread_create(&tid, NULL, processfd, &fd))    fprintf(stderr, "Failed to create thread: %s\n", strerror(error)); else if (error = pthread_detach(tid))    fprintf(stderr, "Failed to detach thread: %s\n", strerror(error)); 
Example 12.6 detachfun.c

When detachfun is executed as a thread, it detaches itself.

 #include <pthread.h> #include <stdio.h> void *detachfun(void *arg) {     int i = *((int *)(arg));     if (!pthread_detach(pthread_self()))         return NULL;     fprintf(stderr, "My argument is %d\n", i);     return NULL; } 

A nondetached thread's resources are not released until another thread calls pthread_join with the ID of the terminating thread as the first parameter. The pthread_join function suspends the calling thread until the target thread, specified by the first parameter, terminates. The value_ptr parameter provides a location for a pointer to the return status that the target thread passes to pthread_exit or return . If value_ptr is NULL , the caller does not retrieve the target thread return status.

  SYNOPSIS  #include <pthread.h>    int pthread_join(pthread_t thread, void **value_ptr);  POSIX:THR  

If successful, pthread_join returns 0. If unsuccessful, pthread_join returns a nonzero error code. The following table lists the mandatory errors for pthread_join .

error

cause

EINVAL

thread does not correspond to a joinable thread

ESRCH

no thread with ID thread

Example 12.7

The following code illustrates how to retrieve the value passed to pthread_exit by a terminating thread.

 int error; int *exitcodep; pthread_t tid; if (error = pthread_join(tid, &exitcodep))    fprintf(stderr, "Failed to join thread: %s\n", strerror(error)); else    fprintf(stderr, "The exit code was %d\n", *exitcodep); 
Exercise 12.8

What happens if a thread executes the following?

 pthread_join(pthread_self()); 

Answer:

Assuming the thread was joinable (not detached), this statement creates a deadlock. Some implementations detect a deadlock and force pthread_join to return with the error EDEADLK . However, this detection is not required by the POSIX:THR Extension.

Calling pthread_join is not the only way for the main thread to block until the other threads have completed. The main thread can use a semaphore or one of the methods discussed in Section 16.6 to wait for all threads to finish.

12.3.4 Exiting and cancellation

The process can terminate by calling exit directly, by executing return from main , or by having one of the other process threads call exit . In any of these cases, all threads terminate. If the main thread has no work to do after creating other threads, it should either block until all threads have completed or call pthread_exit(NULL) .

A call to exit causes the entire process to terminate; a call to pthread_exit causes only the calling thread to terminate. A thread that executes return from its top level implicitly calls pthread_exit with the return value (a pointer) serving as the parameter to pthread_exit . A process will exit with a return status of 0 if its last thread calls pthread_exit .

The value_ptr value is available to a successful pthread_join . However, the value_ptr in pthread_exit must point to data that exists after the thread exits, so the thread should not use a pointer to automatic local data for value_ptr .

  SYNOPSIS  #include <pthread.h>    void pthread_exit(void *value_ptr);  POSIX:THR  

POSIX does not define any errors for pthread_exit .

Threads can force other threads to return through the cancellation mechanism. A thread calls pthread_cancel to request that another thread be canceled . The target thread's type and cancellability state determine the result. The single parameter of pthread_cancel is the thread ID of the target thread to be canceled. The pthread_cancel function does not cause the caller to block while the cancellation completes. Rather, pthread_cancel returns after making the cancellation request.

  SYNOPSIS  #include <pthread.h>    int pthread_cancel(pthread_t thread);  POSIX:THR  

If successful, pthread_cancel returns 0. If unsuccessful, pthread_cancel returns a nonzero error code. No mandatory errors are defined for pthread_cancel .

What happens when a thread receives a cancellation request depends on its state and type. If a thread has the PTHREAD_CANCEL_ENABLE state, it receives cancellation requests . On the other hand, if the thread has the PTHREAD_CANCEL_DISABLE state, the cancellation requests are held pending. By default, threads have the PTHREAD_CANCEL_ENABLE state.

The pthread_setcancelstate function changes the cancellability state of the calling thread. The pthread_setcancelstate takes two parameters: state , specifying the new state to set; and oldstate , a pointer to an integer for holding the previous state.

  SYNOPSIS  #include <pthread.h>    int pthread_setcancelstate(int state, int *oldstate);  POSIX:THR  

If successful, pthread_setcancelstate returns 0. If unsuccessful, it returns a nonzero error code. No mandatory errors are defined for pthread_setcancelstate .

Program 12.3 shows a modification of the processfd function of Program 12.1 that explicitly disables cancellation before it calls docommand , to ensure that the command won't be canceled midstream. The original processfd always returns NULL . The processfdcancel function returns a pointer other than NULL if it cannot change the cancellation state. This function should not return a pointer to an automatic local variable, since local variables are deallocated when the function returns or the thread exits. Program 12.3 uses a parameter passed by the calling thread to return the pointer.

Program 12.3 processfdcancel.c

This function monitors a file descriptor for input and calls docommand to process the result. It explicitly disables cancellation before calling docommand .

 #include <pthread.h> #include "restart.h" #define BUFSIZE 1024 void docommand(char *cmd, int cmdsize); void *processfdcancel(void *arg) { /* process commands with cancellation */    char buf[BUFSIZE];    int fd;    ssize_t nbytes;    int newstate, oldstate;    fd = *((int *)(arg));    for ( ; ; )  {       if ((nbytes = r_read(fd, buf, BUFSIZE)) <= 0)          break;       if (pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate))          return arg;       docommand(buf, nbytes);       if (pthread_setcancelstate(oldstate, &newstate))          return arg;    }    return NULL; } 

As a general rule, a function that changes its cancellation state or its type should restore the value before returning . A caller cannot make reliable assumptions about the program behavior unless this rule is observed . The processfdcancel function saves the old state and restores it rather than just enabling cancellation after calling docommand .

Cancellation can cause difficulties if a thread holds resources such as a lock or an open file descriptor that must be released before exiting. A thread maintains a stack of cleanup routines using pthread_cleanup_push and pthread_cleanup_pop . (We do not discuss these here.) Although a canceled thread can execute a cleanup function before exiting (not discussed here), it is not always feasible to release resources in an exit handler. Also, there may be points in the execution at which an exit would leave the program in an unacceptable state. The cancellation type allows a thread to control the point when it exits in response to a cancellation request. When its cancellation type is PTHREAD_CANCEL_ASYNCHRONOUS , the thread can act on the cancellation request at any time. In contrast, a cancellation type of PTHREAD_CANCEL_DEFERRED causes the thread to act on cancellation requests only at specified cancellation points. By default, threads have the PTHREAD_CANCEL_DEFERRED type.

The pthread_setcanceltype function changes the cancellability type of a thread as specified by its type parameter. The oldtype parameter is a pointer to a location for saving the previous type. A thread can set a cancellation point at a particular place in the code by calling pthread_testcancel . Certain blocking functions, such as read , are automatically treated as cancellation points. A thread with the PTHREAD_CANCEL_DEFERRED type accepts pending cancellation requests when it reaches such a cancellation point.

  SYNOPSIS  #include <pthread.h>    int pthread_setcanceltype(int type, int *oldtype);    void pthread_testcancel(void);  POSIX:THR  

If successful, pthread_setcanceltype returns 0. If unsuccessful, it returns a nonzero error code. No mandatory errors are defined for pthread_setcanceltype . The pthread_testcancel has no return value.

12.3.5 Passing parameters to threads and returning values

The creator of a thread may pass a single parameter to a thread at creation time, using a pointer to void . To communicate multiple values, the creator must use a pointer to an array or a structure. Program 12.4 illustrates how to pass a pointer to an array. The main program passes an array containing two open file descriptors to a thread that runs copyfilemalloc .

Program 12.4 callcopymalloc.c

This program creates a thread to copy a file .

 #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #define PERMS (S_IRUSR  S_IWUSR) #define READ_FLAGS O_RDONLY #define WRITE_FLAGS (O_WRONLY  O_CREAT  O_TRUNC) void *copyfilemalloc(void *arg); int main (int argc, char *argv[]) {        /* copy fromfile to tofile */    int *bytesptr;    int error;    int fds[2];    pthread_t tid;    if (argc != 3) {       fprintf(stderr, "Usage: %s fromfile tofile\n", argv[0]);       return 1;    }    if (((fds[0] = open(argv[1], READ_FLAGS)) == -1)         ((fds[1] = open(argv[2], WRITE_FLAGS, PERMS)) == -1)) {       perror("Failed to open the files");       return 1;    }    if (error = pthread_create(&tid, NULL, copyfilemalloc, fds)) {       fprintf(stderr, "Failed to create thread: %s\n", strerror(error));       return 1;    }    if (error = pthread_join(tid, (void **)&bytesptr)) {       fprintf(stderr, "Failed to join thread: %s\n", strerror(error));       return 1;    }    printf("Number of bytes copied: %d\n", *bytesptr);    return 0; } 

Program 12.5 shows an implementation of copyfilemalloc , a function that reads from one file and outputs to another file. The arg parameter holds a pointer to a pair of open descriptors representing the source and destination files. The variables bytesp , infd and outfd are allocated on copyfilemalloc 's local stack and are not directly accessible to other threads.

Program 12.5 also illustrates a strategy for returning values from the thread. The thread allocates memory space for returning the total number of bytes copied since it is not allowed to return a pointer to its local variables. POSIX requires that malloc be thread-safe. The copyfilemalloc function returns the bytesp pointer, which is equivalent to calling pthread_exit . It is the responsibility of the calling program ( callcopymalloc ) to free this space when it has finished using it. In this case, the program terminates, so it is not necessary to call free .

Program 12.5 copyfilemalloc.c

The copyfilemalloc function copies the contents of one file to another by calling the copyfile function of Program 4.6 on page 100. It returns the number of bytes copied by dynamically allocating space for the return value .

 #include <stdlib.h> #include <unistd.h> #include "restart.h" void *copyfilemalloc(void *arg)  { /* copy infd to outfd with return value */    int *bytesp;    int infd;    int outfd;    infd = *((int *)(arg));    outfd = *((int *)(arg) + 1);    if ((bytesp = (int *)malloc(sizeof(int))) == NULL)       return NULL;    *bytesp = copyfile(infd, outfd);    r_close(infd);    r_close(outfd);    return bytesp; } 
Exercise 12.9

What happens if copyfilemalloc stores the byte count in a variable with static storage class and returns a pointer to this static variable instead of dynamically allocating space for it?

Answer:

The program still works since only one thread is created. However, in a program with two copyfilemalloc threads, both store the byte count in the same place and one overwrites the other's value.

When a thread allocates space for a return value, some other thread is responsible for freeing that space. Whenever possible, a thread should clean up its own mess rather than requiring another thread to do it. It is also inefficient to dynamically allocate space to hold a single integer. An alternative to having the thread allocate space for the return value is for the creating thread to do it and pass a pointer to this space in the argument parameter of the thread. This approach avoids dynamic allocation completely if the space is on the stack of the creating thread.

Program 12.6 creates a copyfilepass thread to copy a file. The parameter to the thread is now an array of size 3. The first two entries of the array hold the file descriptors as in Program 12.4. The third array element stores the number of bytes copied. Program 12.6 can retrieve this value either through the array or through the second parameter of pthread_join . Alternatively, callcopypass could pass an array of size 2, and the thread could store the return value over one of the incoming file descriptors.

Program 12.6 callcopypass.c

A program that creates a thread to copy a file. The parameter of the thread is an array of three integers used for two file descriptors and the number of bytes copied .

 #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #define PERMS (S_IRUSR  S_IWUSR) #define READ_FLAGS O_RDONLY #define WRITE_FLAGS (O_WRONLY  O_CREAT  O_TRUNC) void *copyfilepass(void *arg); int main (int argc, char *argv[]) {    int *bytesptr;    int error;    int targs[3];    pthread_t tid;    if (argc != 3) {       fprintf(stderr, "Usage: %s fromfile tofile\n", argv[0]);       return 1;    }    if (((targs[0] = open(argv[1], READ_FLAGS)) == -1)         ((targs[1] = open(argv[2], WRITE_FLAGS, PERMS)) == -1)) {       perror("Failed to  open the files");       return 1;    }    if (error = pthread_create(&tid, NULL, copyfilepass, targs)) {       fprintf(stderr, "Failed to create thread: %s\n", strerror(error));       return 1;    }    if (error = pthread_join(tid, (void **)&bytesptr)) {       fprintf(stderr, "Failed to join thread: %s\n", strerror(error));       return 1;    }    printf("Number of bytes copied: %d\n", *bytesptr);    return 0; } 

The copyfilepass function of Program 12.7 uses an alternative way of accessing the pieces of the argument. Compare this with the method used by the copyfilemalloc function of Program 12.5.

Program 12.7 copyfilepass.c

A thread that can be used by callcopypass to copy a file .

 #include <unistd.h> #include "restart.h" void *copyfilepass(void *arg)  {    int *argint;    argint = (int *)arg;    argint[2] = copyfile(argint[0], argint[1]);    r_close(argint[0]);    r_close(argint[1]);    return argint + 2; } 
Exercise 12.10

Why have copyfilepass return a pointer to the number of bytes copied when callcopypass can access this value as args[2] ?

Answer:

If a thread other than the creating thread joins with copyfilepass , it has access to the number of bytes copied through the parameter to pthread_join .

Program 12.8 shows a parallel file-copy program that uses the thread in Program 12.7. The main program has three command-line arguments: an input file basename, an output file basename and the number of files to copy. The program creates numcopiers threads. Thread i copies infile.i to outfile.i .

Exercise 12.11

What happens in Program 12.8 if a write call in copyfile of copyfilepass fails?

Answer:

The copyfilepass returns the number of bytes successfully copied, and the main program does not detect an error. You can address the issue by having copyfilepass return an error value and pass the number of bytes written in one of the elements of the array used as a parameter for thread creation.

When creating multiple threads, do not reuse the variable holding a thread's parameter until you are sure that the thread has finished accessing the parameter . Because the variable is passed by reference, it is a good practice to use a separate variable for each thread.

Program 12.8 copymultiple.c

A program that creates threads to copy multiple file descriptors .

 #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #define MAXNAME 80 #define R_FLAGS O_RDONLY #define W_FLAGS (O_WRONLY  O_CREAT) #define W_PERMS (S_IRUSR  S_IWUSR) typedef struct {    int args[3];    pthread_t tid; } copy_t; void *copyfilepass(void *arg); int main(int argc, char *argv[]) {    int *bytesp;    copy_t *copies;    int error;    char filename[MAXNAME];    int i;    int numcopiers;    int totalbytes = 0;    if (argc != 4) {       fprintf(stderr, "Usage: %s infile outfile copies\n", argv[0]);       return 1;    }    numcopiers = atoi(argv[3]);    if ((copies = (copy_t *)calloc(numcopiers, sizeof(copy_t))) == NULL) {       perror("Failed to allocate copier space");       return 1;    }               /* open the source and destination files and create the threads */    for (i = 0; i < numcopiers; i++) {       copies[i].tid = pthread_self();       /* cannot be value for new thread */       if (snprintf(filename, MAXNAME, "%s.%d", argv[1], i+1) == MAXNAME) {          fprintf(stderr, "Input filename %s.%d too long", argv[1], i + 1);          continue;       }       if ((copies[i].args[0] = open(filename, R_FLAGS)) == -1) {          fprintf(stderr, "Failed to open source file %s: %s\n",                          filename, strerror(errno));          continue;       }      if (snprintf(filename, MAXNAME, "%s.%d", argv[2], i+1) == MAXNAME) {          fprintf(stderr, "Output filename %s.%d too long", argv[2], i + 1);          continue;       }       if ((copies[i].args[1] = open(filename, W_FLAGS, W_PERMS)) == -1) {          fprintf(stderr, "Failed to open destination file %s: %s\n",                          filename, strerror(errno));          continue;       }       if (error = pthread_create((&copies[i].tid), NULL,                                   copyfilepass, copies[i].args)) {          fprintf(stderr, "Failed to create thread %d: %s\n", i + 1,                  strerror(error));          copies[i].tid = pthread_self();    /* cannot be value for new thread */       }    }                      /* wait for the threads to finish and report total bytes */    for (i = 0; i < numcopiers; i++) {       if (pthread_equal(copies[i].tid, pthread_self()))        /* not created */          continue;       if (error = pthread_join(copies[i].tid, (void**)&bytesp)) {          fprintf(stderr, "Failed to join thread %d\n", i);          continue;       }       if (bytesp == NULL) {          fprintf(stderr, "Thread %d failed to return status\n", i);          continue;       }       printf("Thread %d copied %d bytes from %s.%d to %s.%d\n",              i, *bytesp, argv[1], i + 1, argv[2], i + 1);       totalbytes += *bytesp;    }    printf("Total bytes copied = %d\n", totalbytes);    return 0; } 

Program 12.9 shows a simple example of what can go wrong. The program creates 10 threads that each output the value of their parameter. The main program uses the thread creation loop index i as the parameter it passes to the threads. Each thread prints the value of the parameter it received. A thread can get an incorrect value if the main program changes i before the thread has a chance to print it.

Exercise 12.12

Run Program 12.9 and examine the results. What parameter value is reported by each thread?

Answer:

The results vary, depending on how the system schedules threads. One possibility is that main completes the loop creating the threads before any thread prints the value of the parameter. In this case, all the threads print the value 10.

Program 12.9 badparameters.c

A program that incorrectly passes parameters to multiple threads .

 #include <pthread.h> #include <stdio.h> #include <string.h> #define NUMTHREADS 10 static void *printarg(void *arg) {    fprintf(stderr, "Thread received %d\n", *(int *)arg);    return NULL; } int main (void) {        /* program incorrectly passes parameters to threads */    int error;    int i;    int j;    pthread_t tid[NUMTHREADS];    for (i = 0; i < NUMTHREADS; i++)       if (error = pthread_create(tid + i, NULL, printarg, (void *)&i)) {          fprintf(stderr, "Failed to create thread: %s\n", strerror(error));          tid[i] = pthread_self();       }    for (j = 0; j < NUMTHREADS; j++)       if (pthread_equal(pthread_self(), tid[j]))          continue;       if (error = pthread_join(tid[j], NULL))          fprintf(stderr, "Failed to join thread: %s\n", strerror(error));    printf("All threads done\n");    return 0; } 
Exercise 12.13

For each of the following, start with Program 12.9 and make the specified modifications. Predict the output, and then run the program to see if you are correct.

1

Run the original program without any modification.

2

Put a call to sleep(1); at the start of printarg .

3

Put a call to sleep(1); inside the first for loop after the call to pthread_create .

4

Put a call to sleep(1); after the first for loop.

5.-8.

Repeat each of the items above, using i as the loop index rather than j .

Answer:

The results may vary if it takes more than a second for the threads to execute. On a fast enough system, the result will be something like the following.

  1. Output described in Exercise 12.12.

  2. Each thread outputs the value 10, the value of i when main has finished its loop.

  3. Each thread outputs the correct value since it executes before the value of i changes.

  4. Same as in Exercise 12.12.

  5. All threads output the value 0, the value of i when main waits for the first thread to terminate. The results may vary.

  6. Same as 5.

  7. Same as 3.

  8. Same as 4.

Exercise 12.14 whichexit.c

The whichexit function can be executed as a thread.

 #include <errno.h> #include <pthread.h> #include <stdlib.h> #include <string.h> void *whichexit(void *arg) {    int n;    int np1[1];    int *np2;    char s1[10];    char s2[] = "I am done";    n = 3;    np1[0] = n;    np2 = (int *)malloc(sizeof(int *));    *np2 = n;    strcpy(s1, "Done");    return(NULL); } 

Which of the following would be safe replacements for NULL as the parameter to pthread_exit? Assume no errors occur.

  1. n

  2. &n

  3. (int *)n

  4. np1

  5. np2

  6. s1

  7. s2

  8. "This works"

  9. strerror(EINTR)

Answer:

  1. The return value is a pointer, not an integer, so this is invalid.

  2. The integer n has automatic storage class, so it is illegal to access it after the function terminates.

  3. This is a common way to return an integer from a thread. The integer is cast to a pointer. When another thread calls pthread_join for this thread, it casts the pointer back to an integer. While this will probably work in most implementations, it should be avoided. The C standard [56, Section 6.3.2.3] says that an integer may be converted to a pointer or a pointer to an integer, but the result is implementation defined. It does not guarantee that the result of converting an integer to a pointer and back again yields the original integer.

  4. The array np1 has automatic storage class, so it is illegal to access the array after the function terminates.

  5. This is safe since the dynamically allocated space will be available until it is freed.

  6. The array s1 has automatic storage class, so it is illegal to access the array after the function terminates.

  7. The array s2 has automatic storage class, so it is illegal to access the array after the function terminates.

  8. This is valid in C, since string literals have static storage duration.

  9. This is certainly invalid if strerror is not thread-safe. Even on a system where strerror is thread-safe, the string produced is not guaranteed to be available after the thread terminates.

Team-FLY


Unix Systems Programming
UNIX Systems Programming: Communication, Concurrency and Threads
ISBN: 0130424110
EAN: 2147483647
Year: 2003
Pages: 274

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net