The Question is:
Ref: WIZ_5843 (thanks for the reply)
In my previous question a wizard said...
>> The aio POSIX.4 routines are part of the existing POSIX environment,
>> and are also part of the new DII COE work.
Does the "existing POSIX environment" mean it's available NOW, prior to
DII COE, if so where can I get it for OpenVMS V7.2-1 ?
In the same topic, it was also stated that...
>> The difficulties involved in mixing DECthreads and ASTs are inherent
>> in the internal implementations of DECthreads and of ASTs.
Since asynchronous I/O via the AIO_xxx routines and Pthreads can be
mixed, does this imply that AST's + Pthreads will be able to be mixed
as well with the DII COE implementation ?
The Answer is :
A POSIX kit is (was) available on older OpenVMS releases, but it is
not available on OpenVMS V7.2-1, and existing POSIX kits are not
compatible with OpenVMS V7.2-1, and are no longer supported. DII COE
(V7.2-6C1) is in beta-test at present, integration of the DII COE work
back into the OpenVMS mainline releases is not expected prior to OpenVMS
V7.4 -- at the earliest.
There may be an updated POSIX kit for OpenVMS V7.2-1 or V7.3, but --
assuming that a decision is made to provide it -- the kit will not be
made available until after the release of V7.2-6C1.
As for the second question...
The aio_xxx routines should be assumed to be quite different from the
OpenVMS AST routines. (These routines are currently implemented using
$qio, $synch, and AST completions, but any assumptions of the internal
implementation are perilous at best.)
Threads (of any sort) and OpenVMS AST routines must be carefully mixed.
There are stringent restrictions on what an application can do within
an AST routine executing within a multithreaded process. If the
application is calling routines other than (most) system services, or
writing longword or quadword-aligned data cells, the routine should
likely be translated into a thread. (Many APIs are either AST-safe
or thread-safe; most are not both AST- and thread-safe, and -- in a
multithreaded process -- the calls that claim to be thread-safe are
likely not AST-safe. The OpenVMS Wizard is aware of one OpenVMS
system service -- sys$mount -- that is known to be not thread-safe.)
Upcalls use ASTs, meaning you must have ASTs enabled in order
to timeslice between threads. From the perspective of the OpenVMS
executive, upcalls and thread time-slicing are implemented via ASTs
-- these ASTs are refer to here as "system ASTs". Also from the
perspective of OpenVMS, "application ASTs" are not administered by
the executive -- rather, they are handed off to the threads library
via an upcall (which happens to be a "system AST"). The threads
library then causes the initial thread to begin executing the
application AST, but the AST environment is not what OpenVMS would
consider AST mode -- the threads library simulates the AST. (The
threads library does not set the USER bit in the ASTSR field of the
PHD.) That said, the "application AST" will behave very similarly to
the behaviour of a hardware AST within a non-threaded application.
The threads library emulation underlying the "application AST" extends
to support of application calls to AST-related routines including
lib$ast_in_prog, $getjpi, $setast, etc. (With upcalls enabled, the
application can freely use and alter the "application AST" state,
short of making direct PALcode calls.)
An additional complicating factor involves the two modes for threaded
applications. If the application has upcalls disabled, then threads
library has no way to participate in AST delivery. In this senario,
time-slicing involves a timer AST and thus if the application disables
AST delivery, time-slicing will not function. Nor, for that matter,
will other aspects of the threads library function when upcalls are
disabled and when ASTs are disabled.
There is currently no mechanism to target an AST at a particular
thread, as all ASTs run in the initial thread when upcalls are
enabled. (There has been some consideration given to targeting an
AST back to the thread that generated it, but that support is not
available.) With upcalls disabled, the ASTs can run in any thread.
Specifically, with upcalls disabled the ASTs will run in whichever
thread was executing when the AST became pending.
While event flag operation works the same in all situations -- with
threading or not, and with upcalls enabled or disabled. With upcalls
disabled, if a thread blocks on an event flag, it blocks the entire
process until the thread is pre-empted (eg: at the end of its execution
quantum). This also implies that threads that are not currently
executing will not notice an event flag state transition until they
are scheduled and resume execution. Furthermore, if the event flag
of interest is set and then cleared, a ready thread may not notice.
(Accordingly, use of IOSBs is strongly recommended.)
With upcalls enabled, a thread which blocks on a local event flag
is immediately removed from scheduling contention -- the result of
a synchronous upcall -- and another thread is scheduled and run.
When the local event flag becomes set, the threads library receives
an upcall via a "system AST" and schedules all threads blocked for
that local event flag. If there are IOSBs specified -- as there
should be -- the threads library will schedule the threads only when
the IOSB has been written.
With upcalls and kernel threading enabled, more than one thread can
be active at a time (and can operate across multiple processors in
an SMP system), but only one AST can be active at a time. You
might well see a thread and an AST active in parallel.
Threaded applications freely assume that wait states are permitted
and not affect other threads, while AST routines generally do not
and should not include waits.
AST and interrupt routines cannot access a thread mutex, nor can AST
or interrupt code call most of the thread routines.
With upcalls, AST routines do not block threads running on other
processors. (Conversely, with upcalls disabled, threads are not
running in parallel on multiple processors.)
With upcalls enabled, any thread can call $setast and can enable or
disable ASTs across all threads. If a thread disables AST delivery
via $setast, then ASTs will not be delivered while that thread is running.
With upcalls enabled, the AST delivery status (enabled or disabled)
is maintained on a per-thread bases, and ASTs which are not directed
to a particular thread (eg: all "application ASTs", at present) will
not be delivered to the process as long as any non-terminated thread
has ASTs delivery disabled; with upcalls disabled, the AST delivery
state is context-switched along with the rest of the thread context.
(eg: If a thread had AST delivery enabled when it last ran, then AST
delivery will be enabled when it next runs, regardless of the AST
delivery state from the previous thread.)
AST re-entrancy is a subset of thread re-entrancy -- a routine that
is AST re-entrant may not be thread re-entrant. (Why? AST routines
can implicitly assume that only one AST routine is active at a time.)
That a routine is or is not AST-safe does not imply it is or is not
thread-safe. Two terms apply here: "thread-safe", and "thread-reentrant".
A thread-safe routine refers simply to the routine being safe to call
in a multi-threaded environment -- that is, the function will operate
correctly, without access violations or memory corruptions, when called
simultaneously or concurrently across multiple threads. (How the thread
might achieve this is unspecified.) One of the most typical ways to
make a function thread-safe is to place synchronization within the
routine to ensure that only one thread can execute the critical code
within the routine at one time. The second term, "thread-reentrant",
refers to the subset of thread-safe functions which operate correctly
without serializing access to their operation by blocking concurrent
callers. For instance, lib$get_vm is re-entrant, because it uses
atomic operations to manage the look-aside lists and thus multiple
threads can allocate memory simultaneously.
Similar terminology applies to ASTs: functions can be "AST-safe" or
"AST-reentrant". The typical way to make a function AST-safe is to
disable ASTs inside the function. This works from within an AST
routine -- this call is possible even within an AST routine, though
obviously unnecessary -- and it works from the main-line code. With
ASTs disabled, the function is serialized. As with a thread-reentrant
routine, an AST-reentrant function is one that works properly when
called by both main-line and AST code -- without blocking ASTs.
Again, lib$get_vm is AST-reentrant because it uses atomic operations.
A problem can arise when a function which is not re0entrant wants to
be both thread-safe and AST-safe. It is straightfoward enough for such
a function to disable AST delivery, since that can be done for both the
main-line caller and for ASTs. However, the function cannot use an
execution-blocking serialization, such as mutex, to control access to
the core of the routine because once AST execution has begun, it cannot
be blocked without risking a deadlock. Thus, APIs are forced to choose
either thread-safety or AST-safety if they cannot be full-reentrant. If
the routine is thread-safe, then it cannot be called in an AST. A routine
that is AST-safe is generally either not thread-safe or the routine is
Simple thread-reentrancy of an API does not imply that the API is also
AST-reentrant. One of the techniques frequently employed to make a
routine thread-reentrant is to use per-thread storage to allocate what
would otherwise be global or static variables. Using this technique, no
call in one thread will conflict with a call in any other thread. However,
a call from an AST would be executing in the context of some thread; the
call would conflict with a concurrent call already in progress in the
If you must use ASTs and threads, you will want to perform the
absolute minimum processing within the AST routines, passing off
all processing to threads via application-specific work request
packets and interlocked queues or other re-entrant technique.
The OpenVMS Wizard recommends using great care -- carefully avoid the
causes of deadlocks and of data corruptions, as outlined above -- when
mixing threads and ASTs within the same application image.
Related topics include (2790), (4647), (6099), and (6984).