JMU
Thread-Specific Data
in Pthreads


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Review
Overview
Create a Key: pthread_key_create() pthread_key_create
int pthread_key_create(pthread_key_t *key, void (*destructor)(void *)
Purpose:
Create a thread-specific key (and install a destructor)
Details:
key A key that can be used to obtain thread-specific data
destructor The function to call to free the thread-specific memory (or NULL if no destructor is needed)
Return 0 on success; a positive error number on error
#include <pthread.h> pthread.h

Note: destructor() will be invoked automatically by the system when the thread terminates (and passed the address of the thread-specific memory for this key).

Set a Value: pthread_setspecific() pthread_setspecific
int pthread_setspecific(pthread_key_t key, const void *value)
Purpose:
Save a value in thread-specific memory
Details:
key The key used to identify the thread-specific data
value The value to save (which is normally a block of memory that has been previously allocated by the caller)
Return 0 on success; a positive error number on error
#include <pthread.h> pthread.h
Get a Value: pthread_getspecific() pthread_getspecific
void *pthread_getspecific(pthread_key_t key)
Purpose:
Retrieve a value from thread-specific memory
Details:
key The key used to identify the thread-specific data
Return A pointer to the value on success; NULL is there is no thread-specific data associated with the key
#include <pthread.h> pthread.h
A Very, Very Problematic Motivating Example

A Driver with a Single Thread

unixexamples/threadspecificdata/driver.c
        #include <stdio.h>
#include "grader.h"

int
main(void)
{
  printf("Exam 1: %s\n", letter_grade(85.5));

  return 0;
}
        
A Very, Very Problematic Motivating Example (cont.)
unixexamples/threadspecificdata/grader_local.c
        #include "grader.h"
#include <string.h>

char
*letter_grade(float number_grade)
{
  char letter[2];

  if      (number_grade >= 90.0) strncpy(letter, "A", 1);
  else if (number_grade >= 80.0) strncpy(letter, "B", 1);
  else if (number_grade >= 70.0) strncpy(letter, "C", 1);
  else if (number_grade >= 60.0) strncpy(letter, "D", 1);
  else                           strncpy(letter, "F", 1);
  letter[1] = '\0';
  
  return letter;
}
        
A Very Problematic Motivating Example

A Driver with Multiple Threads

unixexamples/threadspecificdata/threaded_driver.c
        #include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "grader.h"

static void
*thread_entry(void *arg)
{
  float number_grade = *(float *)arg;
  //void  *result = (void *)letter_grade(number_grade);
  //return result;

  printf("Thread: %s\n", letter_grade(number_grade));
  return NULL;
}


int
main(void)
{
  char        *letter_main;
  float       number_helper;
  pthread_t   exam_helper;

  // Calculate one letter grade in the main thread
  letter_main = letter_grade(85.5);

  // Calculate (and print) another letter grade in the helper thread
  number_helper = 48.0;
  pthread_create(&exam_helper, NULL, thread_entry, &number_helper);

  // Terminate the helper thread
  pthread_join(exam_helper, NULL);

  // Now that the helper thread has terminated, print the
  // grade that was calculated in the main thread
  printf("Main: %s\n", letter_main);

  return 0;
}
        
A Very Problematic Motivating Example (cont.)
unixexamples/threadspecificdata/grader_static.c
        #include "grader.h"
#include <string.h>



char
*letter_grade(float number_grade)
{
  static char letter[2];

  if      (number_grade >= 90.0) strncpy(letter, "A", 1);
  else if (number_grade >= 80.0) strncpy(letter, "B", 1);
  else if (number_grade >= 70.0) strncpy(letter, "C", 1);
  else if (number_grade >= 60.0) strncpy(letter, "D", 1);
  else                           strncpy(letter, "F", 1);
  letter[1] = '\0';
  
  return letter;
}
        
A Problematic Motivating Example
unixexamples/threadspecificdata/grader_file.c
        #include "grader.h"
#include <string.h>

static char letter[2];


char
*letter_grade(float number_grade)
{
  if      (number_grade >= 90.0) strncpy(letter, "A", 1);
  else if (number_grade >= 80.0) strncpy(letter, "B", 1);
  else if (number_grade >= 70.0) strncpy(letter, "C", 1);
  else if (number_grade >= 60.0) strncpy(letter, "D", 1);
  else                           strncpy(letter, "F", 1);
  letter[1] = '\0';
  
  return letter;
}
        
An Example Using Thread-Specific Data
unixexamples/threadspecificdata/grader_specific.c
        #include "grader.h"
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static pthread_key_t   grader_key;
static pthread_once_t  grader_once = PTHREAD_ONCE_INIT;

static
void free_memory(void *buffer)
{
  free(buffer);
}

static
void create_key(void)
{
  pthread_key_create(&grader_key, free_memory);
}


char
*letter_grade(float number_grade)
{
  char *letter;
  
  // The first caller must create the key for the thread-specific data
  pthread_once(&grader_once, create_key);
  
  // Get the memory for the key
  letter = pthread_getspecific(grader_key);
  
  // If this is the first call from this thread then the memory
  // will be NULL and, hence, must be allocated (and saved)
  if (letter == NULL)
    {
      letter = malloc(2);
      pthread_setspecific(grader_key, letter);
    }

  if      (number_grade >= 90.0) strncpy(letter, "A", 1);
  else if (number_grade >= 80.0) strncpy(letter, "B", 1);
  else if (number_grade >= 70.0) strncpy(letter, "C", 1);
  else if (number_grade >= 60.0) strncpy(letter, "D", 1);
  else                           strncpy(letter, "F", 1);
  letter[1] = '\0';

  return letter;
}