HP OpenVMS Systems Documentation
OpenVMS Programming Concepts Manual
6.3.2 Multiprocessor Operations
On multiprocessor systems, you must use special methods to ensure that a read-modify-write sequence is atomic. On VAX systems, interlocked instructions provide synchronization; on Alpha systems, load-locked and store-conditional instructions provide synchronization.
On VAX systems, a number of uninterruptible instructions are provided that both read and write memory with one instruction. When used with an operand type that is accessible in a single memory operation, each instruction provides an atomic read-modify-write sequence. The sequence is atomic with respect to threads of execution on the same VAX processor, but it is not atomic to threads on other processors. For instance, when a VAX CPU executes the instruction INCL x, it issues two separate commands to memory: a read, followed by a write of the incremented value. Another thread of execution running concurrently on another processor could issue a command to memory that reads or writes location x between the INCL's read and write. Section 6.4.4 describes read-modify-write sequences that are atomic with respect to threads on all VAX CPUs in an SMP system.
On a VAX multiprocessor system, an atomic update requires an interlock at the level of the memory subsystem. To perform that interlock, the VAX architecture provides a set of interlocked instructions that include Add Aligned Word Interlocked (ADAWI), Remove from Queue Head Interlocked (REMQHI), and Branch on Bit Set and Set Interlocked (BBSSI).
If you code in VAX MACRO, you use the assembler to generate whatever instructions you tell it. If you code in a high-level language, you cannot assume that the compiler will compile a particular language statement into a specific code sequence. That is, you must tell the compiler explicitly to generate an atomic update. For further information, see the documentation for your high-level language.
On Alpha systems, there is no single instruction that performs an
atomic read-modify-write operation. An atomic read-modify-write
operation is only possible through a sequence that includes load-locked
and store-conditional instructions, (see Section 6.4.2). Use of these
instructions provides a read-modify-write operation on data within one
aligned longword or quadword that is atomic with respect to threads on
all Alpha CPUs in an SMP system.
On VAX systems, the following features assist with synchronization at the hardware level:
On VAX systems, many read-modify-write instructions, including queue manipulation instructions, are noninterruptible. These instructions provide an atomic update capability on a uniprocessor. A kernel-mode code thread can block interrupt and process-based threads of execution by raising the IPL. Hence, it can execute a sequence of instructions atomically with respect to the blocked threads on a uniprocessor. Threads of execution that run on multiple processors of an SMP system synchronize access to shared data with read-modify-write instructions that interlock memory.
On Alpha systems, some of these mechanisms are present, while others have been implemented in PALcode routines.
Alpha processors provide several features to assist with synchronization. Even though all instructions that access memory are noninterruptible, no single one performs an atomic read-modify-write. A kernel-mode thread of execution can raise the IPL in order to block other threads on that processor while it performs a read-modify-write sequence or while it executes any other group of instructions. Code that runs in any access mode can execute a sequence of instructions that contains load-locked (LDx_L) and store-conditional (STx_C) instructions to perform a read-modify-write sequence that appears atomic to other threads of execution. Memory barrier instructions order a CPU's memory reads and writes from the viewpoint of other CPUs and I/O processors. Other synchronization mechanisms are provided by PALcode routines.
The sections that follow describe the features of interrupt priority
level, load-locked (LDx_L) and store-conditional
(STx_C) instructions, memory barriers, interlocked
instructions, and PALcode routines.
The operating system in a uniprocessor system synchronizes access to systemwide data structures by requiring that all threads sharing data run at the highest-priority IPL of the highest-priority interrupt that causes any of them to execute. Thus, a thread's accessing of data cannot be interrupted by any other thread that accesses the same data.
The IPL is a processor-specific mechanism. Raising the IPL on one processor has no effect on another processor. You must use a different synchronization technique on SMP systems where code threads run concurrently on different CPUs that must have synchronized access to shared system data.
On VAX systems, the code threads that run concurrently on different processors synchronize through instructions that interlock memory in addition to raising the IPL. Memory interlocks also synchronize access to data shared by an I/O processor and a code thread.
On Alpha systems, access to a data structure that is shared either by
executive code running concurrently on different CPUs or by an I/O
processor and a code thread must be synchronized through a
Because Alpha systems do not provide a single instruction that both reads and writes memory or mechanism to interlock memory against other interlocked accesses, you must use other synchronization techniques. Alpha systems provide the load-locked/store-conditional mechanism that allows a sequence of instructions to perform an atomic read-modify-write operation.
Load-locked (LDx_L) and store-conditional (STx_C) instructions guarantee atomicity that is functionally equivalent to that of VAX systems. The LDx_L and STx_C instructions can be used only on aligned longwords or aligned quadwords. The LDx_L and STx_C instructions do not provide atomicity by blocking access to shared data by competing threads. Instead, when the LDx_L instruction executes, a CPU-specific lock bit is set. Before the data can be stored, the CPU uses the STx_C instruction to check the lock bit. If another thread has accessed the data item in the time since the load operation began, the lock bit is cleared and the store is not performed. Clearing the lock bit signals the code thread to retry the load operation. That is, a load-locked/store-conditional sequence tests the lock bit to see whether the store succeeded. If it did not succeed, the sequence branches back to the beginning to start over. This loop repeats until the data is untouched by other threads during the operation.
By using the LDx_L and STx_C instructions together, you can construct a code sequence that performs an atomic read-modify-write operation to an aligned longword or quadword. Rather than blocking other threads' modifications of the target memory, the code sequence determines whether the memory locked by the LDx_L instruction could have been written by another thread during the sequence. If it is written, the sequence is repeated. If it is not written, the store is performed. If the store succeeds, the sequence is atomic with respect to other threads on the same processor and on other processors. The LDx_L and STx_C instructions can execute in any access mode.
Traditional VAX usage is for interlocked instructions to be used for multiprocessor synchronization. On Alpha systems, LDx_L and STx_C instructions implement interlocks and can be used for uniprocessor synchronization. To achieve protection similar to the VAX interlock protection, you need to use memory barriers along with the load-locked and store-conditional instructions.
Some Alpha system compilers make the LDx_L and STx_C
instruction mechanism available as language built-in functions. For
example, Compaq C on Alpha systems includes a set of built-in functions
that provides for atomic addition and for logical AND and OR
operations. Also, Alpha system compilers make the mechanism available
implicitly, because they use the LDx_L and STx_C
instructions to access declared data as requiring atomic accesses in a
The Alpha Architecture Reference Manual, Third Edition (AARM) describes strict rules for using interlocked memory instructions. The new Alpha 21264 (EV6) processor and all future Alpha processors are more stringent than their predecessors in their requirement that these rules be followed. As a result, code that has worked in the past, despite noncompliance, could fail when executed on systems featuring the new 21264 processor. Occurrences of these noncompliant code sequences are believed to be rare. Note that the 21264 processor is not supported on versions prior to OpenVMS Alpha Version 7.1--2.
Noncompliant code can result in a loss of synchronization between processors when interprocessor locks are used, or can result in an infinite loop when an interlocked sequence always fails. Such behavior has occurred in some code sequences in programs compiled on old versions of the BLISS compiler, some versions of the MACRO--32 compiler and the MACRO--64 assembler, and in some Compaq C and Compaq C++ programs.
For recommended compiler versions, see Section 220.127.116.11.
The affected code sequences use LDx_L/STx_C instructions, either
directly in assembly language sources or in code generated by a
compiler. Applications most likely to use interlocked instructions are
complex, multithreaded applications or device drivers using highly
optimized, hand-crafted locking and synchronization techniques.
OpenVMS recommends that code that will run on the 21264 processor be checked for these sequences. Particular attention should be paid to any code that does interprocess locking, multithreading, or interprocessor communication.
The SRM_CHECK tool (named after the System Reference Manual,
which defines the Alpha architecture) has been developed to analyze
Alpha executables for noncompliant code sequences. The tool detects
sequences that might fail, reports any errors, and displays the machine
code of the failing sequence.
The SRM_CHECK tool can be found in the following location on the OpenVMS Alpha Version 7.2 Operating System CD-ROM:
To run the SRM_CHECK tool, define it as a foreign command (or use the DCL$PATH mechanism) and invoke it with the name of the image to check. If a problem is found, the machine code is displayed and some image information is printed. The following example illustrates how to use the tool to analyze an image called myimage.exe:
The tool supports wildcard searches. Use the following command line to initiate a wildcard search:
Use the -log qualifier to generate a list of images that have been checked. You can use the -output qualifier to write the output to a data file. For example, the following command directs output to a file named CHECK.DAT:
You can use the output from the tool to find the module that generated the sequence by looking in the image's MAP file. The addresses shown correspond directly to the addresses that can be found in the MAP file.
The following example illustrates the output from using the analysis tool on an image named SYSTEM_SYNCHRONIZATION.EXE:
The MAP file for system_synchronization.exe contains the following:
The address 360C is in the SMPROUT module, which contains the addresses from 0-47BB. By looking at the machine code output from the module, you can locate the code and use the listing line number to identify the corresponding source code. If SMPROUT had a nonzero base, it would be necessary to subtract the base from the address (360C in this case) to find the relative address in the listing file.
Note that the tool reports potential violations in its output.
Although SRM_CHECK can normally identify a code section in an image by
the section's attributes, it is possible for OpenVMS images to contain
data sections with those same attributes. As a result, SRM_CHECK may
scan data as if it were code, and occasionally, a block of data may
look like a noncompliant code sequence. This circumstance is rare and
can be detected by examining the MAP and listing files.
The areas of noncompliance detected by the SRM_CHECK tool can be grouped into the following four categories. Most of these can be fixed by recompiling with new compilers. In rare cases, the source code may need to be modified. See Section 18.104.22.168 for information about compiler versions.
If the SRM_CHECK tool finds a violation in an image, the image should
be recompiled with the appropriate compiler (see Section 22.214.171.124). After
recompiling, the image should be analyzed again. If violations remain
after recompiling, the source code must be examined to determine why
the code scheduling violation exists. Modifications should then be made
to the source code.
The Alpha Architecture Reference Manual describes how an atomic update of data between processors must be formed. The Third Edition, in particular, has much more information on this topic.
Exceptions to the following two requirements are the source of all known noncompliant code:
Therefore, the SRM_CHECK tool looks for the following:
To illustrate, the following are examples of code flagged by SRM_CHECK.
In the above example, an LDQ instruction was found after an LDQ_L before the matching STQ_C. The LDQ must be moved out of the sequence, either by recompiling or by source code changes. (See Section 126.96.36.199.)
In the above example, a branch was discovered between the LDL_L and STL_C. In this case, there is no "fall through" path between the LDx_L and STx_C, which the architecture requires.
The following MACRO--32 source code demonstrates code where there is a "fall through" path, but this case is still noncompliant because of the potential branch and a memory reference in the lock sequence.
To correct this code, the memory access to read the value of INDEX must first be moved outside the LDQ_L/STQ_C sequence. Next, the branch between the LDQ_L and STQ_C, to the label IS_CLEAR, must be eliminated. In this case, it could be done using a CMOVEQ instruction. The CMOVxx instructions are frequently useful for eliminating branches around simple value moves. The following example shows the corrected code:
188.8.131.52 Compiler Versions
This section contains information about versions of compilers that may generate noncompliant code sequences and the recommended versions to use when recompiling.
Table 6-1 contains information for OpenVMS compilers.
Current versions of the MACRO--64 assembler may still encounter the loop rotation issue. However, MACRO--64 does not perform code optimization by default, and this problem occurs only when optimization is enabled. If SRM_CHECK indicates a noncompliant sequence in the MACRO--64 code, it should first be recompiled without optimization. If the sequence is still flagged when retested, the source code itself contains a noncompliant sequence that must be corrected.