OSF DCE Application Development Guide--Core Components

OSF DCE Application Development Guide—Core Components
#undef fork
8.1.2 Blocking System Calls
DCE Threads provides jacket routines that make certain system calls thread-
synchronous. If calling one of these jacketed system calls would normally block the
process, the jacket routine ensures that only the calling thread is blocked and that the
process remains available to execute other threads. Examples of jacketed system calls
include read(), write( ), open( ), socket(), send( ), and recv().
If a thread makes a call to any of the other nonjacketed blocking system calls (or if it
calls one of the jacketed system calls without going through the jacket), then when the
system call blocks the thread, it blocks the whole process, preventing any other threads
in the process from executing. Examples of nonjacketed system calls include wait(),
sigpause( ), msgsnd(), msgrcv(), and semop().
Some care must be used when calling nonjacketed blocking system calls from a
multithreaded program. Other threads in the program may not be able to tolerate not
running for an extended period of time while the process blocks for the system call. If
your program must make use of such system calls, the calling thread should specify a
nonblocking or polling option to the system call. If the call is not successful, then the
calling thread should retry; however, to prevent the retry code from becoming a hot loop,
a yield or delay function call should be inserted into the path. This gives other threads in
the program a chance to run between poll attempts.
8.1.3 Calling fork() in a Multithreaded Environment
The fork( ) system call creates an exact duplicate of the address space from which it is
called, resulting in two address spaces executing the same code. Problems can occur if
the forking address space has multiple threads executing at the time of the fork(). When
multithreading is a result of library invocation, threads are not necessarily aware of each
other’s presence, purpose, actions, and so on. Suppose that one of the other threads (any
thread other than the one doing the fork()) has the job of deducting money from your
checking account. Clearly, you do not want this to happen twice as a result of some
other thread’s decision to call fork( ).
Because of these types of problems, which in general are problems of threads modifying
persistent state, POSIX defined the behavior of fork( ) in the presence of threads to
propagate only the forking thread. This solves the problem of improper changes being
made to persistent state. However, it causes other problems, as discussed in the next
paragraph.
In the POSIX model, only the forking thread is propagated. All the other threads are
eliminated without any form of notice; no cancels are sent and no handlers are run.
However, all the other portions of the address space are cloned, including all the mutex
state. If the other thread has a mutex locked, the mutex will be locked in the child
8 4 Tandem Computers Incorporated 124245