CPGuide - Program Execution Control
Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation
Multitasking is the ability of OS/2 to manage the execution of more than one application at a time. A multitasking operating system, such as OS/2 enables users to run many applications simultaneously.
For the programmer, OS/2 supports two types of multitasking. An application can start other programs, in separate processes, that will execute concurrently with the application. These programs can be a new copy of the application, a related program that is designed to work with the application, or an unrelated program. Running multiple processes is the first type of multitasking provided for programmers.
Running multiple threads is the second type of multitasking supported by OS/2. OS/2 enables applications to run multiple threads of execution within the same process; separate activities can be multitasked within the application. An example of multiple-thread multitasking would be for the application to dispatch a separate subroutine to load a file from the disk, and have the subroutine execute at the same time the main program continues to monitor and respond to user input.
This chapter describes processes, threads, and sessions, and the OS/2 functions used to create and manage them. Additionally, there is a section describing CPU scheduling.
The following topics are related to the information in this chapter:
- Memory
- Semaphores
- Queues
- Pipes
- Exception handling
- Debugging
About Program Execution Control-Thread, Processes, and Sessions
To successfully use multitasking-multiple processes and multiple threads-in your programs, you need to understand the difference between a thread, a process, and a session.
A thread is a dispatchable unit of execution that consists of a set of instructions, related CPU register values, and a stack. Each process has at least one thread, called the main thread or thread 1, and can have many threads running simultaneously. The application runs when OS/2 gives control to a thread in the process. The thread is the basic unit of execution scheduling.
A process is the code, data, and other resources-such as file handles, semaphores, pipes, queues, and so on-of an application in memory. OS/2 considers every application it loads to be a process. System resources are allocated on a per-process basis.
A session is one (or more) processes with their own virtual console. (A virtual console is a virtual screen-either a character-based, full screen or a Presentation Manager window-and buffers for keyboard and mouse input.)
OS/2 supports up to 255 concurrent sessions and up to 4095 processes. OS/2 supports a system-wide maximum of 4095 threads, but the number of threads available in a single process will be lower, and will vary, because of resource usage within the process.
Threads
Applications always have at least one thread of execution-thread 1. Using multiple threads of execution, an application can do several things at the same time.
For example, a simple Presentation Manager application consists of a single process with two threads:
- A user interface thread that listens and responds to user requests, and that queues work for the second thread
- A processing thread that handles lengthy processing
OS/2 creates the first thread of execution for a process when it starts the executable file. To create another thread of execution, a thread calls DosCreateThread, specifying the address within the program module where the thread begins asynchronous execution. Although a thread can execute any part of the application, including a part being executed by another thread, threads typically are used to execute separate sections of the application. By using several threads, the system can distribute the available CPU time and enable an application to carry out several tasks simultaneously. For example, an application can load a file and prompt the user for input at the same time.
Each thread in a process has a unique stack and register context. Threads shares the resources of the process with the other threads in the process. For example, threads in the same process have access to the memory spaces of other threads within the process. However, threads of one process do not have access to the data spaces of other processes.
Each thread has a priority, that determines the amount of CPU time the thread is allocated. Threads inherit the priority of the thread that creates them. The priority of a thread can be changed by the application; see Changing the Priority of a Thread for details.
An application can use DosSuspendThread and DosResumeThread to suspend and resume the execution of a given thread. When an application suspends a thread, the thread remains suspended until the application calls DosResumeThread.
When an application has more than one thread, it might be necessary to ensure that one thread is finished executing before another thread uses a shared resource, such as a disk file. DosWaitThread causes the application to wait until a specific thread has finished. DosWaitThread can also be used to determine the state of a thread; the function can return immediately with an error value if the identified thread is still running.
A thread ends when it calls DosExit.
Processes
An OS/2 application that has been loaded into memory and prepared for execution is called a process. As mentioned earlier, a process consists of the code, data, and other resources (for example, open file handles) that belong to the application. Each process has at least one thread, called the main thread or thread 1.
When OS/2 runs an application, it confirms that the process code and data are in memory and that the main thread's registers and stack are set before starting the application. Each application has access to all resources of the computer, such as memory, disk drives, screen, keyboard, and the CPU itself. The system carefully manages these resources so that applications can access them without conflict.
A process can have more than one thread. OS/2 creates the first thread of execution for a process when it starts the executable file. More threads can be created with DosCreateThread. Each thread runs independently, with its own stack and register values. Unless the application changes a thread's priority, each thread gets a slice of the CPU in a round-robin strategy. All the threads in a process share the application's globally defined variables and other resources (open file handles, and so on).
A process or thread ends when it calls DosExit. OS/2 automatically closes any files or other resources left open by the process when the process ends. When a thread ends, however, any open resources remain open until another thread closes them or the process ends. A process can direct OS/2 to carry out other actions when the process ends, by using DosExitList to create a list of termination functions. OS/2 calls the termination functions, in the order given, when the process is about to end. If the thread has registered any exception handlers, the exception handlers will also be called before the thread ends.
Creating Processes
An application can load and execute other applications by using DosExecPgm. The new application, once loaded, is called a child process. The process that starts the new application is called the parent process.
A child process is like any other process. It has its own code, data, and threads. The child process inherits the resources-such as file handles, pipes, and queues-that belong to the parent process at the time the child process is created, although not necessarily with the same access rights. The parent process can place restrictions on the access of the child process to these resources:
- Files are inherited except for files that were opened with no inheritance indicated.
- Pipes are inherited.
Assuming that the parent process gives the child process appropriate access rights, the child process can use the inherited resources without preparing them. For example, if the parent process opens a file for reading and then starts a child process, the child process can read from the file immediately; it does not have to open the file. However, once the child process is created additional resources that the parent process creates are not available to the child process. Similarly, resources the child process creates are not available to the parent process.
The parent process also has control over the meanings of standard input, output, and error for the child process. For example, the parent can write a series of records to a file, open the file as standard input, open a listing file as standard output, and then execute a sort program that takes its input from standard input and writes to standard output.
Note that memory is not included in the list of things that a child process can inherit from its parent process. The child process is created with its own process address space that is separate and distinct from the memory of the parent process. A new linear address space is built for the new process. The only way for a parent process and a child process to access the same memory is to set up a shared memory area.
The executable file of the child process can be started either synchronously or asynchronously to the parent process. If the parent process starts the child process running synchronously, the parent process is suspended and waits until the child process ends before continuing. A child process running asynchronously executes independently of the parent process (that is, both run at the same time). The parent process specifies how the child process is to run by setting a parameter in the call to DosExecPgm.
The OS/2 command processor, CMD.EXE, starts most child processes synchronously. The parent process waits for each child process to end before it prompts the user for the next command. The command processor also enables the user to start asynchronous applications by using the DETACH command. When the user detaches an application, the command processor starts the application asynchronously, in the background, and continues to prompt for input.
Process Termination
A parent process can use DosWaitChild to determine the termination status of a child process that is running independently. The parent process can have one of its threads call DosWaitChild to wait for completion of the child process while other threads of the parent continue processing.
If the child has started another process, DosWaitChild waits for the completion of any grandchild processes before returning, but does not report their status. If the specified child process has multiple threads, DosWaitChild returns the result code of the last DosExit request.
If there are no child processes, either active or ended with a return code, DosWaitChild returns with an error code. If no child processes have ended, DosWaitChild can optionally wait until one ends before returning to the parent.
Process Exit Lists
Because any process can end any other process for which it has a process identifier, applications might lose information if a process ends the application before it can save its work. To prevent this loss of data, you can create a list of functions to clean up data and files before OS/2 ends the process. This list is called an exit list. OS/2 maintains an exit list for each process and calls these functions whenever the application is ended, whether by another process or by itself.
You call DosExitList to add to the exit list a routine that is to be given control when a process is ended (or finishes its execution). Multiple routines can be added to the list. When the process is ending, OS/2 transfers control to each address on the list.
Exit-list functions perform clean-up operations on resources. For example, an exit-list function can be used in a dynamic link library module to free resources or clear flags and semaphores when a client program has ended.
Multitasking with Threads and Multitasking with Processes
The creation and termination of a process is relatively slow compared to the creation and termination of a thread, and is more costly in terms of system resources.
For example, sharing data and resources between processes requires shared memory and the mechanisms of interprocess communication; threads, on the other hand, have full access to the memory and other resources that belong to the process the threads are part of and can be coordinated using semaphores. For these reasons, thread-to-thread task context switches are faster than process-to-process context switches.
Because OS/2 can create and execute threads more quickly than processes, the preferred multitasking method for applications is to distribute tasks among threads in the same process instead of among processes.
Sessions
OS/2 uses sessions to help the user move from one application to the next without disrupting the screen display of an application.
A session consists of at least one process and a virtual console-buffers for keyboard and mouse input and either a character-based, full screen or a Presentation Manager window. When the system creates a session, the process in the session displays output in the screen or window. The user can view the output and supply input by moving to the session. The user moves to a session by pressing the Alt+Esc key combination, by selecting the title of the session from the Window List, or, for windowed sessions, by clicking the mouse in the session window.
A child session is under the control of the session that creates it. The session that starts the child session is called the parent session. Any process in the parent session can exercise control over a child session.
An unrelated session is not under the control of the session that started it. The process that creates the unrelated session cannot select it, make it nonselectable, bind it, or end it, nor can any other session. DosStartSession does not even return a session identifier when an unrelated session is started. Unrelated sessions are controlled entirely by the user. When OS/2 starts new sessions, it starts them as unrelated sessions.
Creating Sessions
A process creates a new session by using DosStartSession. DosStartSession enables an application to start another session and to specify the name of the application to be started in that session.
DosStartSession also specifies which of the five session types is to be started:
- Full screen, protect mode
- Text windowed, protect mode
- Presentation Manager (PM)
- Full screen DOS Session
- Windowed DOS Session
Protect mode applications run in full screen and text windowed sessions, PM and AVIO applications run in PM windows, and DOS applications run in full screen DOS Sessions and windowed DOS Sessions.
OS/2 applications running in any of the OS/2 session types-full screen, text windowed, and PM-can start sessions of any other type, including DOS Sessions. DOS Session applications cannot start other sessions.
An application can start another process in a separate session when the application will not manage any I/O done by the process. For example, an application that starts an unrelated application could start it in a separate session.
A session can be started as a related or an unrelated session. A related session is called a child session, and the session starting the child session is called the parent session. An application can control its child sessions by using the session identifier returned by DosStartSession with the DosSetSession, DosSelectSession, and DosStopSession. If an application starts an unrelated session, the new session cannot be controlled by the application. The Related field in the STARTDATA structure specifies whether the new session is related to the session calling DosStartSession.
After a process has started a child session, no other process in its session can start a child session until all dependent sessions started by this process have ended.
When a session is created, the title specified in the function call (or the application name if no title is specified) is added to the Window List.
DosStartSession can be used to start either a foreground or a background session, but a new session can be started in the foreground only when the caller's session, or one of the caller's descendant sessions, is currently executing in the foreground. The foreground session for windowed applications is the session of the application that owns the window focus.
Termination Queues The parent session must create a termination queue prior to specifying the queue name in a call to DosStartSession. OS/2 will continue to notify the parent session through the specified queue as long as the session calling DosStartSession remains a parent session. In other words, when all the child sessions for a particular parent session end, the termination queue is closed by OS/2. An existing queue name must be specified on the next DosStartSession call if the caller wants to continue receiving termination notification messages.
OS/2 writes a data element to the specified queue when any child session ends. The queue is posted regardless or who ends the child session (the child, the parent, or the user) and whether the termination is normal or abnormal.
A parent session calls DosReadQueue to receive notification when a child session has ended. The word that contains the request parameter, returned by DosReadQueue, will be 0. The data element has the following format:
Termination Queue Element Format
┌──────────┬──────────────────────────────────────────────────┐ │Size │Description │ ├──────────┼──────────────────────────────────────────────────┤ │WORD │Session ID of the child session that ended │ ├──────────┼──────────────────────────────────────────────────┤ │WORD │Result code │ └──────────┴──────────────────────────────────────────────────┘
The process that originally called the DosStartSession request should call DosReadQueue, with the NOWAIT parameter set to 0. This is the only process that has addressability to the notification data element. After reading and processing the data element, the caller must free the segment containing the data element by calling DosFreeMem.
When a child session ends, the result code returned in the termination queue's data element is the result code of the program specified in the DosStartSession call, providing either one of the following is true:
- The program was run directly, with no intermediate secondary command processor
- The program is run indirectly through a secondary command processor, and the /C parameter is specified
When a child session is running in the foreground at the time it ends, the parent session becomes the new foreground session. When a parent session ends, any child sessions are ended. When an unrelated session ends in the foreground, OS/2 selects the next foreground session.
Child Session Control
A session can be either a child session or an unrelated session. A child session is under the control of the processes in the session that creates it (the parent session). A process can select, set, or stop a child session by using DosSelectSession, DosSetSession, or DosStopSession, respectively. DosStartSession returns a unique session identifier for the child session for use in these functions.
A session can run in either the foreground or background. A process can create a foreground session only if the creating process or one of its descendant sessions is executing in the current foreground session. A process can move a child session to the foreground by selecting the child session using the session identifier and calling DosSelectSession. A process can make a child session nonselectable by using DosSetSession to change the SelectInd field in the STATUSDATA structure. This prevents the user from selecting the session from the Window List but does not prevent a process from selecting the child session by using DosSelectSession.
A process can bind a child session to its own session by using DosSetSession. Binding a session causes that session to move to the foreground whenever the user selects the parent session from the Window List.
A parent session can use a session identifier with the DosSetSession function only if the parent session created the child session associated with that identifier. It cannot use identifiers for child sessions created by other parent processes. This is true for all session management functions.
Although a child session is related to the session that started it, the processes in the child and original sessions are not related. This means that even though DosStartSession supplies the process identifier of the process in the child session, the process identifier cannot be used with OS/2 functions such as DosSetPriority.
Child Session Termination
A parent session can stop a child session by using DosStopSession. Stopping the child session ends the processes in that session. It also stops any sessions related to the child session. If a child session is in the foreground when it is stopped, the parent session becomes the foreground session. DosStopSession breaks any bond that exists between the parent session and the specified child session.
A process running in the session specified in the call to DosStopSession can ignore the request to end. If this happens, DosStopSession still returns 0 (indicating success). The only way to be certain that the child session has ended is to wait for notification through the termination queue specified in the call to DosStartSession. OS/2 writes a data element into the specified queue when the child session ends. The process in the parent session must call DosReadQueue to retrieve this data element, which contains the session identifier for the child session and the return value for the process in the child session. Only the process that created the child session can read the data element.