Interactions of Threads, Processes, and Sessions
Explicit thread interactions are confined to the bounds of an individual process because the model intentionally provides no means for a thread in one process to identify a particular thread in a different process; implicit interactions of threads in different processes may, however, occur as a result of dispatching decisions conditioned by dispatching class membership or the use of event management, interprocess communication, or synchronization services. Interprocess communication is provided between processes in either the same or different virtual machines, possibly even at different nodes of a communications network; other interactions between processes are confined to a single session.
The initial session (only one session is currently supported) is created implicitly during the IPL of CMS in the virtual machine. Creation of a session causes creation of an initial process known as the root process with the architected name Root which is constant and well-known in all CMS sessions. The root process performs session initialization and creates a number of threads dedicated to performing standard system functions or managing shared facilities (for example, the virtual machine timers).
Processes are created in a hierarchical structure whose depth is limited only by the virtual memory available to the session. A process may control the execution of any of its descendants by suspending or resuming it, altering its relative priority, or even terminating it. Termination of a process by default causes termination of all its descendants; termination of the root process causes termination of the session.
The creator of a process specifies the program which the process is to execute. One such program is the CMS command interpreter, which reads and executes commands from the terminal; a process running the command interpreter is referred to as a commands process. During initialization the root process creates a commands process with the architected name Commands. Conventional CMS commands and non-multitasking CMS applications are executed directly in the commands process on the command interpreter thread. Starting a multitasking application in the commands process causes the implicit creation of a child process in which the multitasking program is actually executed while the command interpreter thread waits for its completion. The name of such an implicitly created process is the name of the program whose invocation caused it to be created, suffixed with a timestamp if necessary to produce a unique name within the session. If a thread of a multitasking program subsequently invokes another multitasking program through the CMSCALL interface, another child process is implicitly created for it.
Interprocess communication in CMS is based on queues. Creating a process establishes a primary queue on which messages may be sent to the process. The primary queue bears the name of the process and has session-level scope by default. The primary queue may not be deleted except during process termination. Additional queues may be created and deleted as required. The threads in a single process can communicate and coordinate work using queues. Additionally, queues can be used to communicate with other processes in the same session or even in different virtual machines. See Interprocess Communication for more information on queue management.
Creating a process also creates an initial thread on which the specified program starts executing. Any thread may create additional threads in its process and may freely suspend, resume, terminate, or alter the priority of any other thread in the same process. All threads within a process are peers; normal termination of one thread has no effect on other threads in the process. When the last thread in a process terminates, the process itself terminates.
At creation each process is assigned a relative priority that defaults to the priority of its creator. The priority of the process determines how its threads are treated by the dispatcher relative to all other processes in the session. In addition, at creation each thread is assigned a priority relative to other threads in the same process. The priority of a thread may be altered by any thread, including itself, in the same process. The priority of a process may be altered by any ancestor of the process or by the process itself. In particular, the root process may alter the priority of any process in the session. The actual dispatching priority of a thread is determined by combining its own priority with the priority of its process in such a way that if two processes have different process priorities, all threads in the higher priority process will have higher dispatching priority than all threads in the lower priority process.
CMS uses a few basic principles to regulate the passing of control of a processor from one thread to another. First, a thread can lose control of a processor only when it calls one of a subset of the CSL routines described in this book. Such calls are easily identified: they all share the trait of having the potential to adjust the dispatchability of one or more threads in the system. QueueSend, EventSignal, and SemSignal are examples of such routines, while QueueQuery and EventMonitorCreate are not. Second, loss of control of a processor is classified as either involuntary or voluntary. Involuntary loss of control, also known as preemption, occurs even though the thread itself does nothing that would cause it to become unable to continue executing. Voluntary loss of control, which includes blocking and yielding, occurs when the thread performs some function that makes it unable or unwilling to continue to execute. An example of this is calling EventWait: the thread cannot continue executing until the wait is satisfied. A list of the functions that can cause a thread to lose control voluntarily can be found in Process Management.
CMS groups threads into dispatch classes to manage the thread switches that can occur at involuntary and voluntary losses of control. A thread can be preempted by (that is, involuntarily lose control to) only those threads residing in classes different from its own. A thread can never be preempted by a thread in its own dispatch class. When a thread blocks or yields, though, it can lose control to a thread from any dispatch class, including its own. Dispatch classes also govern parallelism. Threads in different classes can execute in parallel in a multiprocessor virtual machine, but only one thread from a given class can execute at a given time.
CMS provides facilities that let processes recognize and respond to the occurrence of named events either internal to the process or elsewhere in the session. It defines a set of system events, such as exception occurred, trace data became available, and accounting data became available, that it signals at the appropriate times. Applications may also define their own events and may signal the occurrence of any defined event. A process may establish an event handler to be driven asynchronously when the event occurs, or may create a thread to wait explicitly for the occurrence. See Event Management for a complete description of event management services.
Normal termination of a thread occurs implicitly when it returns control through the last save area on its stack; other threads in the process continue unaffected. A thread may also explicitly request termination of itself alone or of the entire process containing it; either one of these methods also constitutes a normal termination. Normal termination of a process, whether by explicit request or as a result of normal termination of the last existing thread, causes end of process event handlers to be signaled with a normal termination code; when end of process event handling is complete, any remaining active threads in the process are terminated, all resources belonging to the process are deleted, and the process name becomes undefined and available for reuse.
Abnormal termination of a thread may be requested either by the system, if a program check or other error has been detected, or by the thread itself, by explicitly signaling an error event. The rules for error handling depend upon the kinds of error handling environments which are extant at the time the exception occurs. Consider first the relatively simple case in which no CMS ABNEXITs or simulated MVS ESPIE or ESTAE exits exist in the session; in that case, error handling is localized to the process that caused the exception since the error event has process scope. If the process has previously established an error event handler, it is driven with all other threads in the process suspended; the event handler may elect either to let termination continue or to recover. If the process has established multiple error event handlers, they are driven in LIFO (last in, first out) order until one has elected to recover or all have chosen to continue termination. A parent process is also given the opportunity to perform error recovery for its child processes. If no applicable error event handler has been established in the process, or if all applicable error event handlers choose not to recover, abnormal termination of a thread induces abnormal termination of the entire process. The steps are the same as in the normal termination case except that the end of process event handlers are signaled with the abnormal termination code.
The presence of ABNEXITs, ESTAEs, or ESPIEs anywhere in the session complicates matters considerably. Because such exits have traditionally enjoyed session-wide scope, for compatibility they must be eligible to be driven on an appropriate exception occurring anywhere in the session. Programs using these facilities, however, are likely to be old nonmultitasking programs ill-equipped to recover from errors in new multithreaded programs. In a multitasking environment, therefore, these exit facilities are supported in such a manner as to maximize compatibility with the behavior of previous levels of CMS while minimizing the interference with the error event handling protocol. Multitasking programs must use error event handling for robust and predictable error recovery. See Abend Services for a complete description of abend services.