Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
2.  Basic Threads Programming The Threads Library Create a Key for Thread-Specific Data pthread_key_create(3THR)  Previous   Contents   Next 
   
 

An optional destructor function, destructor, can be used to free stale storage. When a key has a non-NULL destructor function and the thread has a non-NULL value associated with that key, the destructor function is called with the current associated value when the thread exits. The order in which the destructor functions are called is unspecified.

Return Values

pthread_key_create() returns zero after completing successfully. Any other return value indicates that an error occurred. When any of the following conditions occur, pthread_key_create() fails and returns the corresponding value.

 

EAGAIN

The key name space is exhausted.

 

ENOMEM

Not enough virtual memory is available in this process to create a new key.

Delete the Thread-Specific Data Key

pthread_key_delete(3THR)

Use pthread_key_delete(3THR) to destroy an existing thread-specific data key. Any memory associated with the key can be freed because the key has been invalidated and will return an error if ever referenced. There is no comparable function in Solaris threads.

Prototype:
int	pthread_key_delete(pthread_key_t key);
#include <pthread.h>

pthread_key_t key;
int ret;

/* key previously created */
ret = pthread_key_delete(key); 

Once a key has been deleted, any reference to it with the pthread_setspecific() or pthread_getspecific() call yields undefined results.

It is the responsibility of the programmer to free any thread-specific resources before calling the delete function. This function does not invoke any of the destructors.

Return Values

pthread_key_delete() returns zero after completing successfully. Any other return value indicates that an error occurred. When the following condition occurs, pthread_key_create() fails and returns the corresponding value.

 

EINVAL

The key value is invalid.

Set Thread-Specific Data

pthread_setspecific(3THR)

Use pthread_setspecific(3THR) to set the thread-specific binding to the specified thread-specific data key.

Prototype:
int	pthread_setspecific(pthread_key_t key, const void *value);
#include <pthread.h>

pthread_key_t key;
void *value;
int ret;

/* key previously created */
ret = pthread_setspecific(key, value); 

Return Values

pthread_setspecific() returns zero after completing successfully. Any other return value indicates that an error occurred. When any of the following conditions occur, pthread_setspecific() fails and returns the corresponding value.

 

ENOMEM

Not enough virtual memory is available.

 

EINVAL

key is invalid.


Note - pthread_setspecific() does not free its storage. If a new binding is set, the existing binding must be freed; otherwise, a memory leak can occur.


Get Thread-Specific Data

pthread_getspecific(3THR)

Use pthread_getspecific(3THR) to get the calling thread's binding for key, and store it in the location pointed to by value.

Prototype:
void	*pthread_getspecific(pthread_key_t key);
#include <pthread.h>

pthread_key_t key;
void *value;

/* key previously created */
value = pthread_getspecific(key); 

Return Values

No errors are returned.

Global and Private Thread-Specific Data Example

Example 2-2 shows an excerpt from a multithreaded program. This code is executed by any number of threads, but it has references to two global variables, errno and mywindow, that really should be references to items private to each thread.


Example 2-2 Thread-Specific Data--Global but Private

body() {
    ...

    while (write(fd, buffer, size) == -1) {
        if (errno != EINTR) {
            fprintf(mywindow, "%s\n", strerror(errno));
            exit(1);
        }
    }

    ...

}

References to errno should get the system error code from the routine called by this thread, not by some other thread. So, references to errno by one thread refer to a different storage location than references to errno by other threads.

The mywindow variable is intended to refer to a stdio stream connected to a window that is private to the referring thread. So, as with errno, references to mywindow by one thread should refer to a different storage location (and, ultimately, a different window) than references to mywindow by other threads. The only difference here is that the threads library takes care of errno, but the programmer must somehow make this work for mywindow.

The next example shows how the references to mywindow work. The preprocessor converts references to mywindow into invocations of the _mywindow() procedure.

This routine in turn invokes pthread_getspecific(), passing it the mywindow_key global variable (it really is a global variable) and an output parameter, win, that receives the identity of this thread's window.


Example 2-3 Turning Global References Into Private References

thread_key_t mywin_key;

FILE *_mywindow(void) {
    FILE *win;

    win = pthread_getspecific(mywin_key);
    return(win);
}

#define mywindow _mywindow()

void routine_uses_win( FILE *win) {
    ...
}

void thread_start(...) {
    ...
    make_mywin();
    ...
    routine_uses_win( mywindow )
    ...
}

The mywin_key variable identifies a class of variables for which each thread has its own private copy; that is, these variables are thread-specific data. Each thread calls make_mywin() to initialize its window and to arrange for its instance of mywindow to refer to it.

Once this routine is called, the thread can safely refer to mywindow and, after _mywindow(), the thread gets the reference to its private window. So, references to mywindow behave as if they were direct references to data private to the thread.

Example 2-4 shows how to set this up.


Example 2-4 Initializing the Thread-Specific Data

void make_mywindow(void) {
    FILE **win;
    static pthread_once_t mykeycreated = PTHREAD_ONCE_INIT;

    pthread_once(&mykeycreated, mykeycreate);

    win = malloc(sizeof(*win));
    create_window(win, ...);

    pthread_setspecific(mywindow_key, win);
}

void mykeycreate(void) {
    pthread_key_create(&mywindow_key, free_key);
}

void free_key(void *win) {
    free(win);
}

 
 
 
  Previous   Contents   Next