Guide to DECthreads


Previous | Contents

In a THREADCP command, an image file name is a required parameter for use with all supported qualifiers. THREADCP supports abbreviations to the first character for all qualifiers and parameters. When the SHOW qualifier is used alone with the THREADCP command, the file name can contain wildcard characters.

After you define the THREADCP command verb, an image's thread control bits can be set or cleared using the /ENABLE and /DISABLE qualifiers, respectively. To do so, specify the name of each thread control bit to be enabled, disabled, or shown. One or both thread control bits can be specified. The user must have write access to the image file.

If no thread control bit is specified, the THREADCP default is to operate on both bits. If the image is currently being executed or is installed, it cannot be modified.

B.10.2.1 Examples

This command defines the THREADCP command verb:

 
   $ SET COMMAND SYS$UPDATE:THREADCP.CLD 
 

This command displays the current settings of both thread control bits for the image TEST.EXE:

 
   $ THREADCP/SHOW TEST.EXE 
 

This command displays the current settings of both thread control bits for all SYS$SYSTEM images:

 
   $ THREADCP/SHOW SYS$SYSTEM:* 
 

This command sets both thread control bits explicitly for the image TEST.EXE:

 
   $ THREADCP/ENABLE=(MULTIPLE_KERNEL_THREADS, UPCALLS) TEST.EXE 
 

This command clears both thread control bits explicitly for the image TEST.EXE:

 
   $ THREADCP/DISABLE=(MULTIPLE_KERNEL_THREADS, UPCALLS) TEST.EXE 
 

B.10.3 Querying and Setting Kernel Threads Features

On OpenVMS Alpha systems, a program can call the $GETJPI system service and specify the appropriate MULTITHREAD item code to determine whether kernel threads are in use. The return values have the same meanings as are defined for the MULTITHREAD system parameter, as summarized in Table B-5.

Table B-5 Return Values from$GETJPI System Service
Value Description
0 Both Thread Manager upcalls and the creation of multiple kernel threads are disabled.
1 Thread Manager upcalls are enabled; the creation of multiple kernel threads is disabled.
2 through 16 Both Thread Manager upcalls and the creation of multiple kernel threads are enabled. The number specified represents the maximum number of kernel threads that can be created for a single process.

B.10.4 Creation of Virtual Processors

Virtual processors are created as they are needed by the application. For a multithreaded application, the number of virtual processors that DECthreads creates is limited by the SYSGEN parameter MULTITHREAD. This parameter is typically set to the number of processors present in the system.

In general, there is no reason to create more virtual processors than there are physical processors; that is, the virtual processors would contend with each other for the physical processors and cause unnecessary overhead. Regardless of the value of the MULTITHREAD parameter, DECthreads creates no more virtual processors than there are user threads (excluding DECthreads internal threads).

DECthreads does not delete virtual processors or let them terminate. They are retained in the HIB idle state until they are needed again. During image rundown, they are deleted by OpenVMS.

The DECthreads scheduler can schedule any user thread onto any virtual processor. Therefore, a user thread can run on different kernel threads at different times. Normally, this should pose no problem; however, for example, a user thread's PID (as retrieved by querying the system) can change from time to time.

B.10.5 Delivery of ASTs

When a user mode AST becomes deliverable to a DECthreads process, the OpenVMS scheduler makes an upcall to DECthreads, passing the information that is required to deliver the AST (service routine address, argument, and target user thread ID). DECthreads stores this information and queues the AST to be delivered to the appropriate user thread. That thread is made runnable (if it is not already), and executes the AST routine the next time it is scheduled to run. This means the following:

In addition to per-thread ASTs, there are also user mode ASTs that are directed to the process as a whole, or to no thread in particular, or to a thread that has since terminated. These "process" ASTs are queued to the initial thread, making the thread runnable in a fashion similar to per-thread ASTs. They are executed in the context of the initial thread, for the following reasons:


Note

In OpenVMS Version 7.0 and later, all ASTs are directed to the process as a whole. In future releases, AST delivery will be made per thread as individual services are updated.

The following implications must be considered for application development:

B.10.6 Blocking System Services

All blocking system services are thread synchronous in OpenVMS Alpha Version 7.0 and later. That is, they block only the calling thread. When the thread is to be blocked by the system service, the OpenVMS scheduler makes an upcall to allow DECthreads to schedule another user thread to execute. Therefore, only the calling thread is blocked, all other threads are unaffected, and the process continues running. When the service completes, the thread is awakened by means of another upcall, and DECthreads schedules it to run again at the thread's next opportunity.

This applies to all "W" forms of system services. For example, $QIOW, $END_TRANSW, and $GETJPIW. Additionally, this applies to the following event flag services: $WAITFR, $WFLAND, and $WFLOR.

B.10.7 $HIBER and $WAKE

$HIBER and $WAKE result in upcalls to DECthreads. When a user thread calls $HIBER, only that thread is blocked; all other threads continue running. The blocking thread is immediately unscheduled and another thread is scheduled to run instead. When a thread (or another process) calls $WAKE, all hibernating threads are awakened.

Prior to OpenVMS Version 7.0, a thread that called a $HIBER (or called a library routine that eventually resulted in a call to $HIBER) would cause the whole process to hibernate for a brief period whenever that thread was scheduled to "run." Also, with multiple threads in calls to $HIBER simultaneously, there was no reliable way to wake the threads (or a specific thread); the next hibernating thread to be scheduled would awaken, and any other threads would continue to sleep.

In OpenVMS Alpha Version 7.0 and later, these problems have been resolved. However, this new behavior has some other effects. For instance, hibernation-based services, such as LIB$WAIT and the C RTL sleep() routine, may be prone to premature completion. If the service does not validate its wakeup (that is, ensure that enough time has passed or that there is some other reason for it to return), then it will be prone to this problem, as are the above services, since they do not perform such wake-up validation.

B.10.8 Event Flags

All event flags are shared by all threads in the process. Therefore, it is possible for different threads' use of the same event flag to cause interference. That is, if two threads use the same event flag in calls to different system services, whichever service completes first will cause both threads to awaken, even though the other service has not completed. This situation can be resolved by specifying an I/O status block (IOSB) for those system services that use them. When an IOSB is present, the blocked thread will not be awakened when the event flag is set, unless the IOSB has also been written.

Note that a DECthreads process is rarely in LEF state. In general, instead of blocking for an event flag wait, DECthreads schedules another thread to be run. If no threads are available, DECthreads schedules a "null" thread, which places the virtual processor in HIB state until it is needed to execute a thread.

Note that no upcall is made for waits on a common event flag. If a thread waits on a common event flag, the virtual processor blocks until the wait is satisfied. (On a uniprocessor, this is most likely to block all threads.)

B.10.9 Interactions with OpenVMS

There are several interactions with the OpenVMS operating system that should be noted:

B.10.10 Image Exit

In multithreaded processes, image exit occurs as follows: $EXIT does not immediately invoke exit handler routines. Instead, it results in an upcall that causes DECthreads to schedule a special thread to execute the exit-handler routines. $EXIT then calls pthread_exit() to terminate the calling thread. This allows the calling thread to release any resources that it might be holding.

To avoid possible deadlocks, the exit-handler routines are executed in a separate thread. For example, if a thread calls $EXIT while holding a mutex that is required by an exit-handler routine, then that routine causes the thread to block forever, as it waits for a mutex that it already holds. Because the exit-handler routine executes in a separate thread, it can block while the thread holding the mutex cleans up.

$FORCEX works in an analogous fashion. Instead of invoking $EXIT directly, it causes an upcall that allows DECthreads to release the exit-handler thread.

DCL Ctrl/Y continues to work as it always has on multithreaded applications. However, typing EXIT or issuing any other command that invokes a new image causes the $FORCEX upcall. While this is an improvement in many cases over the behavior prior to OpenVMS Version 7.0, it does not guarantee that the multithreaded application will exit.

For example, if the application is deadlocked, holding a resource required by one of the exit handler's routines, the application will continue to hang, even after typing Ctrl/Y and EXIT. In these cases, type Ctrl/Y and STOP to terminate the application without running exit handlers. Note that doing so causes the application to be unable to clean up, and it may leave data files and the terminal in an inconsistent state.

B.10.11 SYSGEN Parameter MULTITHREAD

The SYSGEN parameter MULTITHREAD limits the maximum number of kernel threads per process. It is set by AUTOGEN to the number of CPUs on the system. If MULTITHREAD is set to zero (0), two-level scheduling support is disabled, and DECthreads reverts to its behavior prior to OpenVMS Version 7.0---that is, no upcalls can occur, and DECthreads does not use all processors in multiprocessor systems.

B.10.12 Process Control System Services and DCL Commands

OpenVMS system services and DCL commands are either process based or operate on a per-thread basis. This section identifies several system services on this basis.

B.10.12.1 Process-Level System Services

The following system services continue to be process based: $SUSPEND, $RESUME, and $DELPRC. These services will operate on an entire process; they are not thread based. For example, $SUSPEND issued by a thread will suspend all of the virtual processors in process, not just the calling thread.

Under OpenVMS Version 7.0 or later, it is possible to see all but one of your kernel threads in SUSP state, such as when at a breakpoint in the debugger. This effect is a part of the debugging support and is not the result of calling $SUSPND.

B.10.12.2 Kernel-Level System Services

The following system services now operate on a per-thread basis: $HIBER, $SCHDWK, and $SYNCH. These services will not operate on an entire process; they are thread based. For example, $HIBER will cause the calling thread to become inactive but will not affect other threads in the process.

B.10.12.3 DCL Commands

The following DCL commands operate as indicated:

B.11 Interoperability with POSIX for OpenVMS

Previous releases of the POSIX for OpenVMS layered product had very limited interoperability with DECthreads. Under OpenVMS Version 7.0 and later, using DECthreads with the POSIX for OpenVMS layered product is not supported.


Appendix C
Considerations for Windows NT Systems

This appendix discusses DECthreads issues and restrictions specific to its implementation under the Windows NT operating system.

C.1 DECthreads Interfaces on Windows NT Systems

The Win32 subsystem provides support for multithreading through the Win32 Application Programming Interface (API). The Win32 API allows for thread creation, termination, synchronization, and other thread functions.

The DECthreads and Win32 threads libraries are interoperable. Threads created using the Win32 API can use DECthreads synchronization primitives, for example, and DECthreads-created threads can use the Win32 synchronization primitives. See Section C.4 for more information.

C.1.1 Pthread Interface

To add value to the Win32 API, DECthreads provides its POSIX 1003.1c-1995 (pthread) interface, which is available across all DIGITAL platforms.

Note these differences in support for the DECthreads pthread interface routines on Windows NT systems:

In addition, the DECthreads exception package is also available to support handling of DECthreads exceptions in conjunction with use of the pthread interface. See Section C.4 for more information.

The following DECthreads pthread interface routines are not implemented for Windows NT systems, and also do not return errors or generate exceptions:

Do not use these routines. In a future DECthreads release they will be changed to return errors and generate exceptions:

The pthread_attr_setstacksize() routine does not change the stack size of newly created threads. On Windows NT a DECthreads-created thread is created with a stack of the same size as for its process's primary thread. The stack size grows as needed. (See the Win32 documentation for the CreateThread() routine.) No errors or exceptions are generated as a result of using pthread_attr_setstacksize().

The corresponding pthread_attr_getstacksize() routine returns the value that was set using pthread_attr_setstacksize(), but this is not useful because a DECthreads-created thread is not created using the thread attributes object's stack size attribute.

An attempt to set the scheduling policy or priority attributes of an existing thread or in a thread attributes object using of the following routines produces a return value of [ENOTSUP]:

C.1.2 Other Interfaces

The DECthreads thread-independent services (or tis) interface is not yet available for Windows NT systems.

The DECthreads proprietary CMA (or cma) and POSIX 1003.4a/Draft 4 (or d4) interfaces are no longer offered for Windows NT systems. See Appendix E and Appendix F for information about migrating your applications from the DECthreads cma and d4 interfaces, respectively, to the DECthreads pthread interface.

C.2 Compiling DECthreads Applications

For a dynamically linked program, compile with the /MT switch. For a statically linked program, compile with the /MD switch. These switches ensure that reentrant definitions, such as errno, are used. For example:

%  CL /c myprog.c /MT

C.3 Linking DECthreads Applications

Applications that use the C run-time library should be linked against one of two C run-time libraries that support multithreading, as follows:

Multithreaded applications should not link against libc.lib because it does not support multithreading.

C.4 Interoperability of Win32 API and DECthreads pthread Routines

Win32 threads can create, use, and operate on DECthreads primitives, such as synchronization objects, as any DECthreads-created thread can. Win32 threads can operate on both DECthreads-created threads and other Win32 threads using the DECthreads pthread interface routines---that is, Win32 threads can join with them, cancel them, and so on.

A Win32 thread can operate directly on a DECthreads-created thread using the Win32 API, provided that it can obtain a Win32 handle to that thread. A Win32 thread can synchronize with a DECthreads-created thread and with other Win32 threads using DECthreads synchronization objects, just as DECthreads-created threads synchronize with each other. A Win32 thread can synchronize with a DECthreads-created thread using Win32 synchronization objects the same way that Win32 threads synchronize with each other.

A DECthreads-created thread can use the Win32 API to obtain a Win32 handle to itself, which allows it to be operated on via Win32 API routines. It can operate on Win32 threads and DECthreads-created threads (including itself) using Win32 API routines the same way that Win32 threads operate on each other. It can synchronize with Win32 threads and other DECthreads-created threads using Win32 synchronization objects the same way that Win32 threads synchronize with each other. It can synchronize with Win32 threads using DECthreads synchronization objects. It can operate on Win32 threads, use pthread_cancel() to cancel them, use pthread_join() to join with them, and so on---but only if the Win32 thread has previously made a call into the DECthreads library, which registers the Win32 thread with the DECthreads run-time environment.

It is strongly recommended that a DECthreads-created thread not call TerminateThread(). This routine terminates a thread immediately and does not provide any means for the thread to clean up any context it might have acquired while executing. Context that is left in an inconsistent state when calling TerminateThread(), such as a locked mutex, can cause unpredictable results in your program.

Using pthread_cancel() to cancel the initial (or "primary") thread of a Win32 process causes the process to exit in the same manner as if it were terminated as the result of a call to the Win32 routine ExitThread().

DECthreads structured exception handling is interoperable with Win32 exception handling, but has a slightly different syntax. DECthreads supports a different set of tokens for defining exception blocks. The DECthreads tokens include TRY, CATCH, CATCH_ALL, FINALLY, and ENDTRY. The Microsoft tokens include __try, __except, and __finally.

The DECthreads exception semantics support multiple catch clauses and provide support for creating unique exceptions (that is, DECthreads address exceptions). For more information, see Chapter 5.

DECthreads exception blocks can be nested inside of Win32 exception blocks, and vice versa. The DECthreads CATCH_ALL macro will catch all exceptions---those raised via DECthreads exception handling macros, those raised via Win32, and system-raised exceptions.

C.5 Thread Cancelability of System Services

Win32 system calls are not cancelation points. None of the system calls should be called with asynchronous cancelation enabled. For more information, see Section 2.3.7.


Appendix D
Debugging Multithreaded Applications

The debugging information in this appendix is specific to applications that use DECthreads.

D.1 Using PTHREAD_CONFIG

During initialization of the DECthreads run-time environment, the PTHREAD_CONFIG environment variable (on DIGITAL UNIX and Windows NT systems) or logical symbol (on OpenVMS systems), if defined, is used to set static options for the multithreaded program. You can set PTHREAD_CONFIG to assist you in debugging a DECthreads application.

D.1.1 Major and Minor Keywords

As summarized in Table D-1, PTHREAD_CONFIG takes "major keywords" as arguments. Use a "minor keyword" to specify a value for each major keyword.

Table D-1 PTHREAD_CONFIG Settings
Major keyword Minor keyword Meaning
dump= file-path Path of DECthreads bugcheck file
meter= condition
mutex
stack
thread
all
none
Meter condition variable operations
Meter mutex operations
Record thread greatest stack extent
(Unused)
Meter all available operations
No metering
width= bugcheck_output_width Width of output from DECthreads bugcheck output

D.1.2 Specifying Multiple Values

When setting PTHREAD_CONFIG, use a semicolon to separate major keyword expressions and use a comma to separate minor keyword values. For example, using the C shell under DIGITAL UNIX, you can set PTHREAD_CONFIG as follows:

 
   % setenv PTHREAD_CONFIG meter=thread,mutex;dump=/tmp/dump-%d.dmp;width=132 
 


Previous | Next | Contents