| Previous | Contents | Index |
Unlocks a read-write lock that was acquired for write access.
C Binding #include <tis.h>tis_write_unlock(
lock );
Argument Data Type Access lock opaque tis_rwlock_t write
int
tis_write_unlock (
tis_rwlock_t *lock);
lock
Address of the read-write lock to be unlocked.
This routine unlocks a read-write lock that was acquired for write access.Return Values If an error condition occurs, this routine returns an integer value indicating the type error. Possible return values are as follows:Upon completion of this routine, any thread waiting to acquire the lock for read access will have those acquisitions granted. If no threads are waiting to acquire the lock for read access, then a thread waiting to acquire it for write access will have that acquisition granted.
| Return | Description |
|---|---|
| 0 | Successful completion. |
| [EINVAL] | The value specified by lock is not a valid read-write lock. |
Notifies the scheduler that the current thread is willing to release its processor to other threads of the same or higher priority.
C Binding inttis_yield( );
None
When threads are not present, this routine has no effect.Return Values If an error condition occurs, this routine returns an integer value indicating the type of error. Possible return values are as follows:This routine notifies the thread scheduler that the current thread is willing to release its processor to other threads of equivalent or greater scheduling precedence. (A thread generally will release its processor to a thread of a greater scheduling precedence without calling this routine.) If no other threads of equivalent or greater scheduling precedence are ready to execute, the thread continues.
This routine can allow knowledge of the details of an application to be used to improve its performance. If a thread does not call tis_yield(), other threads may be given the opportunity to run at arbitrary points (possibly even when the interrupted thread holds a required resource). By making strategic calls to tis_yield(), other threads can be given the opportunity to run when the resources are free. This improves performance by reducing contention for the resource.
As a general guideline, consider calling this routine after a thread has released a resource (such as a mutex) which is heavily contended for by other threads. This can be especially important if the program is running on a uniprocessor machine, or if the thread acquires and releases the resource inside a tight loop.
Use this routine carefully and sparingly, because misuse can cause unnecessary context switching which will increase overhead and actually degrade performance. For example, it is counter-productive for a thread to yield while it holds a resource which the threads to which it is yielding will need. Likewise, it is pointless to yield unless there is likely to be another thread which is ready to run.
| Return | Description |
|---|---|
| 0 | Successful completion. |
| [ENOSYS] | The routine tis_yield() is not supported by this implementation. |
This appendix discusses DECthreads issues specific to Tru64 UNIX
systems.
A.1 Overview
The Tru64 UNIX operating system supports multiple concurrent
"execution contexts" within a process. DECthreads uses these
kernel execution contexts to implement user threads. One important
benefit of this is that user threads can run simultaneously on separate
processors in a multiprocessor system. Review Section 3.1 for tips
for ensuring that your application will work correctly with kernel
threads and multiprocessing.
A.2 Building DECthreads Applications
The following sections discuss points to consider when building using
DECthreads.
A.2.1 Including DECthreads Header Files
Include one of the DECthreads header files shown in Table A-1 in your program to use the appropriate DECthreads library.
| Header File | Interface |
|---|---|
| pthread.h | POSIX routines |
| tis.h | Thread-independent services routines |
Do not include more than one of these header files in your module.
A.2.2 Building Multithreaded Applications from DECthreads Libraries
Multithreaded applications are built using shared libraries. For a description of shared libraries, see the Tru64 UNIX Programmer's Guide.
Table A-2 contains the libraries supported for multithreaded programming.
| libpthreads.so | Shared version of DECthreads "legacy" package, implementing the Compaq-proprietary CMA (or cma) and POSIX 1003.4a/Draft 4 ( d4 or DCEthreads) interfaces. |
| libpthread.so | Shared version of the POSIX threads package. Requires libexc.so and libc.so |
| libexc.so | Shared version of Tru64 UNIX exception support package. |
| libc.so | Shared version of the C language run-time library ( libc.so). |
Build a multithreaded application using shared versions of libexc, libpthread, and libc using this command:
% cc -o myprog myprog.c -pthread |
If you use a compiler front-end or other (not C) language environment
that does not support the -pthread compilation switch, you
must provide the -D_REENTRANT compilation switch (or
equivalent) at compilation, and link as shown in Section A.2.3.
A.2.3 Linking Multithreaded Shared Libraries
The ld command does not support the -pthread or -threads switch. Normally, programs can be compiled and linked from the cc command. If you must link using the ld command, you must list the shared libraries in the proper order. The libc library should be the last library referenced, libexc should immediately precede libc, and the thread libraries should precede libexc.
For libraries that use only the pthread interface, use the following:
ld <...> -lpthread -lexc -lc |
If using the cma or d4 interfaces, use the following:
ld <...> -lpthreads -lpthread -lexc -lc |
Also, cc -pthread (or cc -threads) causes the compiler to replace any libraries that have special thread-safe alternatives. These libraries have the same name ending in -r. For example, cc -pthread -o foo -lbar, if there is a libbar.so and libbar_r.so, would use the latter. When linking with the ld command, you must perform that search and replacement yourself.
If you build software (whether applications or libraries) that links against the static version of a DECthreads library, you must not require developers who use your software to link against any library that dynamically loads any DECthreads shared library, such as libpthread.so. |
Applications that use the Compaq-proprietary thread-independent
services (or tis) interface should include the
tis.h header file and link against the shared C run-time
library (libc.so).
A.3 Two-Level Scheduling on Tru64 UNIX Systems
Under Tru64 UNIX Version 4.0 and later, DECthreads implements a two-level scheduling model. The thread library schedules "user threads" onto kernel execution contexts (often known as "kernel threads" or "virtual processors"), just as Tru64 UNIX schedules processes onto the processors of a multiprocessing machine.
A user thread is executed on a kernel thread until it blocks or exhausts its timeslice quantum. Then, DECthreads schedules a new user thread to run. While DECthreads is scheduling user threads onto kernel threads, the Tru64 UNIX kernel is independently scheduling those kernel threads to run on physical processors. The term "two-level scheduling" refers to this relationship.
This division allows most thread scheduling to take place completely in user mode, without the intervention of the kernel. Since a thread context switch does not involve any privileged information, it can be done much more efficiently in user mode.
The key to making the two-level scheduling model work is efficient
two-way communication between DECthreads and the Tru64 UNIX kernel.
When a thread blocks in the kernel, the DECthreads scheduler is
notified so that it can schedule another thread to take advantage of
the idle kernel thread. This mechanism, sometimes referred to as an
upcall, is inspired by original research on scheduler
activations at the University of Washington. (See Scheduler
Activations: Effective Kernel Support for the User-Level Management of
Parallelism by Anderson, Bershad, Lazowska, and Levy; ACM
Operating Systems Review Volume 25, Number 5, Proceedings of the
Thirteenth ACM Symposium on Operating Systems Principles, October
13-16, 1991).
A.3.1 DECthreads Use of Kernel Threads
Tru64 UNIX kernel threads are created as they are needed by the application. The number of kernel threads that DECthreads creates is limited by normal Tru64 UNIX configuration limits regarding user and system thread creation. Normally, however, DECthreads creates one kernel thread for each actual processor on the system, plus a "manager thread" for bookkeeping operations.
DECthreads does not delete these kernel threads or let them terminate. Kernel threads not currently needed are retained in an idle state until they are needed again. (These idled kernel threads are deleted by the kernel if they remain idle for a long time.) When the process terminates, all kernel threads in the process are reclaimed by the kernel.
The DECthreads scheduler can schedule any user thread onto any kernel
thread. Therefore, a user thread can run on different kernel threads at
different times. Normally, this should pose no problem. However, for
example, the kernel thread ID as reported by the dbx or Ladebug
debuggers (in "native" $threadlevel) can change at any time.
A.3.2 Support for Real-Time Scheduling
DECthreads supports Tru64 UNIX real-time scheduling. This allows you to set the scheduling policy and priority of threads. By default, threads are created using process contention scope. This means that the full range of POSIX.1 scheduling policy and priority is available. However, threads running in process contention scope do not preempt lower-priority threads in another process. For example, a thread in process contention scope with SCHED_FIFO policy and maximum priority 63 will not preempt a thread in another process running with SCHED_FIFO and lower priority.
In contrast, system contention scope means that each thread created by the program has a direct and unique binding to one kernel execution context. A system contention scope thread competes against all threads in the system and will preempt any thread with lower priority. For this reason, the priority range of threads in system contention scope is restricted unless running with root privilege.
Specifically, a thread with SCHED_FIFO policy cannot run at a priority higher than 18 without privilege, since doing so could lock out all other users on the system until the thread blocked. Threads at any other scheduling policy (including SCHED_RR) can run at priority 19 because they are subject to periodic timeslicing by the system. For more information, see the Tru64 UNIX Realtime Programming Guide.
If your program lacks necessary privileges, attempting to call the following routines for a thread in system contention scope returns the error value [EPERM]:
| pthread_attr_setschedpolicy() | ( Error returned by pthread_create() at thread creation) |
| pthread_attr_setschedparam() | ( Error returned by pthread_create() at thread creation) |
| pthread_setschedparam() |
Prior to Tru64 UNIX Version 4.0, all threads used only system
contention scope. In Tru64 UNIX Version 4.0, all threads created using
the pthread interface, by default, have process
contention scope.
A.4 Thread Cancelability of System Services
Tru64 UNIX supports the required system cancelation points specified by the POSIX.1 standard and by the Single UNIX Specifivation, Version 2 (UNIX98).
For legacy multithreaded applications, note that threads created using the cma or d4 interfaces will not be cancelable at any system call. (Here "system call" means any function without the pthread_ prefix.) If system call cancelation is required, you must write code using the DECthreads pthread interface.
It is not legal, or supported, to call any Tru64 UNIX system function with asynchronous cancelability type. You cannot "work around" the lack of system call cancelation using asynchronous cancelability. |
For more information, see Section 2.3.7.
A.4.1 Cancelation Points
The following functions are cancelation points (as defined by the
Single UNIX Specification, Version 2 (SUSV2)):
accept() |
send() |
A.4.2 Conditional or Future Cancelation Points
These functions may not cause delivery of a pending cancel, and
cancelation may not interrupt a blocking state. Some will recognize
cancelation only under some conditions (for example, if
printf() flushes a standard I/O buffer to the file stream).
Others may currently not be coded to recognize cancelation, but may be
changed in the future. All code should be prepared to handle
cancelation at these calls, but must not depend on cancelation at these
calls.
closedir() |
getchar() |
perror() |
Note that appropriate non-standard functions that do not appear in the
preceding list might become cancelation points in the future. Tru64
UNIX will also implement new cancelation points, as specified by future
revisions of the relevant formal or consortium standard bodies.
A.5 Using Signals
This section discusses signal handling based on the POSIX.1 standard.
Tru64 UNIX Version 4.0 introduced the full POSIX.1 signal model. In previous versions, "synchronous" signals (those resulting from execution errors, such as SIGSEGV and SIGILL) could have different signal actions for each thread. Prior to Tru64 UNIX Version 3.2, all threads shared a common, processwide signal mask, which meant one thread could not receive a signal while another had the signal blocked.
Under Tru64 UNIX Version 4.0 and later, all signal actions are processwide. That is, when any thread uses sigaction or equivalent to set a signal handler, or to modify the signal action (for example, to ignore a signal), that action will affect all threads. Each thread has a private signal mask so that it can block signals without affecting the behavior of other threads.
Prior to Tru64 UNIX Version 4.0, asynchronous signals were processed only in the main thread. In Tru64 UNIX Version 4.0, any thread that doesn't have the signal masked can process the signal.
To support binary compatibility, for a thread created by a DECthreads cma or d4 interface routine, the thread starts with all asynchronous signals blocked. |
The POSIX 1003.1 sigwait() service allows any thread to block until one of a specified set of signals is delivered. A thread can wait for any of the asynchronous signals except for SIGKILL and SIGSTOP.
For example, you can create a thread that blocks on a sigwait() routine for SIGINT, rather than handling a Ctrl/C in the normal way. This thread could then cancel other threads to cause the program to shut down the current activities.
Following are two reasons for avoiding signals:
In a multithreaded program, signal handlers cannot be used in a modular way because there is only one signal handler routine for all of the threads in an application. If two threads install different signal handlers for the signal, all threads will dispatch to the last handler when they receive the signal.
Most applications should avoid using asynchronous programming techniques in conjunction with threads. For example, techniques that rely on timer and I/O signals are usually more complicated and errorprone than simply waiting synchronously within a thread. Furthermore, most of the thread services are not supported for use in signal handlers, and most run-time library functions cannot be used reliably inside a signal handler.
Some I/O intensive code may benefit from asynchronous I/O, but these programs will generally be more difficult to write and maintain than "pure" threaded code.
A thread should not wait for a synchronous signal. This is because synchronous signals are the result of an error during the execution of a thread, and if the thread is waiting for a signal, then it is not executing. Therefore, a synchronous signal cannot occur for a particular thread while it is waiting, and the thread will wait forever.
The POSIX.1 standard requires that the thread block the signals for which it will wait before calling sigwait(). For reliable operation, the signals should be blocked in all threads. Otherwise, the signal might be delivered to another thread before the sigwait thread calls sigwait(), or after it has returned with another signal.
| Previous | Next | Contents | Index |