JavaScript is required to for searching.
Skip Navigation Links
Exit Print View
Multithreaded Programming Guide     Oracle Solaris 11.1 Information Library
search filter icon
search icon

Document Information

Preface

1.  Covering Multithreading Basics

2.  Basic Threads Programming

3.  Thread Attributes

4.  Programming with Synchronization Objects

5.  Programming With the Oracle Solaris Software

6.  Programming With Oracle Solaris Threads

7.  Safe and Unsafe Interfaces

8.  Compiling and Debugging

9.  Programming Guidelines

Rethinking Global Variables

Providing for Static Local Variables

Synchronizing Threads

Single-Threaded Strategy

Reentrant Function

Code Locking

Data Locking

Invariants and Locks

Avoiding Deadlock

Deadlocks Related to Scheduling

Locking Guidelines

Finding Deadlocks

Some Basic Guidelines for Threaded Code

Creating and Using Threads

Working With Multiprocessors

Underlying Architecture

Shared-Memory Multiprocessors

Peterson's Algorithm

Parallelizing a Loop on a Shared-Memory Parallel Computer

Examples of Threads Programs

Further Reading

A.  Extended Example: A Thread Pool Implementation

Index

Rethinking Global Variables

Historically, most code has been designed for single-threaded programs. This code design is especially true for most of the library routines that are called from C programs. The following implicit assumptions were made for single-threaded code:

The following examples discuss some of the problems that arise in multithreaded programs because of these assumptions, and how you can deal with the problems.

Traditional, single-threaded C and UNIX have a convention for handling errors detected in system calls. System calls can return anything as a functional value. For example, write() returns the number of bytes that were transferred. However, the value -1 is reserved to indicate that something went wrong. So, when a system call returns -1, you know that the call failed.

Example 9-1 Global Variables and errno

extern int errno;
...
if (write(file_desc, buffer, size) == -1) {
    /* the system call failed */
    fprintf(stderr, “something went wrong, “
        “error code = %d\n”, errno);
    exit(1);
}
...

Rather than returning the actual error code, which could be confused with normal return values, the error code is placed into the global variable errno. When the system call fails, you can look in errno to find out what went wrong.

Now, consider what happens in a multithreaded environment when two threads fail at about the same time but with different errors. Both threads expect to find their error codes in errno, but one copy of errno cannot hold both values. This global variable approach does not work for multithreaded programs.

Threads solve this problem through a conceptually new storage class: thread-specific data. This storage is similar to global storage. Thread-specific data can be accessed from any procedure in which a thread might be running. However, thread-specific data is private to the thread. When two threads refer to the thread-specific data location of the same name, the threads are referring to two different areas of storage.

So, when using threads, each reference to errno is thread specific because each thread has a private copy of errno. A reference to errno as thread-specific is achieved in this implementation by making errno a macro that expands to a function call.