JMU
POSIX Threads (Pthreads)
An Introduction


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Review
Review (cont.)
Motivation
Organization
Organization (cont.)
Organization (cont.)
images/thread-memory-organization.gif
Attributes
Thread Creation: pthread_create() pthread_create
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start)(void *), void *arg)
Purpose:
Create a new thread
Details:
thread Identifier of the new thread
start The function to be executed in the new thread
attr Attributes of the new thread (or NULL to use the default attributes)
arg The argument to pass to start()
Return 0 on success; a positive error number on error
#include <pthread.h> pthread.h

Note:The new thread may start executing before pthread_create() returns.

Thread Termination
"Forced" Thread Termination: pthread_exit() pthread_exit
void pthread_exit(void *retval)
Purpose:
Terminate the calling thread
Details:
retval The return value for the thread
#include <pthread.h> pthread.h

Note:The value pointed to by retval must not be on the thread's stack.

Detaching: pthread_detach() pthread_detach
int pthread_detach(pthread_t thread)
Purpose:
Indicate that you don't care about the thread's return status and that the kernel should remove the thread when it terminates
Details:
thread The thread to detach
Return 0 on success; a positive error number of error
#include <pthread.h> pthread.h

Note: Detaching a thread does not terminate it nor does it change the way in which a thread can/will be terminated.

Joining: pthread_join() pthread_join
int pthread_join(pthread_t thread, void **retval)
Purpose:
Wait for a thread to terminate
Details:
thread The thread to wait for
retval A non-NULL pointer that receives a copy of the terminated thread's return value
Return 0 on success; a positive error number on error
#include <pthread.h> pthread.h

Notes: (1) If thread has already terminated then pthread_join() will return immediately. (2) One must not call pthread_join() on a thread that has already been joined (since the ID may have been re-used).

Cleaning Up
Building Multi-Threaded Programs
A Simple Example
unixexamples/pthreads/messager.c
        #include <pthread.h>
#include <stdio.h>    // For printf()
#include <stdlib.h>   // For exit()
#include <string.h>   // For strlen()
#include <unistd.h>   // For sleep() if needed

static void
*display(void *arg)
{
  char *s;
  void *result;

  // Demonstrate how to use the argument
  s = (char *)arg;               // Cast the void* as a char*
  printf("%s\n", s);             // Print

  // Demonstrate how to return
  result = (void *)(strlen(s)); // Cast the int as a void*
  return result;                // Return
}

int
main(void)
{
  char       *s;
  long       len;
  pthread_t  helper;
  void       *result;

  s = "Department of Computer Science";

  pthread_create(&helper, NULL, display, "JMU");
  printf("%s\n", s);

  pthread_join(helper, &result);
  len = (long)result;

  printf("Characters printed by the main   thread: %ld\n", (long)(strlen(s))); 
  printf("Characters printed by the helper thread: %ld\n", len);
  exit(0);
}
        
Wrapper Functions
Wrapper Functions (cont.)
unixexamples/pthreads/wrapper.c
        #include <pthread.h>
#include <stdio.h>    // For printf()
#include <stdlib.h>   // For exit()
#include <string.h>   // For strlen()
#include <unistd.h>   // For sleep() if needed

// A function that is used for many purposes
long
display(char *s)
{
  printf("%s\n", s);
  return strlen(s);
}

// A wrapper for display() so that it can be executed in a thread
static void
*display_wrapper(void *arg)
{
  char  *s;
  long  result;

  s = (char *)arg;
  result = display(s);
  return (void *)result;
}

// The entry point of the program
int
main(void)
{
  char       *s;
  long       helper_len, main_len;
  pthread_t  helper;
  void       *result;

  s = "Department of Computer Science";

  pthread_create(&helper, NULL, display_wrapper, "JMU");

  main_len = display(s);

  pthread_join(helper, &result);
  helper_len = (long)result;

  printf("Characters printed by the main   thread: %ld\n", main_len); 
  printf("Characters printed by the helper thread: %ld\n", helper_len);
  exit(0);
}
        
Thread IDs: pthread_self() pthread_self
pthread_t pthread_self(void)
Purpose:
Obtain the calling thread's thread ID
Details:
Return The thread ID of the calling thread
#include <pthread.h> pthread.h
Comparing Thread IDs: pthread_equal() pthread_equal
int pthread_equal(pthread_t a, pthread_t b)
Purpose:
Check to see if two thread IDs refer to the same thread
Details:
a The ID of one thread
b The ID of abother thread
Return 0 if unequal; nonzero otherwise
#include <pthread.h> pthread.h
Thread IDs (cont.)
unixexamples/pthreads/id.c
        #include <pthread.h>
#include <stdio.h>    // For printf()
#include <stdlib.h>   // For exit()
#include <string.h>   // For strlen()
#include <unistd.h>   // For sleep() if needed

// A function that is used for many purposes
long
display(char *s)
{
  // Try adding calls to sleep() in various places
  printf("%s\n", s);
  printf("(Thread: %ld)\n", pthread_self());
  printf("\n\n");
  return strlen(s);
}

// A wrapper for display() so that it can be executed in a thread
static void
*display_wrapper(void *arg)
{
  char  *s;
  long  result;

  // Try adding calls to sleep() in various places
  s = (char *)arg;
  result = display(s);
  return (void *)result;
}

// The entry point of the program
int
main(void)
{
  char       *c, *d, *u;
  pthread_t  thread_c, thread_u;
  void       *result;

  d = "Department of Computer Science";
  c = "College of Integrated Science and Engineering";
  u = "James Madison University";
  
  display(d);
  pthread_create(&thread_c, NULL, display_wrapper, c);
  pthread_create(&thread_u, NULL, display_wrapper, u);


  pthread_join(thread_c, &result);
  pthread_join(thread_u, &result);
  exit(0);
}
        
One-Time Initialization: pthread_once() pthread_once
int pthread_once(pthread_once_t *control, void (*init)(void))
Purpose:
Ensure that a function is executed once (regardless of how many threads are created)
Details:
control Pointer to a statically initialized variable with the value PTHREAD_ONCE_INIT
init Pointer to the function that is to be executed once
Return 0 on success; positive error number on error
#include <pthread.h> pthread.h

The first call to pthread_once() modifies the value of the variable pointed to by control and then calls init(). Subsequent calls to pthread_once() then don't call init().