- Forward


Thread Safety
in Pthreads


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu

Print

Review
Back SMYC Forward
  • Threads:
    • A mechanism that permits a single process to perform multiple tasks concurrently
  • Mutexes:
    • A mechanism that can be used to ensure the mutual exclusion of shared variables
  • Reentrant Functions:
    • A function is reentrant if its execution can be interrupted and it can be called again (before the previous invocation is completed)
Definitions
Back SMYC Forward
  • Thread-Safe:
    • A function is thread-safe if it will produce correct results when called by two or more threads (i.e., its effect is the same regardless of the order/interleaving of execution)
  • Thread-Unsafe:
    • A thread that is not thread-safe is said to be thread-unsafe
Thread-Safety and Reentrancy
Back SMYC Forward
  • All Reentrant Functions are Thread Safe:
    • Follows from both definitions
  • Some Thread-Safe Functions aren't Reentrant:
    • Shouldn't be surprising because we know we can coordinate the use of shared variables
Common Reasons Functions Aren't Thread-Safe
Back SMYC Forward
  • Use of a Variable with File Scope:
    • Consider the example of a function that updates a "global" linked list (e.g., malloc() in C)
  • Use of a Variable with Static Duration:
    • Consider the example of a function that maintains state across multiple calls in a local static variable (e.g., a random number generator)
  • Return of a static Local Variable:
    • For example, asctime() returns a pointer to a statically allocated date-time string
  • Calling a Thread-Unsafe Function:
    • For obvious reasons
Adding Thread-Safety - Protect the Function
Back SMYC Forward
  • The Idea:
    • Use a mutex to control access to the entire function (i.e., the function has an associated mutex, the first statement is a call to pthread_mutex_lock(), and the last statement is a call to pthread_mutex_unlock()
  • Effectiveness:
    • Essentially serializes all calls to the function (resulting in a significant loss of concurrency)
Adding Thread-Safety - Protect Shared Variables
Back SMYC Forward
  • The Idea:
    • Use a mutex to control access to the critical sections of the function (i.e., the sections of the function that access the shared variable)
  • Effectiveness:
    • Better than protecting the function but worse than making the function reentrant
Adding Thread-Safety - Pass State Information
Back SMYC Forward
  • The Idea:
    • Instead of maintaining state information in a a variable with file scope or a static local variable, pass the state information to the function
  • Effectiveness:
    • Requires a change in the interface (i.e., all callers have to change) and requires the callers to do some work
Adding Thread-Safety - Pass State Information (cont.)
Back SMYC Forward
int running_total(int x) { static int total = 0; total += x; return total; }
int running_total(int x, int total) { total += x; return total; }
Adding Thread-Safety - Caller Allocates/Caller Frees
Back SMYC Forward
  • The Idea:
    • Instead of having the callee allocate memory have the caller allocate memory and pass an "outbound" argument
  • Effectiveness:
    • Requires a change in the interface (i.e., all callers have to change) and requires the callers to manage memory
Adding Thread-Safety - Caller Allocates/Caller Frees (cont.)
Back SMYC Forward
void running_total(int x, int *total) { total += x; return total; }
Adding Thread-Safety - Lock-and-Copy Wrappers
Back SMYC Forward
  • The Idea:
    • Sometimes it is impossible to change the original function but it is possible to create a wrapper function that is thread safe
  • Effectiveness:
    • Essentially a mix of the above
Adding Thread-Safety - Lock-and-Copy Wrappers (cont.)
Back SMYC Forward
// The function that can't be changed int *running_total(int x) { static int total = 0; total += x; return &total; }
// The wrapped, thread-safe version static pthread_mutex_t running_total_mutex = PTHREAD_MUTEX_INITIALIZER; int *running_total_ts(int x, int *private) { int *shared; pthread_mutex_lock(&running_total_mutex); shared = running_total(x); memcopy(private, shared, sizeof(int)); pthread_mutex_unlock(&running_total_mutex); return private; }
Adding Thread-Safety - Thread-Specific Data
Back SMYC Forward
  • The Idea:
    • Sometimes it is impossible to change the interface of the original function but it is possible to change the function so that it uses thread-specific data
  • Effectiveness:
    • Slightly less efficient than a reentrant version
There's Always More to Learn
Back -