Prior to DIGITAL UNIX Version 4.0, all threads used only system contention scope. In DIGITAL UNIX Version 4.0, all threads created using the pthread interface, by default, have process contention scope.
DIGITAL UNIX supports the required system cancelation points specified by the POSIX.1c standard. In addition, critical non-POSIX functions supported by DIGITAL UNIX (such as select()) have also been defined as cancelation points.
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. None of the system calls should be called with asynchronous cancelation enabled.
For more information, see Section 2.3.7.
accept() |
recvfrom() |
closedir() |
getpwnam() |
Note that appropriate non-POSIX functions that do not appear in the preceding list might become cancelation points in the future. DIGITAL UNIX will also implement new cancelation points, as specified by future revisions of the relevant formal or consortium standard bodies.
DIGITAL UNIX Version 4.0 introduces the full POSIX.1c 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 DIGITAL 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 DIGITAL 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 DIGITAL UNIX Version 4.0, asynchronous signals were processed only in the main thread. In DIGITAL 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.1c 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 threads 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.1c standard requires that the thread block the signals for which it will wait before calling sigwait().
For the signals traditionally representing synchronous errors in the program, DECthreads catches the signal and converts it into an equivalent exception. This exception is then propagated up the call stack in the current thread and can be caught and handled using the normal exception catching mechanisms.
Table A-3 lists DIGITAL UNIX signals that are reported as DECthreads exceptions by default. If any thread declares an action for one of these signals (using sigaction(2) or equivalent), no thread in the process can receive the exception.
Signal | Exception |
---|---|
SIGILL | pthread_exc_illinstr_e |
SIGIOT | pthread_exc_SIGIOT_e |
SIGEMT | pthread_exc_SIGEMT_e |
SIGFPE | pthread_exc_aritherr_e |
SIGBUS | pthread_exc_illaddr_e |
SIGSEGV | pthread_exc_illaddr_e |
SIGSYS | pthread_exc_SIGSYS_e |
SIGPIPE | pthread_exc_SIGPIPE_e |
When creating a thread based on a thread attributes object, DECthreads potentially rounds up the value specified in the object's guardsize attribute. DECthreads does so based on the value of the configurable system variable PAGESIZE (see <sys/mman.h>. The default value of the guardsize attribute in a thread attributes object is a number of bytes equal to setting of PAGESIZE.
Dynamic activation of the DECthreads run-time environment, or of code that depends on DECthreads, is currently not supported.
This appendix discusses DECthreads issues and restrictions specific to the OpenVMS operating system.
Under OpenVMS, DECthreads offers these application programming interfaces:
The DECthreads C language header files shown in Table B-1 provide interface definitions for the DECthreads pthread and tis interfaces.
Header File | Interface |
---|---|
pthread.h | POSIX.1c style routines |
tis.h | DIGITAL-proprietary thread-independent services routines |
Include only one of these header files in your module.
Special compiler definitions are not required when compiling threaded applications that use the pthread interface or the tis interface.
DECthreads is supplied only as shareable images. It is not supplied as object libraries.
When you link an image that calls DECthreads routines, you must link against the appropriate images listed in Table B-2.
Image | Routine Library |
---|---|
PTHREAD$RTL.EXE | POSIX.1c style interface |
CMA$TIS_SHR.EXE | Thread-independent services |
The image files PTHREAD$RTL.EXE, CMA$TIS_SHR.EXE, CMA$RTL.EXE, and CMA$LIB_SHR.EXE are included in the IMAGELIB library, making it unnecessary to specify those images (unless you are using the /NOSYSLIB switch with the linker) in a Linker options file.
When you link an image that utilizes the CMA$OPEN_LIB_SHR.EXE and CMA$OPEN_RTL.EXE images, they must be specified in a Linker options file.
Note
While this version of DECthreads for OpenVMS supports upward compatibility of source and binaries for the DECthreads d4 interface, DECthreads does not support upward compability for object files.For instance, under OpenVMS V7.0 and higher, to link object files that were compiled under OpenVMS V6.2, follow these steps:
- Copy CMA$OPEN_RTL.EXE from SYS$SHARE for OpenVMS V6.2 into the directory with your object files compiled under the current OpenVMS version. During linking, it provides the locations of the transfer vector entries (OpenVMS VAX) or symbol vector entries (OpenVMS Alpha) in CMA$OPEN_RTL.EXE for the the older OpenVMS version.
- Instead of specifying SYS$SHARE:CMA$OPEN_RTL/SHARE in your link options files, specify CMA$OPEN_RTL/SHARE. Be careful about the placement of this option in the options file---it should perhaps be placed at the beginning, or close to it, if you are including other images that link against PTHREAD$RTL.
- Link your program.
- Delete CMA$OPEN_RTL.EXE from your object directory for the current OpenVMS version.
An asynchronous system trap, or AST, is an OpenVMS mechanism for reporting an asynchronous event to a process. The following are restrictions concerning the use of ASTs with DECthreads:
Dynamic activation of DECthreads (or images that depend on DECthreads) is currently not supported.
This section discusses a restriction on declaring an OpenVMS condition handler while using DECthreads exceptions and DECthreads behavior when a condition is signaled.
The following are three ways to declare an OpenVMS condition handler:
Do not declare an OpenVMS condition handler within a DECthreads TRY/ENDTRY exception block. Doing so deletes without notification any handler that exists for the current procedure. If your code declares a condition handler within the TRY/ENDTRY block, DECthreads exceptions will not be handled correctly until the next TRY statement is executed. The TRY statement restores the DECthreads condition handler.
On OpenVMS VAX, you can declare a condition handler outside of a TRY/ENDTRY block with no restrictions. If a condition handler has already been declared when you execute a TRY statement, DECthreads saves the previous handler address. When DECthreads receives a condition it does not handle (including SS$_UNWIND, SS$_DEBUG, or a condition code that does not have a SEVERE severity), DECthreads invokes the saved condition handler. The condition handler will be reestablished when the TRY block exits.
On OpenVMS Alpha, system calls are now cancelation points for threads created using the POSIX 1003.1c style interface. System calls are not cancelation points for threads in legacy multithreaded applications that were created using the DIGITAL-proprietary CMA (or cma) or POSIX 1003.4a/Draft 4 (or d4) interfaces. None of the system calls should be called with asynchronous cancelation enabled. For more information, see Section 2.3.7.
On OpenVMS Alpha, DECthreads supports the use of 64-bit addressing in the pthread interface only. When compiling with the following command, the pthread_join() function returns a 64-bit void* value as the result:
$ CC/POINTER_SIZE=LONG
You can also use pthread_join64() or pthread_join32() to specify the length in bits of the return value.
Note that no other DECthreads functions have special 64-bit versions because the OpenVMS Alpha calling standard always supports 64-bit arguments and return values.
Table B-3 lists the DECthreads condition values for OpenVMS systems and provides an explanation and user action.
Condition Value | Explanation and User Action |
---|---|
CMA$_EXCCOP | Exception raised; OpenVMS condition code follows. |
Explanation: One of the DECthreads exception commands (RAISE or RERAISE) raised or reraised an exception condition originating outside the DECthreads library. The secondary condition code in the signal vector will be the original code. | |
User Action: See the documentation for the software that your program is calling to determine the reason for this exception. | |
CMA$_EXCCOPLOS | Exception raised; some information lost. |
Explanation: CMA$_EXCCOPLOS is nearly the same as CMA$_EXCCOP except that DECthreads determined that the copied signal vector may contain address arguments. However, the address arguments may not be valid when the stack is unwound and the condition is resignaled. Therefore, DECthreads clears the condition codes' arguments in the resignaled vector. In most cases, DECthreads knows that SS$_ code arguments are "safe" and will not clear them. Most other codes with arguments will result in CMA$_EXCCOPLOS. | |
User Action: See the documentation for the software that your program is calling to determine the reason for this exception. | |
CMA$_EXCEPTION | Exception raised; address of exception object is object-address. |
Explanation: This condition is used as the primary condition to RAISE an address-type DECthreads exception. The condition is signaled with a single argument containing the address of the EXCEPTION structure. There is no support for interpreting this value. It is only meaningful to the facility that defined the EXCEPTION. It is not good programming practice to let an address exception propagate outside the facility that raised it. There is no support for getting message text, and it cannot be interpreted by other facilities. | |
User Action: None. |
This section applies to OpenVMS Alpha systems only.
Under OpenVMS Alpha Version 7.0 and later, DECthreads implements a new scheduling model, referred to as two-level scheduling. This model is based on the concept of virtual processors. Virtual processors are implemented as a result of using kernel thread technology in the OpenVMS Alpha operating system.
DECthreads schedules threads onto virtual processors similar to the way that OpenVMS schedules processes onto the processors of a multiprocessing machine. Thus, to the DECthreads runtime environment, a scheduled thread is executed on a virtual processor until it blocks or until it exhausts its timeslice quantum; then DECthreads schedules a new thread to run.
While DECthreads schedules threads onto virtual processors, the OpenVMS scheduler also schedules virtual processors to run on physical processors. The term two-level scheduling derives from this relationship.
The two-level scheduling model provides these advantages:
The key to making the two-level scheduling model work is the upcall mechanism. An upcall is a communication between the OpenVMS scheduler and the DECthreads scheduler. When an event occurs that affects the scheduling of a thread, such as blocking for a system service, the OpenVMS scheduler calls "up" to the DECthreads scheduler to notify it of the change in the thread's status.
This upcall gives DECthreads the opportunity to schedule another thread to run on the virtual processor in place of the blocking thread, rather than to allow the virtual processor itself to block, which would deny that resource to other threads in the process.
Upcalls are typically arranged in pairs, with an "unblock" upcall corresponding to each "block" upcall. The unblock upcall notifies DECthreads that a previously blocked thread is now eligible to run again. DECthreads schedules that thread to run when it is appropriate, given the thread's scheduling policy and priority.
In OpenVMS Alpha Version 7.1 and later, the linker supports the /THREADS_ENABLE (or /NOTHREADS_ENABLE) qualifier for specifying the role of kernel threads in the resulting image. Use this qualifier to specify that the image controls whether the process can create multiple kernel threads and whether the OpenVMS Alpha kernel's support for DECthreads upcalls is enabled. If this qualifier is not specified, the default linker setting is /NOTHREADS_ENABLE, which results in an image that behaves as under OpenVMS Alpha Version 6.
The /THREADS_ENABLE qualifier takes two keyword arguments, MULTIPLE_KERNEL_THREADS and UPCALLS. Table B-4 summarizes the allowable combinations of these keywords and their effects.
Keywords Specified | Result |
---|---|
/NOTHREADS_ENABLE | No kernel threads support for DECthreads |
/THREADS_ENABLE
or: /THREADS_ENABLE=(MULTIPLE_KERNEL_THREADS,UPCALLS) |
Full kernel threads support for DECthreads, including the ability to run multiple use threads simultaneously on different CPUs on a multiprocessor machine |
/THREADS_ENABLE=MULTIPLE_KERNEL_THREADS | Same behavior as if /NOTHREADS_ENABLE is specified (without support for upcalls, DECthreads cannot reliably use multiple kernel threads) |
/THREADS_ENABLE=UPCALLS | Upcall support for DECthreads (such as making system calls thread-synchronous), but restricts the process's threads to one CPU on a multiprocessor machine |
Note
Under no circumstances should a process explicitly create kernel threads. DECthreads creates them as needed when allowed to do so. Explicit creation of kernel threads by an application disrupts the operation of the DECthreads runtime environment and causes incorrect and/or unreliable application behavior.
Under OpenVMS Alpha only, use the THREADCP tool to set or show the kernel threads features described above for an existing image. The tool provides the ability to enable, disable, and show the state of the thread control bits in an image's header.
The THREADCP command verb is not part of the normal set of DCL commands. To use the tool, you must define the command verb before invoking it.