Chapter 6Safe and Unsafe Interfaces
This chapter defines MT-safety levels for functions and
libraries.
Thread Safety
Thread safety is the avoidance of data races--situations in which
data are set to either correct or incorrect values, depending upon the order
in which multiple threads access and modify the data.
When no sharing is intended, give each thread a private copy of the
data. When sharing is important, provide explicit synchronization to make
certain that the program behaves in a deterministic manner.
A procedure is thread safe when it is logically correct when executed
simultaneously by several threads. At a practical level, it is convenient
to recognize three levels of safety.
An unsafe procedure can be made thread safe and serializable by surrounding
it with statements to lock and unlock a mutex. Example 6-1shows
three simplified implementations of fputs(), initially
thread unsafe.
Next is a serializable version of this routine with a single mutex protecting
the procedure from concurrent execution problems. Actually, this is stronger
synchronization than is usually necessary. When two threads are sending output
to different files using fputs(), one need not wait for
the other--the threads need synchronization only when they are sharing
an output file.
The last version is MT-safe. It uses one lock for each file, allowing
two threads to print to different files at the same time. So, a routine is
MT-safe when it is thread safe and its execution does not negatively affect
performance.
Example 6-1 Degrees of Thread Safety
/* not thread-safe */
fputs(const char *s, FILE *stream) {
char *p;
for (p=s; *p; p++)
putc((int)*p, stream);
}
/* serializable */
fputs(const char *s, FILE *stream) {
static mutex_t mut;
char *p;
mutex_lock(&m);
for (p=s; *p; p++)
putc((int)*p, stream);
mutex_unlock(&m);
}
/* MT-Safe */
mutex_t m[NFILE];
fputs(const char *s, FILE *stream) {
static mutex_t mut;
char *p;
mutex_lock(&m[fileno(stream)]);
for (p=s; *p; p++)
putc((int)*p, stream);
mutex_unlock(&m[fileno(stream)]0;
}
|
MT Interface Safety Levels
The threads man pages, man(3THR), use the safety
level categories listed in Table 6-1 to describe how well
an interface supports threads (these categories are explained more fully in
the Intro(3) reference manual page).
Table 6-1 Interface Safety Levels
Category | Description |
Safe | This code can be called
from a multithreaded application |
Safe with exceptions | See the
NOTES sections of these pages for a description of the exceptions. |
Unsafe | This interface is not
safe to use with multithreaded applications unless the application arranges
for only one thread at a time to execute within the library. |
MT-Safe | This interface is fully
prepared for multithreaded access in that it is both safe
and it supports some concurrency. |
MT-Safe with exceptions | See the
NOTES sections of these pages in the man(3THR): Library
Routines for a list of the exceptions. |
Async-Signal-Safe | This routine
can safely be called from a signal handler. A thread that is executing an
Async-Signal-Safe routine does not deadlock with itself when it is interrupted
by a signal. |
Fork1-Safe | This interface releases locks it has held whenever
the Solaris fork1(2) or the POSIX fork(2) is called. |
See the section 3 manual pages for the safety levels of library routines.
Some functions have purposely not been made safe for the following reasons.
Caution - There is no way to be certain that a function with a name
not ending in "_r" is MT-Safe other than by
checking its reference manual page. Use of a function identified as not MT-Safe
must be protected by a synchronizing device or by restriction to the initial
thread.
Reentrant Functions for Unsafe Interfaces
For most functions with Unsafe interfaces, an
MT-Safe version of the routine exists. The name of the new MT-Safe routine
is always the name of the old Unsafe routine with "_r"
appended. The Table 6-2 "_r" routines are supplied in the Solaris environment.
Table 6-2 Reentrant Functions
asctime_r(3c) | gethostbyname_r(3n) | getservbyname_r(3n) |
ctermid_r(3s) | gethostent_r(3n) | getservbyport_r(3n) |
ctime_r(3c) | getlogin_r(3c) | getservent_r(3n) |
fgetgrent_r(3c) | getnetbyaddr_r(3n) | getspent_r(3c) |
fgetpwent_r(3c) | getnetbyname_r(3n) | getspnam_r(3c) |
fgetspent_r(3c) | getnetent_r(3n) | gmtime_r(3c) |
| gamma_r(3m) | getnetgrent_r(3n) | lgamma_r(3m) |
getauclassent_r(3) | getprotobyname_r(3n) | localtime_r(3c) |
getauclassnam_r(3) | getprotobynumber_r(3n) | nis_sperror_r(3n) |
getauevent_r(3) | getprotoent_r(3n) | rand_r(3c) |
getauevnam_r(3) | getpwent_r(3c) | readdir_r(3c) |
getauevnum_r(3) | getpwnam_r(3c) | strtok_r(3c) |
getgrent_r(3c) | getpwuid_r(3c) | tmpnam_r(3s) |
getgrgid_r(3c) | getrpcbyname_r(3n) | ttyname_r(3c) |
getgrnam_r(3c) | getrpcbynumber_r(3n) | |
gethostbyaddr_r(3n) | getrpcent_r(3n) | |
|