═══ 1. Memory Management ═══ This chapter describes the memory management features and functions of OS/2. The key features of OS/2 memory management are paged virtual memory and a 32-bit linear (flat) address space that is mapped through page tables to physical memory. An OS/2 application can allocate memory for its own use or to be shared with other applications. The following topics are related to the information in this chapter:  Exception handling  Program execution and control  Semaphores  Queues ═══ 1.1. About Memory Management ═══ OS/2 offers developers a 32-bit, linear (flat) memory address space. OS/2 uses a paged memory structure. OS/2 allocates, protects, and manipulates memory in terms of pages. ═══ 1.1.1. Process Address Space ═══ The OS/2 memory allocation functions return a 32-bit pointer to the allocated memory object. While a 32-bit pointer is sufficient to address the entire 4 gigabyte global address space, applications can access only the first 512MB of linear memory, called the process address space. Of this 512MB process address space, a minimum of 64MB is reserved for shared memory regions, leaving 448MB. Of this 448MB, some will be used by the application itself and a small amount will be taken by operating system overhead. The remainder is available for allocation. The amount of memory that can actually be committed and used is, of course, determined by the physical memory and hard disk space available on the machine. Keep in mind that the amount of memory that can be committed for actual use is limited by the amount of physical memory and free hard disk space that is available on the computer on which the application is executing. ═══ 1.1.2. Memory Objects ═══ Applications allocate and manipulate memory in terms of memory objects. A memory object consists of one or more pages of memory. An OS/2 application can allocate any number of memory objects, within the following limits:  the physical memory in the system  the free hard disk space on the hard disk containing the swap file  the 512MB process address space limit (see Process Address Space). When requesting memory, the size of the memory object is rounded up to the next higher multiple of 4KB. An application can suballocate a memory object into memory blocks whose size can range from 1 byte to the size of the memory object. Memory objects have the following characteristics:  They are not relocatable.  They are allocated in units of 4KB. One 4KB unit is called a page.  They can be larger than 64KB in size. ═══ 1.1.3. Memory Pages ═══ OS/2 allocates and commits memory objects in pages. A memory page is a 4KB (4096 bytes) piece of memory. Memory access protection is also done on a page-basis, rather than the segment-based protection used in previous versions of OS/2. A page range is a linearly contiguous group of pages within a memory object. A page range can be any of the following:  The entire memory object  Part of the memory object  A single page within a memory object If an application requests 512 bytes of memory, it will receive a 32-bit pointer to a 4KB page. All 4096 bytes are available to the application, even though the request specified only 512 bytes. If an application requests 62000 bytes, it will receive a pointer to a 65536-byte (64KB, 16-page) object. Again, all 65536 bytes are available for use. Each page in the virtual address space of the process is either free (unallocated), private (available only to the process that allocated it), or shared (memory that is shared between processes). Each page within a memory object can be in one of two states, either uncommitted (that is, the linear address range has been reserved, but is not yet backed by physical storage) or committed (physical storage has been allotted for the logical address range). Access to a committed page is controlled by the page's access protection attribute. These protection attributes are read access, write access, execute access (on the 80386, this is the same as read access), and guard page access. An uncommitted page is not accessible. ═══ 1.1.4. Memory Overcommitment and Swapping ═══ Memory overcommitment occurs when applications allocate and commit more memory than is actually available in the computer. OS/2 handles memory overcommitment by copying memory to the system swap file, SWAPPER.DAT, on the hard disk then reusing the memory for another allocation. OS/2 copies as many pages of memory as are necessary to make room for the new allocation. The swapped memory can be retrieved the next time it is accessed; at that time, some other memory might be written to the swap file. OS/2 selects the memory to swap based on when it was last used. The page that is least-recently-used, that is, the page that has gone the longest since its last access, is the page chosen to swap to disk. Swapping is transparent to an application, although excessive swapping can cause an application to run slowly. Through swapping, OS/2 enables applications to allocate more memory than actually exists in the computer, bounded only by the amount of free space on the hard disk that contains the swap file. ═══ 1.1.5. User Configuration of Memory Swapping ═══ Although an application cannot control swapping, the user can specify whether the system can swap memory by including the MEMMAN command in the CONFIG.SYS file. If the MEMMAN command specifies SWAP, OS/2 writes selected memory pages to the SWAPPER.DAT file whenever insufficient physical memory exists to satisfy an allocation request. This is the default choice. If the MEMMAN command specifies NOSWAP, OS/2 does not swap memory. Note: Be aware that disabling swapping will severely limit the number of applications that the user will be able to run concurrently; if there is not enough physical memory present, OS/2 might not even boot. The exact amount of memory available to an application depends on the amount of physical memory in the machine and the amount of free disk space on the partition that contains the SWAPPER.DAT file. The location of the SWAPPER.DAT file can be specified by including the SWAPPATH command in the CONFIG.SYS file. With the SWAPPATH command, one can specify the location of the SWAPPER.DAT file, the amount of free disk space to reserve in the partition which will contain SWAPPER.DAT, and the initial size of SWAPPER.DAT. OS/2 adjusts the size of SWAPPER.DAT as necessary, leaving other files on the drive and the requested free space untouched. ═══ 1.1.6. Memory Allocation and Commitment ═══ When an application asks OS/2 to allocate memory, a linear address range is reserved. The range is not backed by physical memory until the memory is committed. Commitment assigns physical memory to the linear address range. A memory object that is allocated, but not committed is called a sparse memory object. A sparse memory object must be committed before it can be used. An attempt to read from or write to uncommitted memory will cause an access violation. An application can ask OS/2 to commit the memory at the same time it is allocated, thus making it immediately usable, or the memory can be committed at a later time. If the application commits the memory at the same time the memory is allocated, the entire memory object is committed. If the application commits the memory at a later time, it can commit the entire sparse memory object or only commit a portion of it. When multiple pages are committed at the same time (a page range), the pages will have sequential linear addresses. Managing Memory Allocation and Commitment The recommended way to manage memory is to make a large memory allocation early in program execution, then to commit or suballocate memory as the need occurs. The initial allocation should be for as much space as you expect to use during program execution. Allocation without commitment does not actually use any physical memory, so there is no waste involved in allocating several megabytes. After the memory object is allocated, the application uses one of two ways to manage the memory object:  Commit and decommit the memory as it is required  Set up the memory object as a heap and suballocate memory from the heap. Committing and decommitting memory gives the application more control over the process, but the application will have to keep track of which pages are committed and which pages are not. When suballocating memory from a heap, the application can have OS/2 track commitment and decommitment of physical memory pages, so the application does not have to. If you want DosSubAllocMem to manage the commitment of the pages spanned by the heap, all of the pages spanned by the memory object must be uncommitted initially. Remember, no matter how much memory is originally allocated, the amount that an application will ultimately be able to commit and use is limited by the amount of physical memory and free disk space available on the machine. Applications are not limited to a single large allocation of memory-other memory allocations can be made as necessary during execution-but large allocations and small commitments or suballocations are the most efficient way to manage memory. ═══ 1.1.7. Memory Resizing and Reallocation ═══ In earlier versions of OS/2, an application could increase or decrease the size of an allocated memory segment by reallocating the segment. Memory objects cannot be resized. Instead, an application should allocate a sparse memory object of whatever size might be necessary, then commit or decommit portions of the object. If the amount of memory required cannot be determined at the time the memory is allocated, the application should allocate a sparse memory object large enough to meet the largest memory requirement. The application can then change the amount of committed memory as necessary. For example, if you anticipate your application will use around 512KB of memory for most purposes, but might use 5MB under certain circumstances, you might take the following steps:  During program initialization, use DosAllocMem to allocate 5MB.  Commit the first 512KB (or some part of it) using DosSetMem.  Proceed with normal processing.  If extra memory is required occasionally, commit it and decommit it using DosSetMem.  When the situation arises that the application requires the full 5MB, commit it at that time, using DosSetMem, then decommit it after you are finished with it, also using DosSetMem.  When the application is finished with the memory, use DosFreeMem to release the memory back to the system. ═══ 1.1.8. Memory Protection ═══ When an application allocates a memory object, it can specify the type of access to allow to the object. Memory access protection provides a program with control over the type of access that its threads have to a page of memory. Access protection can only be defined for committed pages of memory and is initially set at the time the memory is committed. Different pages within the same memory object can have different access attributes and access attributes can be changed on a page-by-page basis at any time. An application can request any combination of the following access protection attributes: Memory Access Protection Attributes ┌────────────────────┬────────────────────┬────────────────────┐ │Access │Defined Constant │Description │ ├────────────────────┼────────────────────┼────────────────────┤ │Read Access │PAG_READ │The object can be │ │ │ │read from, but not │ │ │ │written to. │ ├────────────────────┼────────────────────┼────────────────────┤ │Write Access │PAG_WRITE │The object can be │ │ │ │written to. On the │ │ │ │80386 │ │ │ │microprocessor, │ │ │ │write access implies│ │ │ │both read and │ │ │ │execute access. │ ├────────────────────┼────────────────────┼────────────────────┤ │Execute Access │PAG_EXECUTE │This is equivalent │ │ │ │to read access on │ │ │ │the 80386. │ ├────────────────────┼────────────────────┼────────────────────┤ │Guard Page Access │PAG_GUARD │Causes a │ │ │ │guard-page-entered │ │ │ │exception to be │ │ │ │raised in a process │ │ │ │that attempts to │ │ │ │access the memory. │ │ │ │This exception can │ │ │ │be ignored or │ │ │ │handled by the │ │ │ │application's │ │ │ │exception handler, │ │ │ │if one is │ │ │ │registered. │ └────────────────────┴────────────────────┴────────────────────┘ The guard page attribute is intended to provide automatic stack growth and stack limit checking. An application can also use it in other data structures, such as arrays. For example, if an application is using an array of 4096 bytes (one page), the application can allocate and commit two pages, one with read and write access and one designated as a guard page. If the application tries to write past the end of the array a page guard exception will be generated. Any reference-read, write, or execute-to a guard page causes an access violation (page fault) to be generated. This fault causes a Guard Page Entered exception to occur for the thread that referred to the guard page. The exception can be handled by the exception handler of the process, if one is registered. If the process does not have an exception handler registered, OS/2's default exception handler will handle the exception. The default action by the system exception handler is to convert the page from a guard page to a committed page, then try to mark the next page in memory as a guard page. If the system is not successful in marking the next page as a guard page, an Unable-To-Grow-Stack exception occurs. The thread is allowed to continue execution, but must be aware that it has at most 4KB of stack remaining. ═══ 1.1.9. Obtaining Information about a Page Range ═══ DosQueryMem is used to obtain information about a range of pages within the virtual address space of the current process. Each page in the virtual address space of a process is either free, private, or shared. Each page within a memory object can be in one of two states, either committed or uncommitted. A committed page has its access controlled by an access protection attribute. These protection attributes are read protection (PAG_READ), write protection (PAG_WRITE), execute protection (PAG_EXECUTE), and guard page protection (PAG_GUARD). ═══ 1.1.10. Protection Violations ═══ OS/2 fully utilizes the memory protection capabilities of the 80386 microprocessor. OS/2 grants an application access to a memory object only if the object has been explicitly allocated by the application or made available for use by the application. If an application attempts to access memory that it is not assigned, the system interrupts the application and executes the exception handling routine for protection violations. Protection violations can be handled by the application (in its own exception handling routines) or by OS/2. If the protection violation is handled by the operating system, the system exception handling routine determines the cause of the exception, displays an error message, and then terminates the application. It is usually not possible for an application to recover from a protection violation. Therefore, programmers should ensure that all pointers are valid. Because protection violations commonly occur during application debugging, each message displayed for a protection violation includes the contents of the registers when the violation occurred. If the violation occurred as a result of passing an invalid pointer to a memory function, an error code is returned by the memory function. In earlier versions of OS/2, protection violations could be used to find bugs when an application accessed memory that was not allocated to the application. This approach will no longer work because memory objects can be larger than the size requested by the application because the memory objects are allocated on 4KB page boundaries. For example, a pointer to the 513th byte of a 512 byte memory object is valid and does not cause a protection violation. This means that programmers cannot always rely on protection violations to spot memory addressing errors. ═══ 1.1.11. Memory Suballocation and Using Heaps ═══ There are times when a process requires only small amounts of memory rather than an entire memory object. It would be wasteful to allocate an entire page of memory when only a few bytes are necessary, so a mechanism is provided for applications to allocate a large block of memory and then suballocate portions of the memory as necessary to fulfill small requests from an application. This is done by creating a heap. A heap is a region of storage within a memory object from which an application can allocate blocks of memory. A memory block is a piece of memory within a heap. The size of the memory block is rounded up to the next higher multiple of 8 bytes. Because OS/2 allocates a 4KB page for each memory allocation, using a heap to suballocate amounts of memory smaller than 4KB is more efficient than using DosAllocMem. When an application creates a heap, it can have OS/2 track the committing and decommitting of memory within the heap. When the application commits and decommits memory itself, it has to keep track of the access state of each page as they are accessed. Applications use DosSubSetMem to initialize a memory object for suballocation, then use DosSubAllocMem and DosSubFreeMem to allocate and free the memory. Memory is still committed in pages when an application uses suballocation. If the application suballocates 512 bytes, 4096 bytes will be committed. Accessing the 513th byte will not cause a protection violation, but you could be accessing memory that was suballocated by another thread in the process. ═══ 1.1.12. Shared Memory ═══ Shared memory is memory that two or more applications can read from and write to. Shared memory is prepared in such a way that any application can receive a pointer to the memory and access the data. Applications must explicitly request access to shared memory; the shared memory is protected from applications that are not granted access. There are two kinds of shared memory: named and unnamed. For named shared memory, any application that knows the name of the shared memory can access it. For unnamed shared memory, a pointer to the shared memory must be passed from the process that created the shared memory to the process being given access. Access can be granted to any application; it is not necessary that the process being granted access be related (parent-child) to the application that created the shared memory. Since memory sharing is done by sharing linear addresses, the linear address range of the shared memory object is reserved in all process address spaces. There are two basic methods of managing shared memory:  In one method, two or more applications share the same memory at the same time. These applications read from and write to the memory object, usually controlling access to the memory by using a semaphore.  In the other method of managing shared memory, one application prepares data in memory, then passes that memory to another application for further processing. The first application releases the memory after passing it along, so that only one application accesses the memory at a time. ═══ 1.1.13. Thread Local Memory ═══ Thread local memory is a 128-byte region of thread-specific memory that OS/2 assigns to a thread when the thread is created. Thread local memory for each thread is always at the same virtual address, but each thread's local memory region is backed by different physical memory, so the memory is specific to each thread. This memory region is divided into 32 DWORDS (4 bytes per DWORD), and is normally used to store a small number of pointers. A thread can allocate thread local memory by calling DosAllocThreadLocalMemory. Thread local memory is freed by calling DosFreeThreadLocalMemory. Note that thread local memory should be used sparingly. You should not use the entire 128 byte region. ═══ 1.2. Using Memory Management ═══ This section describes how to use the OS/2 memory management functions and configuration commands to control the use of memory for OS/2 applications. Note: In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred. ═══ 1.2.1. Allocating Private Memory ═══ An application can allocate regions of storage within the virtual address space of the process. Such an allocated region is called a memory object. DosAllocMem is used to allocate a memory object. You specify a variable to receive the pointer that will address the new object, the amount of memory needed, and the allocation attributes and access protection attributes of the new memory object. When choosing the size of the memory object to allocate, remember that the maximum size of the memory object is defined when it is allocated, and memory objects cannot be resized. When applications call DosAllocMem, the operating system reserves a range of private pages large enough to fulfill the specified allocation request from the private virtual address space of the subject process. DosAllocMem will reserve this linear space and return zero if the allocation was successful. If it was unsuccessful, DosAllocMem will return an error code. An application should always test the return value before attempting to use the memory. The following code fragment requests an allocation of 512 bytes. Remember that 4096 bytes will actually be allocated for this request: #define INCL_DOSMEMMGR /* Memory Manager values */ #include PBYTE pb; APIRET ulrc; ulrc = DosAllocMem((PVOID *) &pb, 512, fALLOC); /* pb receives the base address of the */ /* 4KB memory object */ if (!ulrc) { /* If the allocation was successful, ulrc == 0 */ *pb = 3000; /* Use the allocated memory */ } In this example, DosAllocMem returns a 32-bit pointer to a 4096 byte committed memory object and allows the application to write to and read from the memory. This pointer is valid only for the 4096 bytes allocated by the system. An attempt to use the pointer outside the allocated memory will cause a protection violation. ═══ 1.2.2. Committing and Decommitting Page Ranges ═══ If an application allocates a sparse memory object, no physical memory location is committed for the object. Memory in a sparse object must be committed before it can be used. DosSetMem is used to commit or decommit a range of previously allocated pages in a private or shared memory object. Applications can make specific address ranges within a memory object valid or invalid. Commitment and decommitment always take place in multiples of one or more pages. Applications can also use DosSetMem to change the access protection attributes of a range of pages within a memory object. The following code fragment requests allocation of 2MB of uncommitted memory and then commits 4096 bytes of the memory: #define INCL_DOSMEMMGR /* Memory Manager values */ #include APIRET ulrc; PBYTE pb; /* Allocate 16KB object */ ulrc = DosAllocMem((PVOID *) &pb, 2097152, PAG_READ | PAG_WRITE); /* Commit 4KB */ ulrc = DosSetMem(pb, 4096, PAG_COMMIT | PAG_DEFAULT); An application can also allocate a large committed object and then decommit portions of it as they are no longer needed. Decommitment, like commitment, is done on page boundaries; an application can decommit no less than a 4096 byte page. The following code fragment allocates 16384 bytes of committed memory and then decommits the first 4096 bytes of the memory: #define INCL_DOSMEMMGR /* Memory Manager values */ #include APIRET ulrc; PBYTE pb; ulrc = DosAllocMem((PVOID *) &pb, 16384, fALLOC); /* Allocate 16 K object */ ulrc = DosSetMem(pb, 4096, PAG_DECOMMIT); /* Decommit 4KB */ After memory is decommitted, an attempt to access the decommitted memory will cause a protection violation. You cannot pass an argument that crosses multiple memory objects. The function will return an error. ═══ 1.2.3. Establishing Access Protection ═══ When an OS/2 application commits a memory object, it specifies the types of access permitted for the memory object. This can be done at the same time the memory object is allocated, with DosAllocMem, or at a later time, using DosSetMem. Any combination of read, write, execute, or guard-page access can be set, but at least read or write access must be specified when the memory object is committed; it is not possible to commit an object with no access protection attributes. The application can also use DosSetMem to change the access permission of pages within a previously committed memory object. An application can permit read access to one page of the memory object and write access to the rest. When using DosSetMem, all the pages in the range being changed must be either committed or decommitted. The following code fragment commits a region of two pages within a previously allocated memory object, and sets read-only access rights for the region. #define INCL_DOSMEMMGR /* Memory Manager values */ #include #include PVOID pBaseAddress; /* Pointer to the range of pages to be changed */ ULONG ulRegionSize; /* Size, in bytes, of the region to be changed */ ULONG ulAttributeFlags; /* Flag describing the page range */ APIRET ulrc; /* Return code */ ulRegionSize = 8192; /* Specify a two-page region */ /* Obtain the base address */ ulrc = DosAllocMem((PVOID *) &pBaseAddress, ulRegionSize, PAG_WRITE); ulAttributeFlags = PAG_COMMIT | PAG_READ; ulrc = DosSetMem(pBaseAddress, ulRegionSize, ulAttributeFlags); if (ulrc != 0) { printf("DosSetMem error: return code = %ld", ulrc); return; } ═══ 1.2.4. Querying Memory Object Information ═══ DosQueryMem is used to determine the allocation state and access protection for a specified memory object. The application can query an entire memory object or a range of pages within an object. The following code fragment uses DosQueryMem to ensure that memory is committed before the application attempts to use the memory: #define INCL_DOSMEMMGR /* Memory Manager values */ #include #define HF_STDOUT 1 /* Standard output handle */ PBYTE pb; /* Base address of an allocated object */ ULONG ulSize, ulFlags, ulWritten; APIRET ulrc; /* Return Code */ ulSize = 4096; ulrc = DosAllocMem((PVOID *)&pb, 16384, PAG_COMMIT | PAG_WRITE); ulrc = DosQueryMem(pb, &ulSize, &ulFlags); /* Queries first 4096 bytes */ if (ulFlags & PAG_COMMIT) { /* If memory is committed, use it */ ulrc = DosWrite(HF_STDOUT, "\r\n 4KB is committed.\r\n", 21, &ulWritten); } ═══ 1.2.5. Freeing Memory ═══ When memory object is no longer needed, the application uses the DosFreeMem function to release the memory. If applications do not release memory, the operating system either swaps the memory to the hard disk (if swapping is enabled) or uses memory that could be used by other applications. If OS/2 swaps the memory to the hard disk, the SWAPPER.DAT swap file could become very large. Therefore, applications should release memory as soon as it is no longer needed. Any memory that is still allocated when the application ends is released by OS/2. DosFreeMem frees a private or shared memory object from the virtual address space of the process. The released pages are returned to the system. The following code fragment allocates 8192 bytes of committed memory and then releases the memory: #define INCL_DOSMEMMGR /* Memory Manager values */ #include PBYTE pb; APIRET ulrc; ulrc = DosAllocMem((PVOID *) &pb, 8192, fALLOC); /* Allocate 8KB object */ . . . ulrc = DosFreeMem(pb); /* Free the object */ ═══ 1.3. Using Suballocation and Heaps ═══ This section describes how you can use DosAllocMem, DosSubSetMem, DosSubAllocMem, DosSubFreeMem, and DosSubUnsetMem to manage a memory heap. ═══ 1.3.1. Suballocating Memory ═══ DosAllocMem can be used to create a memory heap. Before an application can allocate small portions of the heap, it must use the DosSubSetMem function to set up the memory for suballocation. The size of the heap is rounded up to the next higher multiple of 8 bytes. Then, the application uses DosSubAllocMem to allocate sections of the heap and the DosSubFreeMem function to release the memory. DosSubAllocMem returns a 32-bit pointer to a block of memory. The pointer can be used to access the memory without further modification. The following code fragment sets up 8192 bytes for suballocation and then allocates two small blocks of memory: #define INCL_DOSMEMMGR /* Memory Manager values */ #include APIRET ulrc; PBYTE pbBase, pb1, pb2; ulrc = DosAllocMem((PVOID *) &pbBase, 8192, fALLOC); /* Allocate 8K object */ ulrc = DosSubSetMem(pbBase, DOSSUB_INIT, 8192); /* Set up object */ /* for suballocation */ ulrc = DosSubAllocMem(pbBase, (PVOID *) &pb1, 100); /* Suballocate 100 bytes */ ulrc = DosSubAllocMem(pbBase, (PVOID *) &pb2, 500); /* Suballocate 500 bytes */ ulrc = DosSubFreeMem(pbBase, pb1, 100); /* Free 1st suballocation*/ ulrc = DosSubAllocMem(pbBase, (PVOID *) &pb1, 50); /* Suballocate 50 bytes */ ═══ 1.3.2. Increasing the Size of a Heap ═══ DosSubSetMem can also be used to increase the size of a previously initialized heap. The heap size can be increased up to the size of the memory object that contains it. The size of the heap is rounded up to the next higher multiple of 8 bytes. The following code fragment increases the size of a heap. Assume that the Offset variable was previously loaded with the virtual address of the memory object. #define INCL_DOSMEMMGR /* Memory Manager values */ #include #include PVOID pOffset; /* Address of the heap to be used for suballocation */ ULONG ulFlags; /* Flags describing the memory object being resized */ ULONG ulSize; /* Size in bytes to increase the size of the heap */ APIRET ulrc; /* Return code */ ulSize = 20000; /* Indicate a heap size increase of 20000 bytes */ /* DOSSUB_INIT set to initialize memory for suballocation */ ulFlags = DOSSUB_INIT | DOSSUB_SPARSE_OBJ; ulrc = DosSubSetMem(pOffset, ulFlags, ulSize); if (ulrc != 0) { printf("DosSubSetMem error: return code = %ld", ulrc); return; } In this example, the heap is incremented, and that memory commitment is managed internally within subsequent DosSubAllocMem calls. When using DosSubSetMem to increase the size of the heap, the Flags parameter must have the same setting as when the heap was initialized. Note: Do not call DosSetMem to change the allocation attribute or access protection attributes of any pages spanned by a memory object that the suballocation functions are managing. Otherwise, unpredictable results could occur. Call DosSubUnsetMem when finished with the heap that was set up with DosSubSetMem. This enables the suballocation function to free the resources that it uses to manage the heap. When you are through with the memory object that the heap was part of, use DosFreeMem to free the memory object. ═══ 1.3.3. Allocating a Block of Memory from a Heap ═══ DosSubAllocMem allocates a block of memory from a heap that was previously initialized by DosSubSetMem. This is used when an application needs an area of memory that is smaller than an entire heap. The size of the memory block is rounded up to the next higher multiple of 8 bytes. The following code fragment allocates a block of memory from a heap that was previously initialized by DosSubSetMem. Assume that the Offset variable has been set to the address of the initialized heap already. #define INCL_DOSMEMMGR /* Memory Manager values */ #include #include PVOID pOffset; /* The heap to suballocate from */ PPVOID ppBlockOffset; /* Pointer to the variable where the offset of */ /* the suballocated memory block is returned */ ULONG ulSize; /* Size in bytes of the memory block requested */ APIRET ulrc; /* Return code */ ulSize = 102; /* Ask for 102 bytes. This will be rounded */ /* to 104 bytes (a multiple of 8 bytes). */ ulrc = DosSubAllocMem(pOffset, &ppBlockOffset, ulSize); if (ulrc != 0) { printf("DosSubAllocMem error: return code = %ld", ulrc); return; } In this example, the address of the allocated block (from the heap) is stored in the BlockOffset variable. Remember to call DosSubFreeMem to free this block of memory when you are finished with it. ═══ 1.3.4. Freeing Memory Blocks ═══ DosSubFreeMem frees a block of memory that was previously allocated by DosSubAllocMem. Call DosSubFreeMem to free a block of memory in the heap when you are finished with that memory block. The following code fragment frees a block of memory that was previously allocated from a heap. DosSubFreeMem returns the block to the heap. Assume that the Offset variable has been previously set to the address of the initialized heap, and that the BlockOffset variable has been previously set to the address of the block to be returned to the heap. #define INCL_DOSMEMMGR /* Memory Manager values */ #include #include PVOID pOffset; /* Offset of the heap to which the */ /* block is to be freed */ PVOID pBlockOffset; /* Offset of memory block to be freed */ ULONG ulSize; /* Size in bytes of block to be freed */ APIRET ulrc; /* Return code */ ulSize = 102; /* Return 102 bytes. This will be rounded */ /* to 104 bytes (a multiple of 8 bytes). */ ulrc = DosSubFreeMem(pOffset, pBlockOffset, ulSize); if (ulrc != 0) { printf("DosSubFreeMem error: return code = %ld", ulrc); return; } ═══ 1.3.5. Ending the Use of the Heap ═══ DosSubUnsetMem terminates the use of a heap within a memory object. All calls to DosSubSetMem must eventually be followed by a call to DosSubUnsetMem. This enables the suballocation function to free the resources that it uses to manage the heap. The application must call DosSubUnsetMem before it frees the memory object that contains this heap (with DosFreeMem). The following code fragment shows the termination of a heap. Assume that the address of the heap was placed into Offset already. #define INCL_DOSMEMMGR /* Memory Manager values */ #include #include PVOID pOffset; /* Offset of the heap whose use is being terminated */ APIRET ulrc; /* Return code */ ulrc = DosSubUnsetMem(pOffset); if (ulrc != 0) { printf("DosSubUnsetMem error: return code = %ld", ulrc); return; } ═══ 1.3.6. Allocating and Freeing Thread Local Memory ═══ Thread local memory is a small region of thread-specific memory assigned to a thread when it is started. A thread can allocate thread local memory by calling DosAllocThreadLocalMemory. Thread local memory is freed by calling DosFreeThreadLocalMemory. The thread local memory region is 32 DWORDS (4 bytes per DWORD) in size, and up to 8 DWORDS can be allocated with each call to DosAllocThreadLocalMemory. To allocate more than 8 DWORDS, a thread must call DosAllocThreadLocalMemory more than once. Note that thread local memory should be used sparingly. You should not use the entire 128 byte region. The following code fragment allocates six DWORDS in thread local memory and then frees these six DWORDS. #define INCL_DOSPROCESS #define INCL_DOSERRORS #include #include /* Needed for printf */ PULONG pulMemBlock = NULL; /* Pointer to thread DWORDs returned */ APIRET ulrc = NO_ERROR; /* Return code */ ulrc = DosAllocThreadLocalMemory(6, &pulMemBlock ); /* Allocate 6 DWORDs */ if (ulrc != NO_ERROR) { printf("DosAllocThreadLocalMemory error: return code = %u\n", ulrc); return 1; } . . /* ... Use the thread-local memory block ... */ . . ulrc = DosFreeThreadLocalMemory(pulMemBlock); /* Free the memory block */ if (ulrc != NO_ERROR) { printf("DosFreeThreadLocalMemory error: return code = %u\n", ulrc); return 1; } ═══ 1.4. Using Shared Memory ═══ This section describes how you can use DosAllocSharedMem, DosGiveSharedMem, DosGetSharedMem, and DosGetNamedSharedMem to use shared memory. There are three types of shared memory:  Named, where a process allocates and names shared memory. A second process can use the shared memory by using DosGetNamedSharedMem, specifying the name of the shared memory.  Unnamed giveable, where a process gives another process access to shared memory with DosGiveSharedMem, using the PID of the second process.  Unnamed gettable, where a process knows the address of shared memory they want to use (allocated by another process), gets permission to use that memory with DosGetSharedMem, and then uses the shared memory. The difference among these different types of shared memory is not in how the memory is allocated by the original process, but in how the second process gets permission to use and access to the shared memory. All three types of shared memory are allocated with the same call: DosAllocSharedMem, using various parameters. ═══ 1.4.1. Using Named Shared Memory ═══ An application uses DosAllocSharedMem to allocate shared memory. When allocating the shared memory, an application can assign a unique name to the memory. Any application that has the name of the shared memory can use DosGetNamedSharedMem to retrieve a pointer to the memory. This makes it possible for two or more applications to share memory at the same time. The name of a shared memory object has the following form: \sharemem\name The "\sharemem\" is required. The "name" parameter can be any name that conforms to the rules for an OS/2 file name. No file is actually created for the memory object. There is no actual "\sharemem\" subdirectory. The following code fragment allocates 65536 bytes of named shared memory with the name "\sharemem\mymem". #define INCL_DOSMEMMGR /* Memory Manager values */ #include APIRET ulrc; CHAR szMem[] = { "\\sharemem\\mymem" }; PULONG pulPb; ulrc = DosAllocSharedMem((PVOID *) &pulPb, szMem, 65536, fALLOC); *pulPb = 2762; Once the named memory is allocated, any other process can retrieve a pointer to the named memory by using DosGetNamedSharedMem. The following code fragment retrieves a pointer to the named memory allocated above: #define INCL_DOSMEMMGR /* Memory Manager values */ #include #define HF_STDOUT 1 /* Standard output handle */ APIRET ulrc; CHAR szMem[] = { "\\sharemem\\mymem" }; PULONG pulPb2; ULONG ulWritten; ulrc = DosGetNamedSharedMem((PVOID *) &pulPb2, szMem, PAG_READ | PAG_WRITE); if (*pulPb2 == 2762) ulrc = DosWrite(HF_STDOUT, "\r\n Success!\r\n", 13, &ulWritten); ═══ 1.4.2. Using Unnamed Giveable Shared Memory ═══ A process allocates unnamed giveable shared memory by using DosAllocSharedMem with the object name (pszName parameter) set to NULL and the memory options (flag parameter) set to OBJ_GIVEABLE. The process allocating the memory must pass a pointer to the shared memory to another process. This is typically done by using some form of interprocess communication, such as a queue or a named pipe. If a process allocates shared memory with the OBJ_GIVEABLE option, this process can validate the pointer in another process with DosGiveSharedMem. The following code fragment allocates 24576 bytes (24KB) of unnamed giveable shared memory: #define INCL_DOSMEMMGR /* Memory Manager values */ #include APIRET ulrc; PBYTE pb; ulrc = DosAllocSharedMem((PVOID *) &pb, (PSZ) NULL, 24576, /* amount of memory */ fALLOC | OBJ_GIVEABLE); /* giveable memory */ Once the memory is allocated, the allocating process can pass the memory pointer to a second process through interprocess communication. The second process need not use DosGetSharedMem; the second process can just use the shared memory. ═══ 1.4.3. Using Unnamed Gettable Shared Memory ═══ A process allocates unnamed gettable shared memory by using DosAllocSharedMem with the object name (pszName parameter) set to NULL and the memory options (flag parameter) set to OBJ_GETTABLE. The allocating process must pass a pointer to the shared memory to another process. This is typically done by using some form of interprocess communication, such as a queue or a named pipe. If an application allocates shared memory with the OBJ_GETTABLE option, it passes a pointer to the shared memory to the second process. The second process gets access to the shared memory by using DosGetSharedMem to validate the passed pointer. The following code fragment allocates 24576 bytes (24KB) of unnamed gettable shared memory: #define INCL_DOSMEMMGR /* Memory Manager values */ #include APIRET ulrc; PBYTE pb; ulrc = DosAllocSharedMem((PVOID *) &pb, (PSZ) NULL, 24576, fALLOC | OBJ_GETTABLE); /* gettable memory */ Once the memory is allocated, the process can pass the memory pointer to a second process through interprocess communication. Once the second process receives the pointer, it validates the memory with DosGetSharedMem, as shown in the following code: #define INCL_DOSMEMMGR /* Memory Manager values */ #include APIRET ulrc; PBYTE pb2; ulrc = DosGetSharedMem(pb2, /* validating the memory allocated by */ /* the allocating process */ PAG_READ | PAG_WRITE); ═══ 2. Message Management ═══ This chapter describes the use of message files to hold an application's text messages to the user. Keeping messages in a message file simplifies changing those messages, for example when maintaining versions of the same application for different countries. The following topic is related to the information in this chapter:  National language support ═══ 2.1. About Message Management ═══ For full-screen applications, text messages-used by an application to display information to the user-can be held in a message file. To keep and maintain messages in a message file, create a separate message file for each national language you intend to support. Use the MKMSGF utility program to create a binary version of a message file, and the MSGBIND utility program to bind the binary file to the application's executable file. Binding the messages to an application's executable file inserts the messages into the .EXE file. This enables faster access (but, of course, a larger .EXE file). OS/2 provides several functions to assist in message management. Applications can get messages from a message file, substitute variable information into the messages, and write messages to the screen or to a file. Code Page Considerations You can have versions of the message file for each code page you choose to support. When you use MKMSGF to create the message files, the utility will insert the code page information and link together the message files of the different versions. When message files are linked together in this manner, DosGetMessage will search for the appropriate version of the message for the code page that is active at the time the function is called. If there is no version of the message for the current code page, the function will return the first version of the message, no matter which code page it is associated with. Note: To create portable source code for message files, make sure you specifically set the code page in CONFIG.SYS using the CODEPAGE statement. If you have the wrong code page, or use the default code page, which could be wrong, DosGetMessage returns error code 37 (ERROR_CODE_PAGE_MISMATCHED). ═══ 2.2. Using Message Management ═══ DosGetMessage retrieves a message from the specified system message file, and inserts variable information into the body of the message. DosInsertMessage inserts variable text-string information into the body of the message, but does not retrieve the message. DosPutMessage sends the message in a buffer, usually to a display screen. DosPutMessage formats the screen buffer to prevent words from being split at the end of a line. DosQueryMessageCP retrieves the message file list of code pages and language identifiers. Note: In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred. ═══ 2.2.1. Message Retrieval and String Substitution ═══ DosGetMessage retrieves a message from a system message file, and inserts variable text-string information into the message. In the following code fragment, the message file is "D:\MESSAGE\AUTOMSG.MSG". The third message within the message file contains the string "%1 Error at Station %2". The application calls DosGetMessage to convert this message into the string "Automation Failure Error at Station 69B". #define INCL_DOSMISC /* Miscellaneous values */ #include #include UCHAR *ucIvTable[2]; /* Table of variables to insert */ ULONG ulIvCount; /* Number of variables */ UCHAR ucDataArea[80]; /* Message buffer (returned) */ ULONG ulDataLength; /* Length of buffer */ ULONG ulMsgNumber; /* Number of the message */ UCHAR ucFileName[40]; /* Message file path-name string */ ULONG ulMsgLength; /* Length of message (returned) */ UCHAR ucField1[20]; /* String to substitute into variable */ /* field %1 of the message */ UCHAR ucField2[20]; /* String to substitute into variable */ /* field %2 of the message */ APIRET ulrc; /* Return code */ strcpy(ucField1, "Automation Failure"); /* Define the field with which to */ /* perform the first substitution */ strcpy(ucField2, "69B"); /* Define the field with which to */ /* perform the second substitution */ ucIvTable[0] = ucField1; /* Set up the array of pointers to */ ucIvTable[1] = ucField2; /* substitute strings */ ulIvCount = 2; /* Two variable message fields in message */ ulDataLength = 80; /* Data buffer that will receive the */ /* complete message is 80 bytes in size */ ulMsgNumber = 3; /* Specify the third message in the */ /* message file */ strcpy(ucFileName, "D:\\MESSAGE\\AUTOMSG.MSG"); /* Path name of the message file */ ulrc = DosGetMessage(ucIvTable, ulIvCount, ucDataArea, ulDataLength, ulMsgNumber, ucFileName, &ulMsgLength); if (ulrc != 0) { printf("DosGetMessage error: return code = %ld", ulrc); return; } On successful return, the DataArea buffer contains the complete message (with the two variable fields appropriately updated), and the MsgLength variable contains the length of the message that was placed into the DataArea buffer. If an error occurs (that is, if the return code does not equal 0), a message that is related to the error will be placed in the message buffer. See the DosGetMessage API reference in Control Program Programming Reference for a description of the default messages that can be placed into the user's buffer if an error occurs during the processing of these requests. ═══ 2.2.2. Text String Substitution in Memory ═══ DosInsertMessage inserts variable text-string information into a message that resides within program memory. In the following code fragment, the message resides in a program string variable named Message. The message is the string "%1 Error at Station %2". The application calls DosInsertMessage to convert this message into the string "Automation Failure Error at Station 69B". #define INCL_DOSMISC /* Miscellaneous values */ #include #include UCHAR *ucIvTable[2]; /* Table of variables to insert */ ULONG ulIvCount; /* Number of variables */ UCHAR ucMsgInput[40] = "%1 Error at Station %2"; /* The input message */ ULONG ulMsgInLength; /* Length of input message */ UCHAR ucDataArea[80]; /* Message buffer (returned) */ ULONG ulDataLength; /* Length of updated message buffer */ ULONG ulMsgLength; /* Length of updated message (returned) */ UCHAR ucField1[20]; /* String to substitute into variable */ /* field %1 of the message */ UCHAR ucField2[20]; /* String to substitute into variable */ /* field %2 of the message */ APIRET ulrc; /* Return code */ strcpy(ucField1, "Automation Failure"); /* Define the field with which to */ /* perform the first substitution */ strcpy(ucField2, "69B"); /* Define the field with which to */ /* perform the second substitution */ ucIvTable[0] = ucField1; /* Set up the array of pointers to */ ucIvTable[1] = ucField2; /* substitute strings */ ulIvCount = 2; /* Two variable message fields in */ /* message */ ulMsgInLength = strlen(ucMsgInput); /* Length of input message */ ulDataLength = 80; /* Data buffer that will receive */ /* the complete message is 80 */ /* bytes in size */ ulrc = DosInsertMessage(ucIvTable, ulIvCount, ucMsgInput, ulMsgInLength, ucDataArea, ulDataLength, &ulMsgLength); if (ulrc != 0) { printf("DosInsertMessage error: return code = %ld", ulrc); return; } On successful return, the DataArea buffer contains the complete message (with the two variable fields appropriately updated), and the MsgLength variable contains the length of the message that was placed into the DataArea buffer. If an error occurs (that is, if the return code does not equal 0), a message that is related to the error will be placed in the message buffer. See the DosGetMessage API reference in Control Program Programming Reference for a description of the default messages that can be placed into the user's buffer if an error occurs during the processing of these requests. ═══ 2.2.3. Writing Messages ═══ DosPutMessage is used to write the message to the screen or to a file on a disk. The following code fragment writes the message string contained in MessageBuffer to the file specified by FileHandle. The message string has already been placed in MessageBuffer using either DosGetMessage or DosInsertMessage. MsgLength was set to the length of the message string by the same call that put the message into MessageBuffer. #define INCL_DOSMISC /* Miscellaneous values */ #include #include HFILE hfFileHandle; /* Handle of output file or device */ ULONG ulMsgLength; /* Length of message buffer */ UCHAR ucMessageBuffer[80]; /* Message buffer */ APIRET ulrc; /* Return code */ ulrc = DosPutMessage(hfFileHandle, ulMsgLength, ucMessageBuffer); if (ulrc != 0) { printf("DosPutMessage error: return code = %ld", ulrc); return; } To write a message to the screen, use the standard output file handle. ═══ 2.2.4. Code Page Information Associated with Message Files ═══ DosQueryMessageCP obtains a list of code page identifiers and language identifiers that are associated with a specified message file. In the following code fragment code page and language identifiers associated with the message file "D:\MESSAGE\AUTOMSG.MSG" are retrieved. #define INCL_DOSMISC /* Miscellaneous values */ #include #include UCHAR ucBufferArea[20]; /* Buffer for the returned list */ ULONG ulBufferLength; /* Length of the buffer area */ UCHAR ucFileName[40]; /* Message file path-name string */ ULONG ulDataLength; /* Length of the returned data */ APIRET ulrc; /* Return code */ strcpy(ucFileName, "D:\\MESSAGE\\AUTOMSG.MSG"); /* Path name of message file */ ulBufferLength = 20; /* Length of buffer area */ ulrc = DosQueryMessageCp(ucBufferArea, ulBufferLength, ucFileName, &ulDataLength); if (ulrc != 0) { printf("DosQueryMessageCp error: return code = %ld", ulrc); return; } On successful return, the BufferArea buffer contains a set of information concerning the code page identifiers and language identifiers that are associated with the message file. ═══ 3. National Language Support ═══ Many applications need to be independent of a particular language. Rather than being hard-coded in English, they want to support, for example, an English version of the application, and a French version, and a German version; preferably without having to change the program code for each version. Meeting this requirement is simplified through the use of such resources as string tables, menu templates, dialog templates, accelerator tables, and through the use of code pages. This chapter describes the functions an application uses to be NLS enabled, or language independent. The following topic is related to the information in this chapter:  Message management ═══ 3.1. About National Language Support ═══ The support of national languages by applications requires the following considerations:  Displayed text must be translated into the appropriate language.  Symbols or icons might not convey the same meaning in all countries. Alternative designs for different countries might be necessary.  Translation changes the length of text strings.  Different languages often have different text characters. The use of national language resource files can help with the first three items, and the ability of the application to receive input and display output in any ASCII code page can help with the last item. The use of ASCII code page 850 avoids many of the problems in this area, since it contains most of the characters required for Latin-1 languages, which include much of Western Europe and North and South America. However, older programs use code page 437 for U.S. English, and code pages 860, 863, and 865 for various languages. The code page applies to both input and output data. Note: Code page 850 was used for translating Presentation Manager text. Use code page 850 whenever possible for all Presentation Manager applications that might require translation. ═══ 3.1.1. National Language Resource Files ═══ When creating an application, define national language dependencies in resources that are held in resource files separate from the program code. That is:  Keep pure text strings in string tables.  Keep menus in menu templates.  Keep dialog boxes in dialog templates.  Keep accelerators in accelerator tables. The language displayed by the application can then be changed by translating the resources, in most cases without changing the application. However, when translating from one language to another, the length of a text string can change substantially. For example, when translating from English to German, the length of a text string can double in length. The following table furnishes a general idea of the amount of expansion that can be expected during translation. Translation Expansion ┌──────────────────────────────┬──────────────────────────────┐ │For English Phrases │Translation Expansion Factors │ ├──────────────────────────────┼──────────────────────────────┤ │Up to 10 characters │101 - 200% │ ├──────────────────────────────┼──────────────────────────────┤ │11 - 20 characters │81 - 100% │ ├──────────────────────────────┼──────────────────────────────┤ │21 - 30 characters │61 - 80% │ ├──────────────────────────────┼──────────────────────────────┤ │31 - 50 characters │41 - 60% │ ├──────────────────────────────┼──────────────────────────────┤ │51 - 70 characters │31 - 40% │ ├──────────────────────────────┼──────────────────────────────┤ │Over 70 characters │30% │ └──────────────────────────────┴──────────────────────────────┘ When designing your dialog boxes and text string messages, add white space to allow for the expansion that will occur when the text is translated. You might have to adapt the application program to allow for the change in the length of text strings after they are translated. For example, a change in the length of a text string can cause it to become misaligned with other displayed objects. You can also use the Dialog Box Editor to adjust for misalignments, or to change the size of the dialog box. This would enable you to leave your application program unchanged. Text strings explicitly displayed by the application program are more of a problem. You will have to include program code that can handle text strings of varying length and format them at runtime according to their size. ═══ 3.1.2. Language-Specific Versions of NLS-Enabled Applications ═══ There are two methods of creating a specific national language version of a program designed to handle more than one national language. The choice of the method depends on the amount of available disk space and whether the user wants to change between different languages once the program is installed. The two methods are:  Statically link the resources to the application's .EXE files. The executable files are then language-specific and cannot be changed to another national language. The specific .EXE files are then sent to the user.  Place the resources into a language-specific, dynamic link library. Designate one library file for each national language. Selecting a particular library file for use with the application gives the desired version of the program. Using this method, all national languages can be shipped with the product; selection of the national language occurs during installation (for example, by naming a specific .DLL file). It is possible to change the national language setting while the program is operating. ═══ 3.2. About Code Page Management ═══ A code page is a table that defines how the characters in a language or group of languages are encoded. A specific value is given to each character in the code page. For example, in code page 850 the letter "д" (lowercase) is encoded as hex A4 (decimal 164), and the letter "е" (uppercase) is encoded as hex A5 (decimal 165). Code page management enables a user to select a code page for keyboard input, and screen and printer output before starting an application, a system command, or a utility program in the OS/2 multitasking environment. This means that a user in a particular country, such as England (code page 850), Norway (code page 865), or a language region such as Canadian French (code page 863) can use a code page that defines an ASCII-based character set containing characters used by that particular country or language. Installable code page files include keyboard translate tables, display character sets, printer character sets, and country/language information for each code page supported. Of particular interest are two code pages:  Code Page 850  Code Page 437 Code Page 850 (CP850) Code Page 850 is also called the Latin-1, multilingual code page. This code page supports the alphabetic characters of the Latin-1-based languages. It contains characters required by 13 languages used in approximately 40 countries. CP850 also provides the flexibility to develop new applications based on non-Latin-based or special industry-based code pages. Code Page 850 supports countries using the following languages: ┌──────────────────────────────┬──────────────────────────────┐ │Belgian French │Canadian French │ ├──────────────────────────────┼──────────────────────────────┤ │Danish │Dutch │ ├──────────────────────────────┼──────────────────────────────┤ │Finnish │Flemish │ ├──────────────────────────────┼──────────────────────────────┤ │French │German │ ├──────────────────────────────┼──────────────────────────────┤ │Italian │Norwegian │ ├──────────────────────────────┼──────────────────────────────┤ │Portuguese │Spanish │ ├──────────────────────────────┼──────────────────────────────┤ │LAD Spanish │Swedish │ ├──────────────────────────────┼──────────────────────────────┤ │Swiss French │Swiss German │ ├──────────────────────────────┼──────────────────────────────┤ │U.K. English │U.S. English │ └──────────────────────────────┴──────────────────────────────┘ Code Page 437 (CP437) Code Page 437 is the standard personal computer code page. The lower 128 characters are based on the 7-bit ASCII code. The upper 128 characters contain characters from several European languages (including part of the Greek alphabet) and various graphic characters. However, some of the accented characters, such as those used in the Nordic countries, are not represented. The missing characters are available in other code pages (code page 850 will usually contain the desired characters). Note: Some of the 256 symbols that can be displayed are printer control characters, and are not printed. ═══ 3.2.1. ASCII and EBCDIC Code Page Support ═══ The two leading character-coding systems are ASCII and EBCDIC. Presentation Manager applications can use an EBCDIC code page instead of an ASCII code page. Code pages based on both systems are supported by OS/2. Any code page that either is defined in the CONFIG.SYS file, or is one of the EBCDIC code pages supported, can be selected. ═══ 3.2.2. Code Page Preparation ═══ During system initialization, the code pages specified in the CODEPAGE statement are prepared to enable run-time code page switching of the display, the keyboard, the printer, and the country information. The display, keyboard, and printer must be defined in a DEVINFO statement in order to be prepared. Country information is prepared for the system country code specified in the COUNTRY statement. If a resource cannot be prepared for the selected code page during system initialization, it is prepared for a default code page. The following are the defaults:  A keyboard layout defaults to the code page of the translate table designated as the default layout in the KEYBOARD.DCP file. The default layout is based on the national code page of its associated country. You must explicitly specify KEYBOARD.DCP in the DEVINFO statement for the keyboard in CONFIG.SYS.  The display defaults to the code page of ROM_0 for the device. (ROM_0 means a device default code page that is the device native code page or the lowest addressed ROM code page.)  The printer defaults to the code page of ROM_0 for the device. (ROM_0 means a device default code page that is the device native code page or the lowest addressed ROM code page.)  The country information defaults to the code page of the first entry found in the COUNTRY.SYS file for the country code. Each entry is the same information for a given country code, but is encoded in a different code page. The first entry is based on the preferred country code page. If country information cannot be prepared at system initialization because it is not found in the COUNTRY.SYS file, for a code page selected with the CODEPAGE statement, then it is prepared (maintained for run-time code page switching in memory) in the default code page. Similarly, a keyboard layout is prepared in its default code page if it cannot be prepared in the selected code page, because it is not found in the KEYBOARD.DCP file. COUNTRY.SYS contains one default entry per country code, and KEYBOARD.DCP contains one default entry per keyboard layout based on these assignments. ═══ 3.2.3. Code Page Functions ═══ At the system level, OS/2 switches the code pages of supported displays and printers to agree with the code page of the process sending the output. At the application level, OS/2 functions enable a process to control code page assignments. ═══ 3.3. Using Code Pages ═══ OS/2 provides applications with several functions to obtain information about and manipulate code pages. These functions enable applications to determine and set the current code page. OS/2 code page management functions enable applications to read keyboard input and write display and printer output for multiple processes using ASCII-based data encoded in different code pages. The system switches to the required code page, for a code-page-supported device, before input or output. Note: In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred. ═══ 3.3.1. Querying Code Page Support and the Current Code Page ═══ DosQueryCp is used to determine the code page of the current process and the prepared system code pages. The following code fragment shows how to get the current code page, and then up to three other prepared pages: #define INCL_DOSNLS /* National Language Support values */ #include ULONG ulCpList[8]; ULONG ulCpSize; APIRET ulrc; /* Return code */ ulrc = DosQueryCp(sizeof(ulCpList), /* Length of list */ ulCpList, /* List */ &ulCpSize); /* Length of returned list */ The required code page is the current code page of the process at the time it opens a device, or a specific code page selected by the process with a set-code-page function. A character set can also be specified for some devices, for example, for some printers. The country functions retrieve country- and language-dependent information in the current code page of the calling process, or in a code page selected by the process. ═══ 3.3.2. Setting the Code Page for Text Characters ═══ Each process has a code page tag maintained by OS/2. A code page tag is the identifier of the current code page for the process. A child process inherits the code page tag of its parent. The default code page for the first process in a session is the same as the session code page. The default code page for a new session is the primary code page specified in the CODEPAGE configuration statement. To change the code page tag of a process, call DosSetProcessCp. This will not change the process code page tag of its parent or any child process. ═══ 3.3.3. Obtaining the Case Map String ═══ DosMapCase performs case mapping on a string of binary values that represent ASCII characters. The case map that is used is the one in the country file that corresponds to the system country code or selected country code, and to the process code page or selected code page. The default name of the country file is COUNTRY.SYS. ═══ 3.3.4. Obtaining the DBCS Environment Vector ═══ DosQueryDBCSEnv obtains a double-byte character set (DBCS) environment vector that resides in the country file. The default name of the country file is COUNTRY.SYS. The vector corresponds to the system country code or selected country code, and to the process code page or selected code page. The following code fragment shows how to use DosQueryDBCSEnv: #define INCL_DOSNLS /* National Language Support values */ #include #include ULONG ulLength; /* Length of data area provided */ COUNTRYCODE ccStructure; /* Input data structure */ UCHAR ucMemoryBuffer[12]; /* DBCS environmental vector (returned) */ APIRET ulrc; /* Return code */ ulLength = 12; /* A length of 12 bytes is sufficient */ /* to contain the DBCS data returned */ ccStructure.country = 0; /* Use the default system country code */ ccStructure.codepage = 0; /* Return DBSC information for the */ /* caller's current process code page */ ulrc = DosQueryDBCSEnv(ulLength, &ccStructure, ucMemoryBuffer); if (ulrc != 0) { printf("DosQueryDBCSEnv error: return code = %ld", ulrc); return; } On successful return, the buffer MemoryBuffer will contain the country dependent information for the DBCS environmental vector. Instead of the single-byte character set (SBCS) representation used for Latin text, some Asian countries use code pages that consist of double-byte character set characters, in which each character is represented by a two-byte code. The DBCS code pages enable single-byte data, double-byte data, or mixed (single-byte and double-byte) data. ═══ 3.3.5. Obtaining Formatting Information ═══ DosQueryCtryInfo obtains country dependent formatting information that resides in the country file. The default name of the country file is COUNTRY.SYS. The information corresponds to the system country code or selected country code, and to the process code page or selected code page. ═══ 3.3.6. Obtaining Collating Information for SORT ═══ DosQueryCollate obtains a collating sequence table (for characters 00H through FFH) from the country file. The default name of the country file is COUNTRY.SYS. The SORT utility program uses this table to sort text according to the collating sequence. The collating table returned corresponds to the system country code or selected country code, and to the process code page or selected code page. ═══ 4. Pipes ═══ Communication between processes is valuable in a multitasking operating system to enable concurrent processes to work together. Pipes are one of three forms of interprocess communication (IPC), the other forms of IPC being semaphores and queues. This chapter describes how to create, manage, and use pipes. Pipes enable two or more processes to communicate as if they were reading from and writing to a file. The following topics are related to the information in this chapter:  Memory (shared memory)  Program execution and control  Semaphores  Queues ═══ 4.1. About Pipes ═══ A pipe is a named or unnamed buffer used to pass data between processes. A process writes to or reads from a pipe as if the pipe were standard input or standard output. A parent process can use pipes to control the input that a child process receives and to receive the output that the child process produces. There are two types of pipes-named and unnamed. ═══ 4.1.1. Unnamed Pipes ═══ An unnamed pipe is a circular buffer in memory. The buffer has in and out pointers that are maintained by the system. An unnamed pipe can transfer information only between related processes. A child process started by a parent process with DosExecPgm inherits the handles to any unnamed pipes created by its parent. This inheritance enables the parent process and the child process to use the unnamed pipe to communicate with one another. This type of pipe is typically used to redirect the standard input and standard output of a child process. To do this, a process opens a pipe and duplicates the read and write handles of the pipe as the standard input and standard output files for the child process. Once the handles are duplicated, the parent process can use DosExecPgm to start the child process. When the child process reads and writes to its standard input and standard output handles, it is reading and writing to the pipe. The parent process can also communicate with the child process through the pipe. Using an unnamed pipe, a text editor could run another program, such as a compiler or assembler, and display the output of the compiler or assembler within the editor. DosCreatePipe creates an unnamed pipe. This function returns two file handles for the pipe, one for writing to the pipe and another for reading from the pipe. A process can then write to the pipe by using DosWrite and read from the pipe by using DosRead. A pipe exists until both handles are closed. The order in which the handles are closed is sometimes important. For example, DosWrite might wait for data to be read from the pipe before completing its operation. In this case, the read handle is closed before the write handle is closed, writing to the pipe generates an error. No control or permission mechanisms or checks are performed on operations to unnamed pipes. ═══ 4.1.2. Named Pipes ═══ Named pipes enable related or unrelated processes on either the same computer system or different systems to communicate with each other. Any process that knows the name of a pipe can open and use a named pipe. In addition, named pipe data can be transparently redirected across a network, such as a local area network (LAN). (Unnamed pipes, by contrast, can be used only by related processes that are on the same computer system.) One process (the server process) creates the pipe and connects to one end of it. Other processes that access the named pipe are called client processes; they connect to the other end of the pipe. The server and client processes can then pass data back and forth by reading from and writing to the pipe. The server process controls access to the named pipe. The client process can be either local or remote. A local client process is one that runs on the same computer system as the server process. A remote client process runs on a different system and communicates with the server process across a local area network (LAN). When the server process creates a named pipe with DosCreateNPipe, it must specify the direction that data will flow through the pipe. The process specifies an inbound pipe if it intends to read data from the client process, an outbound pipe if it intends to write data to the client process, or a duplex pipe if it intends to read from and write to the client process. The server process also specifies whether data passes through the pipe as bytes or messages. A message is a block of data, with a system-supplied header, that is read or written as a single unit. The server and client processes define the size and format of a message. The server process also specifies whether child processes will inherit the named pipe and how information will be read from and written to the pipe. If the server specifies wait mode, DosRead will be blocked (it will not return to the process) until data is available in the pipe, and DosWrite will be blocked until there is enough room in the pipe to contain the entire data buffer. If the server specifies no-wait mode, reading from an empty pipe or writing to a full pipe immediately returns an error value. A named pipe consists of two pipe buffers, one for each direction of communication. However, each end of the pipe has only one handle associated with it. The server receives the handle for its end when it creates the pipe with DosCreateNPipe. The client receives the handle for its end when it opens the pipe with DosOpen. The server and the client use their respective handles both to read from the pipe and to write to it. (This is in contrast to unnamed pipes, for which both the server and the client read from one handle and write to another.) In other words, data that is written by the process at one end of the pipe is read by the process at the other end. A named pipe can have multiple instances, up to the number specified when the pipe is first created. Pipe instances are actually separate pipes-that is, unique sets of pipe buffers with unique handles-that share the same name. The ability to create multiple pipe instances enables the server to communicate with multiple client processes at the same time. ═══ 4.1.2.1. Server-Client Communications Using Named Pipes ═══ A server process initiates a connection to a client process by using DosConnectNPipe. Once the pipe has been connected by the server process, the client process must open the pipe by using DosOpen to complete the connection. If no client process has opened the pipe when the server process calls DosConnectNPipe, the function either waits until a client opens the pipe or returns ERROR_PIPE_NOT_CONNECTED, depending on whether the server process created the pipe to wait for data. Each client process requires a separate instance of a pipe. For example, if a server process creates a named pipe and specifies that four instances of the pipe can be created, the process can then create three more instances of the named pipe (for a total of four pipes, including the original). Each instance has the same name, but each has a unique pipe handle. The process can then connect each pipe to the server. (Each instance must be connected by an explicit use of DosConnectNPipe.) In this example, the server must use each function four times to create and connect four separate instances of the named pipe. Each pipe is then available to a separate client process. If a client process receives the ERROR_PIPE_BUSY return value from DosOpen, no instances of the given pipe are available. The process can use DosWaitNPipe to wait for an instance to become available. The function waits until an instance is free or until the specified time interval elapses. When an instance becomes free, the process can open the pipe. If several processes are waiting for an instance to become available, the system gives the named pipe to the process that has been waiting the longest. The server process can disconnect a client process from a pipe by using DosDisConnectNPipe. Ideally, the client process closes the pipe by using DosClose before the server process disconnects the pipe. If the client process has not closed the pipe when the server process disconnects it, the server process forces the pipe closed and the client process subsequently receives errors if it attempts to access the pipe. Note that forcing the closure of the pipe might discard data in the pipe before the client process has had an opportunity to read it. A process can read and write bytes to a named pipe by using DosRead and DosWrite. A process can read or write messages by using DosTransactNPipe. Depending on the access mode, DosTransactNPipe writes a message to the pipe, reads a message from the pipe, or both. If a named pipe contains unread data or is not a message pipe, DosTransactNPipe fails. Named pipes created with the NP_ACCESS_INBOUND or NP_ACCESS_OUTBOUND access mode cannot use the DosTransactNPipe function. If the named pipe's client uses the DosTransactNPipe function, the function returns error code 5 (ERROR_ACCESS_DENIED). If it is reading from the pipe, DosTransactNPipe does not return until a complete message is read, even if the server process specified no-wait mode when the pipe was created. A process can also read data from a named pipe without removing the data from the pipe by using DosPeekNPipe. This function copies the specified number of bytes from the pipe and returns the number of bytes of data left in the pipe and the number of bytes left in the current message, if any. DosPeekNPipe also returns the state of the pipe. A named pipe can be in one of the following states: Named Pipe States ┌───────────────┬─────────────────────────────────────────────┐ │State │Description │ ├───────────────┼─────────────────────────────────────────────┤ │Connected │The pipe has been created and connected by │ │ │the server process and has been opened by a │ │ │client process. Only connected pipes can be │ │ │written to or read from. │ ├───────────────┼─────────────────────────────────────────────┤ │Closing │The pipe has been closed by the client │ │ │process but has not yet been disconnected by │ │ │the server process. │ ├───────────────┼─────────────────────────────────────────────┤ │Disconnected │The pipe has been created by the server │ │ │process but not connected, or has been │ │ │explicitly disconnected and not yet │ │ │reconnected. A disconnected pipe cannot │ │ │accept a DosOpen request. │ ├───────────────┼─────────────────────────────────────────────┤ │Listening │The pipe has been created and connected by │ │ │the server process but has not yet been │ │ │opened by a client process. A listening pipe │ │ │is ready to accept a request to open. If the │ │ │pipe is not in a listening state, DosOpen │ │ │returns ERROR_PIPE_BUSY. │ └───────────────┴─────────────────────────────────────────────┘ DosCallNPipe is used to open, write to, read from, and close a named message-format pipe. Named pipes created with the NP_ACCESS_INBOUND or NP_ACCESS_OUTBOUND access mode cannot use the DosCallNPipe function. If the named pipe's client uses the DosCallNPipe function, the function returns error code 5 (ERROR_ACCESS_DENIED). This function is equivalent to calling DosOpen, DosTransactNPipe, and DosClose If no instances of the pipe are available, DosCallNPipe waits for an instance or returns without opening the pipe if the specified interval of time elapses. A process can retrieve information about the handle state of a named pipe by using DosQueryNPHState. The handle state is a combination of the instance count, the access mode, and the pipe type (byte or message). DosQueryNPHState also specifies whether the process owning the handle is a server or client and whether the pipe waits if reading and writing cannot proceed. A process can modify the handle state of a named pipe by using DosSetNPHState. For example, the process can change the reading mode for the pipe, enabling a process to read bytes from the pipe instead of messages. ═══ 4.1.2.2. Steps in Managing Server-Client Transactions ═══ The following sequence summarizes the typical steps in the management of a named pipe: 1. The server process creates the pipe by calling DosCreateNPipe. DosCreateNPipe returns a handle for the server end of the pipe. (Note that the server uses the same handle to both read from and write to the pipe.) The pipe is now in the disconnected state and cannot be opened by a client process. The server process calls DosConnectNPipe to put the pipe into a listening state. 2. The pipe can now be opened by a client process. 3. A client process supplies the name of the pipe in a call to DosOpen and receives a pipe handle in return. (The client uses the same handle to both read from and write to the pipe.) The pipe is now in the connected state and can be read from or written to by the client. 4. The server and client processes communicate by calling DosRead and DosWrite. DosResetBuffer can be used to synchronize read and write dialogs. A server process that supports a large number of clients for a local named pipe can use DosSetNPipeSem and DosQueryNPipeSemState to coordinate access to the pipe. Server and client processes can also use DosTransactNPipe and DosCallNPipe to facilitate their communication. 5. After completing its transactions, the client process calls DosClose to close its end of the pipe. The pipe is now in the closing state and cannot be accessed by another client. 6. The server process calls DosDisConnectNPipe to acknowledge that the client has closed its end of the pipe. The pipe is now in the disconnected state again. 7. To enable another client process to open the pipe, the server must call DosConnectNPipe again. This puts the pipe back into the listening state. To end its access to the pipe, the server calls DosClose. When all of the handles for both ends of the pipe have been closed, the pipe is deallocated by the system. ═══ 4.1.2.3. Preparing a Named Pipe for a Client ═══ A server process uses DosConnectNPipe to put a newly created named pipe into the listening state. The pipe must be in the listening state in order for a client process to gain access to the pipe by calling DosOpen. After successfully opening the pipe and finishing its transactions, the client process calls DosClose to end its access to the pipe. The server process must acknowledge the close by calling DosDisConnectNPipe. It can then call DosConnectNPipe again to put the pipe into the listening state for the next client. Together, DosConnectNPipe and DosDisConnectNPipe enable a server to create a named pipe and to reuse it for communication with different clients. Without these functions, the server would have to delete and re-create the pipe for each client. Note: If multiple instances of a named pipe have been created, then each instance of the pipe must be put into the listening state before it can be opened by a client. ═══ 4.1.2.4. Facilitating Transaction Processing ═══ DosTransactNPipe and DosCallNPipe facilitate the use of named pipes by combining other named pipe functions. Compared to calling the other functions separately, DosTransactNPipe and DosCallNPipe provide significant performance gains for applications that operate in a networked environment. They can also be used by local processes. However, both of these functions can be used only with duplex message pipes.  DosTransactNPipe performs a transaction (a DosWrite followed by DosRead) on a duplex message pipe.  DosCallNPipe has the combined effect of DosOpen, DosTransactNPipe, and DosClose, and is referred to as a procedure call. It provides an efficient means of implementing local and remote procedure call interfaces between processes. ═══ 4.1.2.5. Coordinating Access to a Local Named Pipe with Semaphores ═══ When a process writes to a named pipe, the process at the other end (or handle) of the pipe might require notification that data is available to be read. Similarly, when a process reads from a named pipe, the process at the other end might require notification that write space has become available. As long as the communicating processes are on the same computer system, shared event semaphores and muxwait semaphores can be used to provide this notification. Using shared semaphores for this purpose is more efficient than dedicating a thread to periodically poll each pipe, particularly when a server process is communicating with a large number of client processes. Whenever data is available in the pipe, the system posts a semaphore to the server or client (whichever is reading from the pipe). This means that the reading process can use DosWaitEventSem or DosWaitMuxWaitSem to wait for data to arrive, rather than devote a thread to periodically polling the pipe. A process associates a semaphore with a named pipe by using DosSetNPipeSem. First, create an event semaphore with DosCreateEventSem, specifying the initial state of the semaphore as reset. Then call DosSetNPipeSem to attach the event semaphore to a particular named-pipe handle. Up to two event semaphores can be attached to each named pipe, one for the server process and one for the client process. If there is already a semaphore associated with one end of the pipe, that semaphore is replaced. A process can check the state of the semaphores by using DosQueryNPipeSemState. The server or client process must then call DosWaitEventSem. The particular thread that calls this function will block until data is either read from or written to the pipe. At that time, the system posts the event semaphore, enabling the blocked thread to resume its execution. If a process requires notification whenever any one of multiple named pipes has been written to or read from, it can either attach the same event semaphore to multiple pipes, or it can create a muxwait semaphore:  If the same event semaphore is attached to multiple pipes, then the KeyHandle parameter of DosSetNPipeSem is used to assign a unique value to each pipe. After the event semaphore has been posted, the process calls DosQueryNPipeSemState. This function returns information about each of the named pipes that are attached to the semaphore, including key-handle values. The calling process can use this information to determine which one of the named pipes has either data or write space available.  To use a muxwait semaphore, a process first creates an event semaphore for each of the pipes that it wants to monitor. Each semaphore must then be attached to a pipe by calling DosSetNPipeSem. Again, a unique key-handle value must be assigned to each pipe. Next, the process calls DosCreateMuxWaitSem to create the muxwait semaphore, specifying DCMW_WAIT_ANY as one of the flAttr flags. The muxwait semaphore will consist of a linked list of the previously created event semaphores. The process calls DosWaitMuxWaitSem so that it will be notified the next time data is read from or written to any of the pipes. However, it must call DosQueryNPipeSemState to determine which one of the pipes is ready to be read from or written to. ═══ 4.2. Using Unnamed Pipes ═══ Unnamed pipes are useful in applications that transfer data between related processes. They are commonly used to control the input and output of child processes by redirecting the standard input and output of the child process to a pipe controlled by the parent process. Note: In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred. ═══ 4.2.1. Creating Unnamed Pipes ═══ DosCreatePipe creates an unnamed pipe. Two handles are returned: one for read access to the pipe and one for write access. The pipe size specified is advisory; its actual size is dependent on the amount of available memory. If the size parameter is 0, the pipe is created with the default size, which is 512 bytes. This example creates an unnamed pipe. The current process can use the unnamed pipe for communication between itself and a child process. #define INCL_BASE /* Queue values */ #include #include HFILE hfReadHandle; /* Pointer to the read handle */ HFILE hfWriteHandle; /* Pointer to the write handle */ ULONG ulPipeSize; /* Pipe size */ APIRET ulrc; /* Return code */ ulPipeSize = 4096; /* Ask for 4KB of internal storage */ /* for the pipe */ ulrc = DosCreatePipe(&hfReadHandle, &hfWriteHandle, ulPipeSize); if (ulrc != 0) { printf("DosCreatePipe error: return code = %ld", ulrc); return; } On successful return, the ReadHandle variable contains the read handle for the pipe, and the WriteHandle variable contains the write handle for the pipe. After a process creates a pipe, any child process started with DosExecPgm inherits the pipe handles. Using shared memory, the parent process can pass one of the pipe handles to the child process; thus, one process can store data in the pipe and the other can retrieve it. ═══ 4.2.2. Reading from and Writing to Unnamed Pipes ═══ Applications use the OS/2 file system functions to read from and write to unnamed pipes. The handles returned by DosCreatePipe are used as file handles to DosRead and DosWrite. To write (or add data) to an unnamed pipe, call DosWrite, specifying the write handle of the pipe in DosWrite's file handle parameter. DosWrite requests to a pipe are processed in the order in which they are made. Multiple calls to DosWrite can be made before data is read (or removed) from the pipe. When the pipe becomes full, write requests are blocked until space is freed by read requests. When the pipe is too full to hold the entire contents of the write request, the portion that will fit is written to the pipe before the writer is blocked. To read from a pipe, call DosRead, specifying the read handle of the pipe in DosRead's file handle parameter. Subsequent calls to DosRead can empty the pipe if no further calls to DosWrite are made in the meantime. Different threads writing to and reading from a pipe are not synchronized. It is possible for the pipe reader to obtain partial contents of a write request if the pipe becomes full during the write request. If the process reading the pipe ends, the next DosWrite request for that pipe returns ERROR_BROKEN_PIPE. Calling DosClose terminates access to an unnamed pipe. However, the pipe is not deleted from memory until all handles to the pipe have been closed, including any handles that were defined with DosDupHandle. ═══ 4.2.3. Redirecting Standard I/O for Child Processes ═══ An application can use unnamed pipes to redirect the standard input and the standard output for a child process. A typical use of an unnamed pipe is to read the output of a child process. An application creates a pipe and then duplicates the standard output handle. When the child process is started, its standard output will be written into the pipe, where the application can read and display it. The following code fragment shows how to do this: #define INCL_DOSPROCESS #define INCL_DOSNMPIPES #include #define PIPESIZE 256 #define HF_STDOUT 1 /* Standard output handle */ HPIPE hpR, hpW; RESULTCODES resc; ULONG ulRead, ulWritten; CHAR achBuf[PIPESIZE], szFailName[CCHMAXPATH]; HFILE hfSave = -1, hfNew = HF_STDOUT; DosDupHandle(HF_STDOUT, &hfSave); /* Saves standard output handle */ DosCreatePipe(&hpR, &hpW, PIPESIZE); /* Creates pipe */ DosDupHandle(hpW, &hfNew); /* Duplicates standard output handle */ DosExecPgm(szFailName, sizeof(szFailName), /* Starts child process */ EXEC_ASYNC, (PSZ) NULL, (PSZ) NULL, &resc, "DUMMY.EXE"); DosClose(hpW); /* Closes write handle to ensure */ /* Notification at child termination */ DosDupHandle(hfSave, &hfNew); /* Brings stdout back */ /* * Read from the pipe and write to the screen * as long as there are bytes to read. * */ do { DosRead(hpR, achBuf, sizeof(achBuf), &ulRead); DosWrite(HF_STDOUT, achBuf, ulRead, &ulWritten); } while(ulRead); A parent process can also use unnamed pipes to communicate with a child process by redirecting both the standard input and the standard output for the child process. To do this, the parent process: 1. Uses DosDupHandle to redefine the read handle of one pipe as standard input (0000), and the write handle of the other pipe as standard output (0001). 2. Starts the child process with DosExecPgm. 3. Uses the remaining pipe handles to read and write to the pipes. The parent process controls the meanings for standard I/O for the child process. Thus, when the child process uses standard I/O handles with DosRead and DosWrite, it reads from and writes to the pipes of its parent instead of reading from the keyboard and writing to the display. ═══ 4.3. Using Named Pipes ═══ Named pipes are useful in applications that transfer data between processes. The processes using named pipes can be related, unrelated, or even on different computers. ═══ 4.3.1. Creating Named Pipes ═══ The server process creates a named pipe by using DosCreateNPipe. To create a named pipe, specify on the DosCreateNPipe function:  The name of the pipe  The access modes  The type of pipe (byte or message)  The sizes of the input and output buffers DosCreateNPipe returns a pipe handle that can be used in subsequent pipe operations. Each named pipe must have a unique name of the following form: \PIPE\PipeName The "\PIPE\" in the name above is required, but need not be uppercase. It is not the name of a subdirectory. To open a pipe on a remote computer, the client process must specify the name of the server process that opened the pipe as part of the pipe name, as follows: \\Server\PIPE\PipeName "\\Server" in the name above is the name of the remote computer; again, "\PIPE\" is required. The name parameter must conform to the rules for OS/2 file names, but no actual file is created for the pipe. Named pipes created with certain access modes prevent the named pipes' clients from using certain functions. If the named pipe is created with access mode NP_ACCESS_INBOUND, the named pipe's client cannot use these functions:  DosCallNPipe  DosTransactNPipe  DosPeekNPipe If the named pipe is created with access mode NP_ACCESS_OUTBOUND, the named pipe's client cannot use these functions:  DosCallNPipe  DosTransactNPipe  DosWrite If the named pipe's client uses an incorrect function, the function returns error code 5 (ERROR_ACCESS_DENIED). In the following code fragment, DosCreateNPipe creates a pipe named \pipe\pipe1 and supplies a unique handle identifying the pipe. OpenMode is set to NP_ACCESS_DUPLEX. This activates full duplex access to the named pipe. There will be no inheritance to child process, and no write-through (write-through only affects remote pipes). PipeMode is set to "NP_WMESG | NP_RMESG | 0x01". This specifies that the pipe should be read as a message stream for both reading and writing and an instance count of 1 (only one instance of the named pipe can be created at a time). The pipe will block on Read/Write if no data is available. #define INCL_DOSNMPIPES /* Named-pipe values */ #include #include UCHAR ucFileName[40]; /* Pipe name */ HPIPE hpPipeHandle; /* Pipe handle (returned) */ ULONG ulOpenMode; /* Open-mode parameters */ ULONG ulPipeMode; /* Pipe-mode parameters */ ULONG ulOutBufSize; /* Size of the out-buffer */ ULONG ulInBufSize; /* Size of the in-buffer */ ULONG ulTimeOut; /* Default value for */ /* DosWaitNPipe time-out */ /* parameter */ APIRET ulrc; /* Return code */ strcpy(ucFileName, "\\PIPE\\PIPE1"); ulOpenMode = NP_ACCESS_DUPLEX; /* Full duplex, no inheritance, */ /* no write-through */ ulPipeMode = NP_WMESG | NP_RMESG | 0x01; /* Block on read and write, message */ /* stream, instance count of 1 */ ulOutBufSize = 4096; /* The outgoing buffer must be 4KB in size */ ulInBufSize = 2048; /* The incoming buffer must be 2KB in size */ ulTimeOut = 10000; /* Time-out is 10 seconds (units are in milliseconds) */ ulrc = DosCreateNPipe(ucFileName, &hpPipeHandle, ulOpenMode, ulPipeMode, ulOutBufSize, ulInBufSize, ulTimeOut); if (ulrc != 0) { printf("DosCreateNPipe error: return code = %ld", ulrc); return; } Once the named pipe is created, the application can call DosConnectNPipe to connect a client process to the pipe. Once a client process connects to the pipe, the process can read from and write to the pipe. The preceding example creates a byte pipe, so the process can use DosRead and DosWrite to read from and write to the pipe. After the client process finishes using the pipe, the server process can disconnect the pipe by using DosDisConnectNPipe. The server process can either connect again or close the named pipe by using DosClose. When a server process creates a named pipe, it defines the pipe to the system by specifying the file write-through mode, the inheritance mode, the access and blocking modes, the pipe type, the read mode, the size of the in and out buffers, and the instance count. The following list describes these modes, types, and buffers.  The file write-through mode has significance only for communication with remote client processes. When the file write-through bit is set, data is sent across the network as soon as it is written; otherwise, OS/2 will in some cases hold data briefly in a local buffer before sending it across the network.  The inheritance mode specifies whether or not the pipe handle will be inherited by a child process.  The access mode specifies the direction in which data will flow through the pipe. The server creates an inbound pipe (a pipe with inbound access mode) if it intends to read data from the client process, an outbound pipe if it intends to write data to the client process, or a duplex pipe if it intends to both read from and write to the client process.  The blocking mode specifies whether or not DosRead and DosWrite will block when no data is available to read, or there is no room to write data.  The pipe type is the form in which a stream of data is written to the pipe. If the pipe is a byte pipe, the server and client processes write data as an undifferentiated stream of bytes. If the pipe is a message pipe, the processes write data as a stream of messages; messages are blocks of data, each with a system-supplied header, that are written as single units. The server and client processes define the size and format of a message.  The read mode is the form in which data is read from the pipe. The data in a pipe that was created as a byte pipe can only be read as bytes; therefore, a byte pipe will always be in byte-read mode. The data in a message pipe, however, can be read either as messages or as bytes. (If it is to be read as bytes, DosRead skips over the message headers). Therefore, message pipes can be in either message-read mode or byte-read mode. Note: The terms "byte pipe" and "message pipe" always refer to the pipe type-the form in which data is written to the pipe. When the read mode of a pipe is being referred to, it is always explicitly identified as either message-read mode or byte-read mode.  The in and out buffers can be up to 64KB in size. If the pipe will be read in message-read mode, and if the message size is known, the server can control how many messages the buffer will hold at one time by specifying the appropriate buffer size.  The instance count is the maximum number of instances of the named pipe that can be created. A pipe instance is actually a separate pipe-that is, a unique set of pipe buffers with unique handles. However, the term "pipe instance" is used to distinguish pipes that share the same name from pipes with different names. Because a client process uses only the name of the pipe when opening it, the existence of multiple pipe instances is transparent to a client process. Creating Multiple Instances of a Named Pipe Although each named pipe must have a unique name, a server process can create multiple instances of a pipe, all of which have the same name. A pipe instance is actually a separate pipe-that is, a unique set of pipe buffers with unique handles. The ability to create multiple pipe instances enables the server to communicate with multiple client processes at the same time. Because a client process uses only the name of the pipe when opening it, the existence of multiple pipe instances is transparent to a client process. The ICount parameter of DosCreateNPipe specifies the maximum number of named pipe instances that can be created. (An unlimited number can also be specified.) This parameter is specified only when the first instance of a named pipe is created; any subsequent attempt to redefine the instance count will be ignored. If the instance count is greater than 1, the server process can create additional pipe instances by specifying the same pipe name in subsequent calls to DosCreateNPipe. Generally, the attributes of the subsequent pipe instances are defined to be the same as those of the original pipe instance, because a client process that requests the pipe has no way of controlling which pipe instance will be assigned to it. After an additional pipe instance has been created, it is used in the same manner as the original pipe instance. That is, the same sequence of named pipe functions is used in the control or management of all named pipe instances. (See Steps in Managing Server-Client Transactions for more information.) Note: If all of the instances of a named pipe are in use when a client calls DosOpen, ERROR_PIPE_BUSY is returned. However, the client can wait for an instance of that pipe to become available by calling DosWaitNPipe. Multiple instances of a named pipe can be created by different processes. That is, multiple server processes can create and use instances of the same named pipe to communicate with their clients. ═══ 4.3.2. Opening Named Pipes ═══ A client process can open the client end of a named pipe by using DosOpen. DosOpen opens the client end of a pipe by name and returns a handle. The application must use the appropriate pipe name and access modes to open the pipe for reading, writing, or both. (To open a pipe on a remote computer, the client process must also specify the name of the computer system as part of the pipe name, as follows: \\ComputerName\PIPE\PipeName.) If a pipe name includes a remote LAN server name, DosOpen attempts to open the pipe on a remote computer. The server process can then read input from the client process through the pipe. The following code fragment opens a remote pipe, reads from the standard input (usually the keyboard), and sends the information to the server process through the pipe: #define INCL_DOSQUEUES /* Queue values */ #include #define PIPESIZE 256 #define SERVER_PIPE_NAME "\\\\myserver\\pipe\\mypipe" #define HF_STDIN 0 /* Standard input handle */ HPIPE hp; BYTE abBuf[PIPESIZE]; ULONG ulAction, ulRead, ulWritten; APIRET ulrc; ulrc = DosOpen(SERVER_PIPE_NAME, &hp, &ulAction, 0, FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE, (PEAOP) NULL); if (ulrc) DosExit(EXIT_PROCESS, 0); /* Open pipe failed */ do { /* Open pipe succeeded */ DosRead(HF_STDIN, abBuf, sizeof(abBuf), &ulRead); DosWrite(hp, abBuf, ulRead, &ulWritten); /* Writes to the pipe */ } while (ulRead > 2); /* Stop on a blank line */ DosClose(hp); The client process checks the return value from DosOpen to verify that the pipe was actually opened. If the server process has not yet created the pipe, DosOpen returns an error. When the client process finishes using the pipe, it closes the pipe by using DosClose. When a named pipe is opened, its initial state is set by the system to block read and write operations (blocking mode), and to read data as a byte stream (byte-read mode). However, the client can change these modes by calling DosSetNPHState. A call to DosOpen fails if all instances of the named pipe are already open. The open also fails if the pipe has been closed by a client, but the server has not called DosDisConnectNPipe (to acknowledge the client's close), followed by DosConnectNPipe (to prepare the pipe for the next client). In both of these situations, ERROR_PIPE_BUSY is returned. If all instances of a named pipe are busy, a client process can call DosWaitNPipe to wait for an instance to become available before it calls DosOpen again. After a pipe instance has been opened by a client, that same instance cannot be opened by another client at the same time. However, the opening process can duplicate the handle as many times as desired by calling DosDupHandle. This enables child processes to share access to a pipe instance with a parent process. The access-mode and sharing-mode fields that are specified for DosOpen must be the same as those that were specified by the server when it created the pipe with DosCreateNPipe. ═══ 4.3.3. Reading from Named Pipes ═══ Both the server and the client processes read data from a pipe by calling DosRead. The server reads from the handle that was returned when it created the pipe with DosCreateNPipe, and the client reads from the handle that was returned to it by DosOpen. When a pipe is created, the PipeMode parameter is used to specify both the pipe type and the read mode for the server end of the pipe:  A byte pipe can be read-only in byte-read mode. (DosCreateNPipe and DosSetNPHState return ERROR_INVALID_PARAMETER if message-read mode is specified for a byte pipe.) In byte-read mode, all currently available data is returned, up to the buffer size specified by DosRead.  A message pipe can be read in either byte-read mode or message-read mode, as follows: - When a message pipe is read in byte-read mode, the message headers are skipped, and the pipe is read as if it were a byte pipe. - When a message pipe is read in message-read mode, each message is read either in its entirety, or not at all, depending on the size of the message and the buffer length: -- If the buffer length that was specified for DosRead is larger than the next available message, then only that message is read, and the Bytes-Read parameter indicates the size of the message. -- If the buffer length for DosRead is smaller than the next available message, DosRead returns the number of bytes requested and ERROR_MORE_DATA. Subsequent calls to DosRead are blocked until the rest of the message can be transferred. (DosPeekNPipe can be used to find out how many bytes are left in the message.) The PipeMode parameter of DosCreateNPipe also specifies the blocking mode for the server end of the pipe:  If nonblocking mode was specified, DosRead returns immediately with 0 in the Bytes-Read parameter if no data is available.  If blocking mode was specified, DosRead blocks until data is available; the only time it will return with 0 in the Bytes-Read parameter is if it reaches end-of-file. DosRead works the same for both ends of the pipe. However, the read mode and blocking mode are not necessarily the same for the client end of the pipe as they are for the server end, because DosOpen always opens the CLIENT end in byte-read mode and blocking mode. The read mode and blocking mode for either end of the pipe can be changed by calling DosSetNPHState. The pipe type, however, is always the same for both the server and client ends of the pipe, and it cannot be changed. ═══ 4.3.4. Writing to Named Pipes ═══ Both the server and the client processes write to a pipe by calling DosWrite. The server writes to the handle that was returned to it by DosCreateNPipe, and the client writes to the handle that was returned to it by DosOpen. Either bytes or messages can be written, depending on whether the pipe was created as a byte pipe or as a message pipe. Named pipes created with the NP_ACCESS_OUTBOUND access mode cannot use the DosWrite function. If the named pipe's client uses the DosWrite function, the function returns error code 5 (ERROR_ACCESS_DENIED). An attempt to write to a pipe whose other end has been closed returns ERROR_BROKEN_PIPE or, if the other end was closed without without reading all pending data, ERROR_DISCARDED. When a process writes to a message pipe, the buffer-length parameter for DosWrite holds the size of the message that the process is writing. Because DosWrite automatically encodes message lengths in the pipe, applications do not have to encode this information in the data buffers. The action taken by DosWrite depends on the blocking mode of the pipe, which is not necessarily the same for the server and client ends of the pipe. For the server process, the blocking mode of the pipe is specified when the pipe is created. For a client process, the blocking mode is automatically set to blocking when the pipe is opened. The blocking mode can also be reset by calling DosSetNPHState. If the end of the message pipe that is being written to is in blocking mode, DosWrite does not return until all of the requested bytes have been written. (It might have to wait for the first part of the message to be read before it can write the rest of the message.) If the message pipe is in nonblocking mode, DosWrite takes the following action:  If the message is larger than the pipe buffer, DosWrite blocks until the entire message has been written. (Again, it might have to wait for the first part of the message to be read before it can write the rest of the message.)  If the message is smaller than the pipe buffer, but there is currently not enough room in the buffer, DosWrite returns with a value of 0 in the Bytes-Written parameter. If a byte pipe is in nonblocking mode, and if there is more data to be written than will fit in the pipe buffer, then DosWrite writes as many bytes as will fit in the buffer and returns the number of bytes that were actually written. If a process tries to write to a pipe whose other end is closed, ERROR_BROKEN_PIPE is returned. ═══ 4.3.5. Synchronizing Named Pipe Dialogs ═══ Communicating processes can synchronize their named pipe dialogs by calling DosResetBuffer after each call to DosWrite. When used with external files, DosResetBuffer flushes the buffer cache of the requesting process to disk. When used with named pipes, this function blocks the calling process at one end of the pipe until the data the calling process has written to the pipe has been successfully read at the other end of the pipe. ═══ 4.3.6. Determining Pipe Status ═══ DosQueryNPHState and DosQueryNPipeInfo can be used to obtain information about named pipes. DosQueryNPHState A client process can read data from the pipe, write data to the pipe, or both, depending on the access mode specified when the pipe was created. To check the current access mode, the client process can call DosQueryNPHState. The following code fragment shows how to use DosQueryNPHState to obtain information about a named pipe: #define INCL_DOSNMPIPES /* Named-pipe values */ #include #include HPIPE hpHandle; /* Pipe handle */ ULONG ulPipeHandleState; /* Pipe-handle state */ APIRET ulrc; /* Return code */ ulrc = DosQueryNPHState(hpHandle, &ulPipeHandleState); if (ulrc != 0) { printf("DosQueryNPHState error: return code = %ld", ulrc); return; } On successful return, PipeHandleState will contain information that describes the nature of the named pipe. DosQueryNPHState returns the following information about the pipe handle and the attributes of the pipe:  The end of the pipe that the handle is for (server or client end)  The pipe type (byte pipe or message pipe)  The instance count  The blocking mode (blocking or nonblocking)  The read mode (byte-read mode or message-read mode) The values for pipe type and instance count cannot be changed, so they are always the same as those that were specified when the pipe was created with DosCreateNPipe. The information returned for blocking mode and read mode, however, can come from different sources:  If the handle is for the server end of the pipe, then the blocking mode and the read mode were set with DosCreateNPipe, but could have been reset with DosSetNPHState.  If the handle is for the client end of the pipe, then the blocking mode and the read mode were set to "blocking" and "byte-read" by the system when the client called DosOpen. However, again, they could have been reset with DosSetNPHState. The pipe attributes are described in more detail in Creating Named Pipes. An application can use DosSetNPHState to change the wait mode and the read mode. The pipe cannot be changed to no-wait mode when another thread is blocked on a read or write operation to the same end of the pipe. DosQueryNPipeInfo More detailed information about a named pipe can be obtained by using DosQueryNPipeInfo. This function returns information in a PIPEINFO data structure that includes the name of the pipe, the current and maximum instance counts (the current number of pipes and the maximum number of times the pipe can be created), the size of the input and output buffers for the pipe, and the pipe identifier of the client process. The following code fragment shows how to use DosQueryNPipeInfo: #define INCL_DOSNMPIPES /* Named-pipe values */ #include #include HPIPE hpHandle; /* Pipe handle */ ULONG ulInfoLevel; /* Pipe data required */ PIPEINFO pipInfoBuf; /* Pipe information data structure */ ULONG ulInfoBufSize; /* Pipe data-buffer size */ APIRET ulrc; /* Return code */ ulInfoLevel = 1; /* Ask for standard level of pipe info */ ulInfoBufSize = sizeof(PIPEINFO); /* Length of pipe info data structure */ ulrc = DosQueryNPipeInfo(hpHandle, ulInfoLevel, &pipInfoBuf, ulInfoBufSize); if (ulrc != 0) { printf("DosQueryNPipeInfo error: return code = %ld", ulrc); return; } On successful return, the pipe information data structure contains a set of information describing the nature and the current state of the named pipe. DosQueryNPipeInfo returns level 1 or level 2 file information for the pipe. Level 1 information includes the following:  The actual sizes of the in-buffer and out-buffer  The maximum number of pipe instances permitted  The current number of pipe instances  The length of the pipe name  The ASCIIZ name of the pipe, including \\ComputerName if the pipe is in a remote computer system. Level 2 information consists of a unique 2-byte identifier for each of the pipe's client processes. ═══ 4.3.7. Examining the Contents of Named Pipes ═══ DosPeekNPipe examines the current contents of a named pipe. Named pipes created with the NP_ACCESS_INBOUND access mode cannot use the DosPeekNPipe function. If the named pipe's client uses the DosPeekNPipe function, the function returns error code 5 (ERROR_ACCESS_DENIED). It is similar to DosRead, except that DosPeekNPipe does not remove data from the pipe. In addition, DosPeekNPipe never blocks, even if the pipe is in blocking mode; if the pipe cannot be accessed immediately, ERROR_PIPE_BUSY is returned. Because DosPeekNPipe does not block, it returns only what is currently in the pipe. Thus, if a message pipe is being examined, only a portion of a message might be returned, even though the specified buffer length could accommodate the entire message. DosPeekNPipe also returns the state of the pipe. A named pipe can be in any of the following states: Connected, Disconnected, Listening, Closing. The following code fragment shows how to use DosPeekNPipe: #define INCL_DOSNMPIPES /* Named-pipe values */ #include #include HPIPE hpHandle; /* Pipe handle */ UCHAR ucBuffer[200]; /* Address of user buffer */ ULONG ulBufferLen; /* Buffer length */ ULONG ulBytesRead; /* Bytes read (returned) */ struct _AVAILDATA BytesAvail; /* Bytes available (returned) */ ULONG ulPipeState; /* Pipe state (returned) */ APIRET ulrc; /* Return code */ ulBufferLen = 200; /* Length of the read buffer */ ulrc = DosPeekNPipe(hpHandle, ucBuffer, ulBufferLen, &ulBytesRead, &BytesAvail, &ulPipeState); if (ulrc != 0) { printf("DosPeekNPipe error: return code = %ld", ulrc); return; } On successful return, the input buffer Buffer will contain up to the first 200 bytes from the named pipe, BytesRead will contain the number of bytes read into Buffer, BytesAvail will contain the total number of bytes that were available in the pipe, and PipeState will contain a value indicating the state of the named pipe. ═══ 4.3.8. Closing Named Pipes ═══ DosClose closes the specified pipe handle. When all of the handles that access one end of a pipe have been closed, the pipe is referred to as a broken pipe. If the client end of the pipe closes, no other process can reopen the pipe until the server calls DosDisConnectNPipe (to acknowledge the client's close) followed by DosConnectNPipe (to prepare the pipe for a new client). Until it calls DosDisConnectNPipe, the server will receive ERROR_EOF if it tries to read from the pipe, and ERROR_BROKEN_PIPE if it tries to write to it. Clients that attempt to open the pipe receive ERROR_PIPE_BUSY. If the server end closes when the client end is already closed, the pipe is deallocated immediately; otherwise, the pipe is not deallocated until the last client handle is closed. The following code fragment shows how to close a named pipe. Assume that a previous call to DosOpen provided the named pipe handle that is contained in Handle. #define INCL_DOSNMPIPES /* Named-pipe values */ #include #include HPIPE hpHandle; /* Pipe handle */ APIRET ulrc; /* Return code */ ulrc = DosDisConnectNPipe(hpHandle); if (ulrc != 0) { printf("DosDisConnectNPipe error: return code = %ld", ulrc); return; } ═══ 5. Program Execution Control ═══ 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 ═══ 5.1. 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. ═══ 5.1.1. 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. ═══ 5.1.2. 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. ═══ 5.1.2.1. 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. ═══ 5.1.2.2. 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. ═══ 5.1.2.3. 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. ═══ 5.1.3. 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. ═══ 5.1.4. 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. ═══ 5.1.4.1. 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. ═══ 5.1.4.2. 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. ═══ 5.1.4.3. 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. ═══ 5.2. About CPU Scheduling ═══ OS/2 performs prioritized, preemptive, multitasking. Prioritized means that OS/2 does not divide CPU time equally among all threads. All programs do not get equal access to the CPU. A prioritizing, time-slicing strategy is used to allocate access to the CPU among competing threads. Each thread has a priority and OS/2 runs the highest priority thread that is ready to run. Programs with higher priorities (a real-time robotic application, for example), are given access to the CPU before programs with lower priorities. If a thread with a higher priority than the currently running thread becomes ready to run, the current thread is stopped immediately, or preempted, and the higher priority thread is given the CPU. The lower priority thread does not get to complete its time slice. Threads of equal priority are given CPU time in a round-robin manner. Preemptive means that the multitasking activity needs no cooperation from the executing programs. OS/2 maintains control over executing programs, and stops, or preempts, them when their time slice with the CPU is over or when a higher priority program is ready to run. CPU scheduling is based on four priority classes-Time Critical, Fixed-High, Regular, and Idle-Time. Each class has 32 levels of execution ordering. Scheduling parameters are user-selectable at the time the system is started or can be varied dynamically based on system load. Depending on a thread's priority class and level, OS/2 periodically gives each thread in each process a small slice of CPU time. Threads with higher priorities always run before threads having lower priorities. A thread runs until its time is up or until a thread with a higher priority is ready to run. At that time, OS/2 preempts the thread and starts another thread. Threads can also voluntarily relinquish the CPU (for example, by calling DosSleep). The amount of time in each time slice is defined by the TIMESLICE command in the CONFIG.SYS file. The TIMESLICE command can be used by the user to customize the size of the time slices that a thread gets. The default is for OS/2 to dynamically vary the size of the time slice based on the activity of the thread and the overall system load. When a thread is created (using DosCreateThread), it inherits the priority of the thread that started it. DosSetPriority enables threads to change their priority classes and levels in response to changes in their execution environments. DosSetPriority enables a thread to change its own priority, or the priority of any thread within its process. DosSetPriority also enables changing priorities for the entire process and for descendant processes. Within each class, the priority level of a thread can vary because of a DosSetPriorty request or, if dynamic priority variation is being used, because of action taken by OS/2. ═══ 5.2.1. Priority Classes ═══ OS/2 uses four priority classes to determine when a thread receives a time slice: Priority Classes ┌──────────────────────────────┬──────────────────────────────┐ │Priority │Description │ ├──────────────────────────────┼──────────────────────────────┤ │Time-critical │Highest priority. For use │ │ │when response time is │ │ │critical. │ ├──────────────────────────────┼──────────────────────────────┤ │Fixed-high │Used by threads that provide │ │ │service to other threads. This│ │ │priority class is to be used │ │ │when it is desirable that the │ │ │thread not be too sensitive to│ │ │the foreground/background │ │ │boost provided by dynamic │ │ │priority variation. It is │ │ │meant for programs that need │ │ │to execute before regular │ │ │programs, but without the │ │ │immediate response time │ │ │requirement called for by │ │ │time-critical threads. │ ├──────────────────────────────┼──────────────────────────────┤ │Regular │Default priority. Most │ │ │threads belong in this class. │ ├──────────────────────────────┼──────────────────────────────┤ │Idle-time │Lowest priority. This │ │ │priority only gets CPU time │ │ │when there is no other work to│ │ │do. │ └──────────────────────────────┴──────────────────────────────┘ A time-critical thread always receives a time slice before a fixed-high thread, a fixed-high thread always receives a time slice before a regular thread, and a regular thread always receives a time slice before an idle-time thread. Time-Critical Threads Time-critical threads have the highest priority class and execute before any fixed-high, regular, or idle-time threads. The time-critical class is for threads that must react to events outside the system. For example, in a communications application, a thread responsible for reading data from the communications device needs enough time to read all incoming data. Because more than a regular time slice might be needed, a time-critical classification ensures that the thread gets all the time required. Time-critical threads have a static priority that is not varied by OS/2. They are scheduled among themselves in priority level order, with round-robin scheduling of threads of equal priority. Time-critical threads must be executed quickly, then free the CPU for other work until another time-critical event occurs. This is important to maintain good interactive responsiveness to the user and enable communications and other time critical applications to run concurrently. The time-critical activity should, when possible, be in a thread separate from the rest of the application, to isolate and minimize the time spent processing at the time-critical level. A good rule of thumb is that a time-critical thread should consist of no more than about 20,000 assembly language instructions. Fixed-High Threads A fixed-high thread has a priority class that is lower than time-critical but executes before any regular or idle-time threads. This class of threads should be used to provide service for other threads where it is desirable that the thread not be too sensitive to the foreground/background boost provided by dynamic priority variation. A messaging thread, would be a good example of this type of thread. OS/2 varies the priority of a fixed-high thread around a base value according to the activity of the thread and the system at any point in time. The base value can be set by the thread itself. Regular Threads A regular thread is the class that the majority of threads fall into. No explicit action is necessary by the application to run at this priority, it is the default. OS/2 varies the priority level of a regular thread around a base value according to the activity of the thread and the system at any point in time. The base value can be set by the thread itself. Idle-Time Threads An idle-time thread is one with very low priority that executes only when there are no regular, fixed-high, or time-critical threads to execute. Idle-time threads get CPU time only when there is no other work to do. The idle-time class is for threads that need very little CPU time. Idle-time threads have a static priority that is not varied by OS/2. ═══ 5.2.2. Priority Levels ═══ Within each class, OS/2 maintains a priority level for a thread. For each of the four priority classes, there are 32 priority levels-0 to 31. A thread with priority level 31 always receives a time slice before a thread with priority level 30, and so on. If two or more threads have the same priority level, OS/2 distributes the CPU time equally by using a round-robin scheme; that is, OS/2 gives a time slice to first one, then another, and so on, and then goes back to the first. A thread can use DosSetPriority to change its own priority or the priority of any other thread within its process. Although an application can set the priority level of a thread at any time, only applications that use more than one thread or process should do so. The best use of priority is to speed up threads or processes on which other threads or processes depend. For example, an application might temporarily raise the priority of a thread loading a file if another thread is waiting for that file to be loaded. Because the priority of a thread is relative to all other threads in the system, raising the priority of the threads in an application merely to get the extra CPU time adversely affects the overall operation of the system. There are other ways to affect the amount of CPU time a thread is given. A thread can define a critical section of code by using DosEnterCritSec and DosExitCritSec. While inside the critical section of code, a thread cannot be preempted by any other thread within its process (threads in other processes are still given their time slices). Using critical sections enables threads to get more CPU time, while not unduly affecting the rest of the system. The priority class and priority level are set using DosSetPriority. The priority class is changed by passing the new priority class to the function. The priority level, however, is changed by passing a value, called the priority-delta, that is added to the existing priority level to produce the new priority level; changes to the priority level are relative to the current priority level. Specifying a positive priority-delta increases the priority level, enabling the thread to obtain more CPU time than it normally would. A negative priority-delta decreases the priority level, giving the thread less CPU time than it would normally receive. The value is restricted to the valid range, based upon the current priority class of the process. If you change the priority level without changing the priority class, the priority-delta is relative to the current priority level. However, if you change the priority class at the same time that you change the priority level, the priority-delta value is relative to 0. Whenever DosSetPriority is called with a class specification, but no value is specified for priority-delta, the base priority level defaults to 0. The process identifier parameter of DosSetPriority specifies which process is affected by the call. A process can change the priority of itself, of any process that is a descendant of itself, or of one of its threads. A thread can change the priority of any thread within its current process. When a thread changes the priority of threads in a descendant process, however, only those threads running at the default priority will be changed. You cannot change the priority of a thread in a child process that has already changed its priority from the default. The initial thread of execution for an application is given a regular class priority that varies by the system. When a thread is created, it is initially dispatched in the same class and priority as the thread that started it. A child process inherits the priority of the thread in the parent process that creates it. ═══ 5.2.3. Priority Guidelines ═══ Within the two most common priority classes-time-critical and regular-there are certain broad guidelines recommended for choosing the priority level for a program.  TIME-CRITICAL CLASS The guidelines for level within the time critical class are set to maximize the number of different applications that can successfully multitask in an OS/2 system. The guidelines are described in the following table. Recommended Priority Levels for Time-Critical Threads ┌────────────────────┬────────────────────┬────────────────────────────────────────────────────────────┐ │Activity │Range of Recommended│Justification/Comments │ │ │Priority Levels │ │ ├────────────────────┼────────────────────┼────────────────────────────────────────────────────────────┤ │Robotics/Real time │20-31 │OS/2 systems can be used on manufacturing lines to control │ │process control │ │equipment or in other real time process control │ │ │ │applications. In this case a slow response from the PC could│ │ │ │cause expensive damage to equipment or even human injury. │ │ │ │Therefore the highest priority levels should be reserved for│ │ │ │these applications. │ ├────────────────────┼────────────────────┼────────────────────────────────────────────────────────────┤ │Communications │10-19 │In communications, the inability to get the processor could │ │ │ │cause a loss of data or communications sessions. Therefore │ │ │ │this class of applications is next highest. │ ├────────────────────┼────────────────────┼────────────────────────────────────────────────────────────┤ │Other │0-09 │Other threads might need to preempt the foreground in │ │ │ │special cases (for example, Control-Break). These should be │ │ │ │set below the other 2 classes. │ └────────────────────┴────────────────────┴────────────────────────────────────────────────────────────┘ In general, application performance is not a good reason to make a thread time critical.  REGULAR CLASS In cases where explicit priority levels are set, they should follow the guidelines listed below. Recommended Priority Levels for Regular Priority Threads ┌────────────────────┬────────────────────┬──────────────────────────────────────────────────┐ │Activity │Range of Recommended│Justification │ │ │Priority Level │ │ ├────────────────────┼────────────────────┼──────────────────────────────────────────────────┤ │Communications │26-31 │Communications should take priority over other │ │ │ │background processing to increase overlap with │ │ │ │transmission and processing on the partner PC or │ │ │ │host. This gives the best system performance. │ ├────────────────────┼────────────────────┼──────────────────────────────────────────────────┤ │Other │0-25. │If an application has multiple threads it might be│ │ │ │necessary to set them to several different │ │ │ │priorities to optimize the order in which they │ │ │ │run. A range of priority levels is provided to │ │ │ │facilitate this. (The default priority is regular │ │ │ │class, level = 0.) │ └────────────────────┴────────────────────┴──────────────────────────────────────────────────┘ ═══ 5.2.4. Dynamic Priority Alteration ═══ OS/2 can be configured to dynamically alter the priority of a process. The PRIORITY statement in CONFIG.SYS can be set to either ABSOLUTE or DYNAMIC. If PRIORITY specifies the ABSOLUTE option, all processes receive CPU time based on the priority established by calls to DosSetPriority. If the PRIORITY command in the CONFIG.SYS file specifies the DYNAMIC option, OS/2 adjusts process priorities based on system load and process activity, and on whether the process is in the foreground. DYNAMIC is the default setting; if the PRIORITY command is not specified, the system uses DYNAMIC priority. DYNAMIC is designed to gives the best overall system performance under most conditions. When dynamic priority variation is enabled, OS/2 grants higher priority to the foreground process than to background processes. System load and process activity are also taken into consideration. The priority of the process consists of a computed priority value that is based upon the display status of the process (foreground or background), and its recent I/O and CPU time usage history. When dynamic priority variation is enabled, I/O priority boosts are generated for keyboard input, window, foreground, processor starvation, device I/O, and DOS Session interrupts. This ensures that the foreground process-the process most likely to be in use-receives enough CPU time to provide quick response to user input. There are times when dynamic priority variation can interfere with a multi-threaded application's execution. For example, if you are doing a lot of keyboard input on a thread, its priority will get boosted and other threads may not get enough CPU time. A communication thread is an example of a time sensitive background thread which would be one case. The solution is to either set PRIORITY = ABSOLUTE or to call DosSetPriority on a regular basis to keep the threads priority at the desired level. ═══ 5.2.5. Altering the Size of the Time Slice ═══ The TIMESLICE command in CONFIG.SYS sets the minimum and maximum amount of processor time allocated to processes and programs for both OS/2 and DOS sessions. The first parameter selects the minimum TIMESLICE value in milliseconds. This value is the minimum amount of time a thread is to be processed before yielding the processor to a thread of the same priority level. This value must be an integer greater than or equal to 32. The second parameter selects the maximum TIMESLICE value in milliseconds. The value of this parameter is the maximum amount of time a thread can be processed before yielding processor time. This value must be an integer greater than or equal to the minimum value, and less than 65536. The default is dynamic time slicing, which varies the size of the time slice depending on system load and paging activity. Dynamic time slicing is designed to give the best performance in most situations. ═══ 5.3. Using Processes ═══ An OS/2 application that has been loaded into memory and prepared for execution is called a process. A process is the code, data, and other resources of the application, such as the open file handles, semaphores, pipes, queues and so on. OS/2 considers every application it loads to be a process. Each process has at least one thread, called the main thread or thread 1. The application runs when the system scheduler gives control to a thread in the process. For more on thread management, see Using Threads. Note: In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred. ═══ 5.3.1. Starting a Child Process ═══ You start a process by calling DosExecPgm. The process you start is a child of the calling, or parent, process and inherits many of the resources owned by the parent process, such as file handles. DosExecPgm creates a process environment from an executable file. The target program is loaded into storage, and it begins execution. The following code fragment starts an application named ABC: #define INCL_DOSPROCESS /* Process and thread values */ #include CHAR szFailName[CCHMAXPATH]; RESULTCODES resc; DosExecPgm(szFailName, /* Object-name buffer */ sizeof(szFailName), /* Length of buffer */ EXEC_SYNC, /* Sync flag */ (PSZ) NULL, /* Argument string */ (PSZ) NULL, /* Environment string */ &resc, /* Address for result */ "ABC.EXE"); /* Name of application */ In this example, ABC runs synchronously (as specified by EXEC_SYNC). This means the parent process temporarily stops while the child process runs. The parent process does not continue until the child process ends. ═══ 5.3.1.1. Starting an Asynchronous Child Process ═══ To start a child process and enable it to run asynchronously (that is, without suspending the parent process until the child process ends), you use the EXEC_ASYNC constant in a call to DosExecPgm. If you start a process in this way, the function copies the process identifier of the child process to the codeTerminate field of the RESULTCODES structure that is returned by DosExecPgm. You can use this process identifier to check the progress of the child process or to end the process. You can also run a child process asynchronously by using DosExecPgm with the EXEC_ASYNCRESULT constant. In addition to causing DosExecPgm to return to the parent process immediately, this constant also directs OS/2 to save a copy of the termination status when the child process ends. This status specifies the reason the child process stopped. The parent process can retrieve the termination status by using DosWaitChild. The following code fragment starts the program SIMPLE.EXE, and then waits for it to finish. It then prints the termination code and the return code. #define INCL_DOSPROCESS /* Process and thread values */ #include #include #define START_PROGRAM "SIMPLE.EXE" CHAR szLoadError[100]; PSZ pszArgs; PSZ pszEnvs; RESULTCODES rcReturnCodes; APIRET ulrc; ulrc = DosExecPgm(szLoadError, /* Object name buffer */ sizeof(szLoadError), /* Length of object name buffer */ EXEC_SYNC, /* Asynchronous/Trace flags */ pszArgs, /* Argument string */ pszEnvs, /* Environment string */ &rcReturnCodes, /* Termination codes */ START_PROGRAM); /* Program file name */ printf("Termination Code %d Return Code %d \n", rcReturnCodes.codeTerminate, rcReturnCodes.codeResult); /*----------------SIMPLE.EXE------------------*/ #define INCL_DOSPROCESS /* Process and thread values */ #include #include #define RETURN_CODE 0 main() { printf("Hello!\n"); DosExit(EXIT_PROCESS, /* End the thread or process */ RETURN_CODE); /* Result code */ } ═══ 5.3.1.2. Starting a Background Process ═══ You can start a child process in the background by specifying the EXEC_BACKGROUND constant in DosExecPgm. A background process runs independently of the parent process and is called a detached process. A detached process should not require any input from the keyboard or output to the video screen, but it can use interprocess communication, such as pipes, queues, and shared memory. The following code fragment starts the program BATCH.EXE in the background. #define INCL_DOSPROCESS /* Process and thread values */ #include #include #define START_PROGRAM "BATCH.EXE" CHAR szLoadError[100]; PSZ pszArgs; PSZ pszEnvs; RESULTCODES rcReturnCodes; APIRET ulrc; ulrc = DosExecPgm(szLoadError, /* Object name buffer */ sizeof(szLoadError), /* Length of object name buffer */ EXEC_BACKGROUND, /* Asynchronous/Trace flags */ pszArgs, /* Argument string */ pszEnvs, /* Environment string */ &rcReturnCodes, /* Termination codes */ START_PROGRAM); /* Program file name */ if (ulrc != 0) { printf("DosExecPgm error: return code = %ld", ulrc); return; } ═══ 5.3.1.3. Setting the Command Line and Environment for a Child Process ═══ When you start a process, it inherits many of the resources of the parent. This includes file handles, such as the standard input and standard output files. A child process also inherits the resources of the screen group, such as the mouse and video modes, and the environment variables of the parent process. The call to DosExecPgm determines the command line and environment that the child process receives. The fourth and fifth parameters of the function are pointers to the command line and the environment, respectively. If these pointers are NULL, the child process receives nothing for a command line and only an exact duplicate of the environment of the parent process. The parent process can modify this information by creating a string (ending with two NULL characters) and passing the address of the string to the function. The command line string must include the name of the application, followed by a NULL character, and the command line arguments, followed by two NULL characters. Any number of arguments can be passed to the child process, as long as the argument string ends with two NULL characters. The following code fragment passes to the child process the string "test -option1 -option2" as its command line: #define INCL_DOSPROCESS /* Process and thread values */ #include RESULTCODES resc; CHAR szFailName[CCHMAXPATH]; CHAR szCommandLine[] = "test\0-option1 -option2\0"; DosExecPgm(szFailName, /* Object-name buffer */ sizeof(szFailName), /* Length of buffer */ EXEC_SYNC, /* Sync flag */ szCommandLine, /* Argument string */ (PSZ) NULL, /* Environment string */ &resc, /* Address of result */ "test.exe"); /* Name of application */ ═══ 5.3.2. Changing the Priority of a Process ═══ Changing the priority of a process is simply a matter of changing the priority of every thread executing in the process. For the details see the section on changing thread priorities, Changing the Priority of a Thread. ═══ 5.3.3. Obtaining Information about Child Processes ═══ OS/2 creates and maintains a process information block for every process. An application can use DosGetInfoBlocks to access the process information block. This function returns a pointer to a PIB data structure, which contains the information from the process information block. The following code fragment returns the address of the process information block of the current process. The calling thread can subsequently browse either the PIB block. #define INCL_DOSPROCESS /* Process and thread values */ #include PTIB ptib; /* Address of pointer to thread information block */ PPIB ppib; /* Address of pointer to process information block */ APIRET rc; /* Return code */ rc = DosGetInfoBlocks(&ptib, &ppib); DosGetInfoBlocks also returns the address of the thread information block of the current thread. ═══ 5.3.4. Waiting for a Child Process to End ═══ You can synchronize the execution of a process with the execution of one of its child processes by calling DosWaitChild. DosWaitChild does not return until the specified child process ends. This can be useful, for example, if the parent process needs to ensure that the child process has completed its task before the parent process continues with its own task. In the following code fragment, the parent process starts a child process and then waits for the child process to finish: #define INCL_DOSPROCESS /* Process and thread values */ #include RESULTCODES resc; PID pidEnded; CHAR szFailName[CCHMAXPATH]; CHAR szCommandLine[] = "APP\0test\0"; DosExecPgm(szFailName, /* Failed-name buffer */ sizeof(szFailName), /* Length of buffer */ EXEC_ASYNC, /* Sync flag */ szCommandLine, /* Argument string */ (PSZ) NULL, /* Environment string */ &resc, /* Address of result */ "APP.EXE"); /* Name of application */ DosWaitChild(DCWA_PROCESS, /* Only the process */ DCWW_WAIT, /* Waits until it is done */ &resc, /* Puts the result here */ &pidEnded, /* PID of ended process */ resc.codeTerminate); /* Child to wait for */ You can cause a process to wait for all its child processes to end by using the DCWA_PROCESSTREE constant in the call to DosWaitChild. ═══ 5.3.5. Ending the Current Process ═══ You end the current process by calling DosExit. When you exit, the system stops the process and frees any existing resources the process owns. In the following code fragment, DosExit is used to end the process if the given file does not exist: #define INCL_DOSPROCESS /* Process and thread values */ #include #define HF_STDERR 2 /* Standard error handle */ HFILE hf; ULONG ulAction, ulWritten; APIRET rc; rc = DosOpen("SAMPLE.TXT", &hf, &ulAction, 0, FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYWRITE, (PEAOP2) NULL); if (rc) { DosWrite(HF_STDERR, "Cannot open file\r\n", 18, &ulWritten); DosExit(EXIT_PROCESS, rc); } EXIT_PROCESS directs DosExit to end all the threads in a process including the calling thread, thus ending the process. DosExit includes an error value that is returned to the parent process through the RESULTCODES structure specified in the DosExecPgm call that started the process. If you started the application from the command line, the command processor, CMD.EXE, makes this value available through the ERRORLEVEL variable. If another process started the application, that process can call DosWaitChild to determine the error value. If you want to exit only from a given thread, you can call DosExit with the EXIT_THREAD constant. This will end only the calling thread; other threads in the process are not affected. If the thread you end is the last thread in the process, the process also ends. If the thread consists of a function, the thread ends when the function returns. ═══ 5.3.6. Terminating a Process ═══ A process can end the execution of a descendant process by calling DosKillProcess. This causes OS/2 to send a XCPT_SIGNAL_KILLPROC exception to the target process. The child processes of the target process can also be ended. The following code fragment ends the specified process and all child processes belonging to that process: #define INCL_DOSPROCESS /* Process and thread values */ #include PID pidProcess; DosKillProcess(DKP_PROCESSTREE, pidProcess); In this example, the pidProcess parameter specifies which descendant process to end. The process identifier is returned by DosExecPgm in the codeTerminate field of the RESULTCODES structure when you start the child process. The parameter DKP_PROCESSTREE in the example indicates that the specified process, pidProcess, and all of its descendant processes are to be ended. If you specify DKP_PROCESS in a call to DosKillProcess, only the specified process is ended. Its child processes, if any, continue to run. The process to be ended must either be the current process, or it must have been directly created by the current process with DosExecPgm for asynchronous execution. That is, a process can end itself and its descendants. The process to be ended need not still be executing. If it has started its own child processes, but has stopped executing, its children can still be flagged for termination. Obtaining the Termination Status of a Child Process OS/2 saves the termination status for a process if the process was started by using the EXEC_ASYNCRESULT constant in the call to DosExecPgm. You can retrieve the termination status of the most recently ended process by using the DCWW_NOWAIT constant with DosWaitChild and setting the child process identification parameter to 0. The DCWW_NOWAIT constant directs the function to return immediately, without waiting for a process to end. Instead, the function retrieves the termination status from the process that most recently ended. If you specify a child process identification with DCWW_NOWAIT, DosWaitChild returns ERROR_CHILD_NOT_COMPLETE if the child process has not ended. Once the specified process has ended, DosWaitChild returns its termination code. The following code fragment starts a child session (the program SIMPLE.EXE), and then retrieves the termination status from the process that most recently ended. #define INCL_DOSPROCESS /* Process and thread values */ #include #include #define START_PROGRAM "SIMPLE.EXE" CHAR szLoadError[100]; PSZ pszArgs; PSZ pszEnvs; RESULTCODES rcReturnCodes; ULONG ulPid; /* Process ID (returned) */ ULONG ulTarget; /* Process ID of process to wait for */ APIRET ulrc; /* Return code */ strcpy(pszArgs, "-a2 -l"); /* Pass arguments "-a2" and "-l" */ ulTarget = 0; /* Process ID for the most recently ended process */ ulrc = DosExecPgm(szLoadError, /* Object name buffer */ sizeof(szLoadError), /* Length of object name buffer */ EXEC_ASYNCRESULT, /* Asynchronous/Trace flags */ pszArgs, /* Argument string */ pszEnvs, /* Environment string */ &rcReturnCodes, /* Termination codes */ START_PROGRAM); /* Program file name */ if (ulrc != 0) { printf("DosExecPgm error: return code = %ld", ulrc); return; } ulrc = DosWaitChild(DCWA_PROCESS, /* Execution options */ DCWW_NOWAIT, /* Wait options */ &rcReturnCodes, /* Termination codes */ &ulPid, /* Process ID (returned) */ ulTarget); /* Process ID of process to wait for */ if (ulrc != 0) { printf("DosWaitChild error: return code = %ld", ulrc); return; } ═══ 5.3.7. Creating an Exit List ═══ 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. If there are multiple addresses on the list, each function gets control in numerical order (with 0 being first and 0FFH being last), based on a value supplied by the application when it calls DosExitList. In case of duplicate entries for this parameter, the routines will be executed in LIFO (last in, first out) order. DosExitList requires a function code that specifies an action and a pointer to the function that is to receive control upon termination. The following code fragment adds the locally defined function SaveFiles to the exit list: #define INCL_DOSPROCESS /* Process and thread values */ #include #define HF_STDOUT 1 /* Standard output handle */ VOID main(VOID) { . . . DosExitList(EXLST_ADD, (PFNEXITLIST) SaveFiles); . . . DosExit(EXIT_PROCESS, 0); } VOID APIENTRY SaveFiles(ULONG ulTermCode) { ULONG ulWritten; switch (ulTermCode) { case TC_EXIT: case TC_KILLPROCESS: DosWrite(HF_STDOUT, "Goodbye\r\n", 10, &ulWritten); break; case TC_HARDERROR: case TC_TRAP: break; } DosExitList(EXLST_EXIT, 0); } Any function that you add to the list must take one parameter. The function can carry out any task, as shown in the preceding example, but as its last action it must call DosExitList, specifying the EXLST_EXIT constant. An exit-list function must not have a return value and must not call DosExit to end. When an exit-list routine receives control, the parameter (located at ESP+4 on the stack) contains an indicator of why the process ended. The values returned are the same as those for termination codes returned by DosWaitChild or DosExecPgm requests. These values are: TC_EXIT (0) Normal exit TC_HARDERROR (1) Hard-error halt TC_TRAP (2) Trap operation for a 16-bit child process TC_KILLPROCESS (3) Unintercepted DosKillProcess TC_EXCEPTION (4) Exception operation for a 32-bit child process To execute the exit-list functions, OS/2 reassigns thread 1 after ending all other threads in the process. If thread 1 has already exited (for example, if it called DosExit without ending other threads in the process), the exit-list functions cannot be executed. In general, it is poor practice to end thread 1 without ending all other threads. Before transferring control to the termination routines, OS/2 resets the stack to its initial value. Transfer is by way of an assembly language JMP instruction. The routine must be in the address space of the ending process. The termination routine at that address takes the necessary steps and then calls DosExitList with FunctionOrder=EXLST_EXIT. Control is then transferred to the next address in the invocation order of the exit list. When all such addresses have been processed, the process completes exiting. If a routine on the list does not call DosExitList at the completion of its processing, the process waits, and OS/2 prevents termination. During DosExitList processing, the process is in a state of partial termination. All threads of the process are ended, except for the one executing the exit-list routines. To ensure good response to a user request to end a program, there should be minimal delay in completing termination. Termination routines should be short and fail-safe. You can use DosExitList with the EXLST_REMOVE constant to remove a function from the list. The designer of an exit-list routine must carefully consider which functions will be used by the routine. In general, calls to most OS/2 functions are valid in a DosExitList routine, but certain functions, such as DosCreateThread and DosExecPgm, are not. ═══ 5.4. Using Threads ═══ A thread is a dispatchable unit of execution that consists of a set of instructions, related CPU register values, and a stack. Every process has at least one thread and can have many threads running at the same time. The application runs when OS/2 gives control to a thread in the process. The thread is the basic unit of execution scheduling. Every process has at least one thread, called the main thread or thread 1. To execute different parts of an application simultaneously, you can start several threads. A new thread inherits all the resources currently owned by the process. This means that if you opened a file before creating the thread, the file is available to the thread. Similarly, if the new thread creates or opens a resource, such as another file, that resource is available to the other threads in the process. ═══ 5.4.1. Creating a Thread ═══ You use DosCreateThread to create a new thread for a process. DosCreateThread requires the address of the code to execute and a variable to receive the identifier of the thread. The address of the code is typically the address of a function that is defined within the application. You can pass one ULONG parameter to the thread when you start it. To pass more information to the thread, pass the address of a data structure. You specify how you want the thread to run when you call DosCreateThread. If bit 1 of the flag parameter in the function call is 0, the thread starts immediately. If bit 1 of the flag parameter is 1, the thread starts suspended and will not run until the application calls DosResumeThread. Each thread maintains its own stack. You specify the size of the stack when you call DosCreateThread. The amount of space needed depends on a number of factors, including the number of function calls the thread makes and the number of parameters and local variables used by each function. If you plan to call OS/2 functions, a reasonable stack size is 8192 bytes (8KB); 4096 bytes (4KB) should be the absolute minimum. If bit 1 of the flag parameter is 0, OS/2 uses the default method for initializing the thread's stack. If bit 1 of the flag parameter is 1, memory for the thread's entire stack is pre-committed. The following code fragment creates a thread: #define INCL_DOSPROCESS /* Process and thread values */ #include #include #define HF_STDOUT 1 /* Standard output handle */ VOID _System ThreadFunc(ULONG ulBeepLen); INT main(VOID) { ULONG ulBeepLen; TID tidThread; ulBeepLen = 1000; DosCreateThread(&tidThread, /* Thread ID returned by DosCreateThread */ &ThreadFunc, /* Address of the thread function */ ulBeepLen, /* Parameter passed to thread */ 0, /* Immediate execution, default stack */ /* initialization */ 4096); /* Stack size */ DosWaitThread(&tidThread, DCWW_WAIT); return 0; } /* end main */ /***************************************************/ /* ThreadFunc */ /***************************************************/ VOID _System ThreadFunc(ULONG ulBeepLen) { ULONG ulWritten; /* needed for DosWrite */ DosBeep(750, ulBeepLen); DosWrite(HF_STDOUT, "Message from new thread\r\n", 25, &ulWritten); DosExit(EXIT_PROCESS, 0); } /* end ThreadFunc */ A thread continues to run until it calls DosExit, returns control to OS/2, or is ended by a DosKillThread call. In the preceding example, the thread exits when the function implicitly returns control at the end of the function. ═══ 5.4.2. Obtaining Information about a Thread ═══ OS/2 creates and maintains a thread information block for each thread. An application can use DosGetInfoBlocks to access the thread information block. This function returns a pointer to a TIB data structure. The code fragment below returns the address of the thread information block of the current thread. The calling thread can subsequently browse the TIB. DosGetInfoBlocks also returns the address of the process information block of the current process. ═══ 5.4.3. Changing the Priority of a Thread ═══ You can use DosSetPriority to change the execution priority of threads in a process. The execution priority defines when or how often a thread receives an execution time slice. Threads with higher priorities receive time slices before those with lower priorities. When a thread that is higher in priority than the currently running thread becomes ready to run, it immediately preempts the lower priority thread (the lower priority thread does not get to complete its time slice). Threads with equal priority receive time slices in a round-robin order. If you raise the priority of a thread, the thread is executed more frequently. You can use DosSetPriority to set the priority for one thread in a process, for all threads in a process (and thus the process itself), or for threads in a child process. A process can change the priority of any thread within itself. When a process changes the priority of threads in a descendant process, however, only those with default priorities are changed. The priority of any thread in a descendant process that has already explicitly changed its priority from the default with DosSetPriority is not changed. In the following code fragment, DosSetPriority lowers the priority of a process to be used as a background process: #define INCL_DOSPROCESS /* Process and thread values */ #include PTIB ptib; /* thread information block */ PPIB ppib; /* process information block */ APIRET rc; /* return code from DosGetInfoBlocks */ rc = DosGetInfoBlocks(&ptib, &ppib); DosSetPriority(PRTYS_PROCESSTREE, PRTYC_IDLETIME, 0, ppib->pib_ulpid); DosGetInfoBlocks retrieves the process information blocks and thread information blocks. DosSetPriority then uses the process identifier to change the priority to idle time (idle-time processes receive the least attention by OS/2). If you specify PRTYS_PROCESS when calling DosSetPriority, only the priority of the specified process changes. The priorities of all child processes remain unchanged. If you specify PRTYS_THREAD in the call to DosSetPriority, you must specify a thread identifier as the last parameter. The priority of the specified thread changes, but the priorities of all other threads in the process remain unchanged. Whenever DosSetPriority is called with a class specification, but no value is specified for priority-delta, the base priority level defaults to 0. ═══ 5.4.4. Suspending the Current Thread ═══ You can temporarily suspend the execution of the current thread for a set amount of time by using DosSleep. This function suspends execution of the thread for the specified number of milliseconds. DosSleep is useful when you need to delay the execution of a task. For example, you can use DosSleep to delay a response when the user presses a DIRECTION key. The delay provides the user with enough time to observe the results and release the key. The following code fragment uses DosSleep to suspend execution of a thread for 1000 milliseconds (1 second): #define INCL_DOSPROCESS /* Process and thread values */ #include DosSleep(1000); ═══ 5.4.5. Suspending and Resuming Execution of a Thread ═══ DosSuspendThread and DosResumeThread are used to temporarily suspend the execution of a thread when it is not needed and resume execution when the thread is needed. These functions are best used when it is necessary for a process to temporarily suspend execution of a thread that is in the middle of a task. For example, consider a thread that opens and reads files from a disk. If other threads in the process do not require input from these files, the process can suspend execution of the thread so that OS/2 does not needlessly grant control to it. The specified thread might not be suspended immediately if it has some system resources locked that must be freed first. However, the thread is not permitted to execute further application program instructions until a corresponding DosResumeThread is called. A thread can only suspend another thread that is within its process. DosResumeThread is used to enable the suspended thread to resume execution. The following code fragment temporarily suspends the execution of another thread within the same process. A subsequent call to DosResumeThread restarts the suspended thread. Assume that the thread identifier of the target thread has been placed int ThreadID already. #define INCL_DOSPROCESS /* Process and thread values */ #include #include TID tidThreadID; /* Thread identifier */ APIRET ulrc; /* Return code */ ulrc = DosSuspendThread(tidThreadID); if (ulrc != 0) { printf("DosSuspendThread error: return code = %ld", ulrc); return; } ulrc = DosResumeThread(tidThreadID); if (ulrc != 0) { printf("DosResumeThread error: return code = %ld", ulrc); return; } ═══ 5.4.6. Entering Critical Sections ═══ A thread can prevent execution of any of the other threads in its process by calling DosEnterCritSec. This function temporarily prevents a thread from being preempted by other threads within its process. The other threads in the process will not be executed until the current thread calls DosExitCritSec. This enables the calling thread to access a time-critical resource of the process. The following code fragment enters a section that will not be preempted, performs a simple task, and then exits quickly. #define INCL_DOSPROCESS /* Process and thread values */ #include #include BOOL flag; /* Program control flag */ APIRET ulrc; /* Return code */ ulrc = DosEnterCritSec(); if (ulrc != 0) { printf("DosEnterCritSec error: return code = %ld", ulrc); return; } flag = TRUE; /* Set the flag */ ulrc = DosExitCritSec(); if (ulrc != 0) { printf("DosExitCritSec error: return code = %ld", ulrc); return; } A count is maintained of the outstanding DosEnterCritSec requests. The count is incremented when a DosEnterCritSec request is made, and decremented when a DosExitCritSec request is made. A DosExitCritSec request will not cause normal thread dispatching to be restored while the count is greater than 0. This count is maintained in a WORD-sized variable. If overflow occurs, the count is set to its maximum value, and an error is returned. The operation is not performed when this occurs. Threads that call DosEnterCritSec must not must not make dynamic link calls within these critical sections. The dynamic link procedure could be using semaphores to serialize a resource. If a thread entering the critical section blocks another thread that already owns the resource which the dynamic link function is about to request, a deadlock occurs. For example, threads of an application are serializing their access to a queue by means of a semaphore. A thread enters a critical section and makes a request to read the queue while another thread already has the semaphore that controls access to the queue. The thread that has the semaphore is now effectively blocked by DosEnterCritSec, and the thread that has requested the queue waits forever to access it. Note: Thread 1 is the initial thread of execution. It handles all signals (Ctrl+C, Ctrl+Break, and KillProcess). If a signal occurs while DosEnterCritSec is active, thread 1 can begin execution to process the signal. Thread 1 must not access the critical resource that is being protected by the use of DosEnterCritSec. ═══ 5.4.7. Waiting for a Thread to End ═══ An application might need to ensure that one thread has finished executing before another thread continues with its own task. For example, one thread might have to finish reading a disk file into memory before another thread can use the information. You can use DosWaitThread to suspend a thread until another thread has ended. DosWaitThread places the current thread into a wait state until another thread in the current process has ended. It then returns the thread identifier of the ending thread. The following code fragment creates three threads. The thread identifier for each thread is returned by DosCreateThread in the atid array. Using &atid[0] as a parameter in the call to DosWaitThread causes OS/2 to wait until the thread with that identifier (the thread running Thread2Func) ends. #define INCL_DOSPROCESS /* Process and thread values */ #include #define HF_STDOUT 1 /* Standard output handle */ TID tidAtid[3]; ULONG ulWritten; DosCreateThread(&tidAtid[0], Thread2Func, 0, 0, 4096); DosCreateThread(&tidAtid[1], Thread3Func, 0, 0, 4096); DosCreateThread(&tidAtid[2], Thread4Func, 0, 0, 4096); DosWaitThread(&tidAtid[0], DCWW_WAIT); DosWrite(HF_STDOUT, "The thread has ended\r\n", 27, &ulWritten); If you set the tid parameter to 0 in the call to DosWaitThread, OS/2 waits only until the next thread (any thread in the process) ends. The identifier for the ended thread is then returned in the tid parameter. After the threads are created as in the preceding example, the following code fragment waits until one of the threads ends, and then returns its thread identifier: #define INCL_DOSPROCESS /* Process and thread values */ #include TID tid; tid = 0; DosWaitThread(&tid, DCWW_WAIT); The thread identifier of the next thread to end after the DosWaitThread call is returned in the tid parameter. You can use DosWaitThread so that you can recover thread resources when the thread ends, or to synchronize the execution of a thread with the execution of other threads. ═══ 5.4.8. Ending the Current Thread ═══ To end the execution of the current thread, call DosExit, specifying the action code as 0. It is good practice to end each thread in the application individually. If the thread that is ending is the last thread in the process, or if the request is to end all threads in the process, then the process also ends. All threads except one are ended, and that thread executes any routines in the list specified by DosExitList. When this is complete, the resources of the process are released, and the result code that was specified in the DosExit call is passed to any thread that calls DosWaitChild for this process. In the following code fragment, the main routine starts another program, SIMPLE.EXE, and then expects a return code of 3 to be returned. SIMPLE.EXE sets the return code with DosExit. #define INCL_DOSPROCESS /* Process and thread values */ #include #include #define START_PROGRAM "SIMPLE.EXE" #define RETURN_OK 3 CHAR szLoadError[100]; PSZ pszArgs; PSZ pszEnvs; RESULTCODES rcReturnCodes; APIRET ulrc; ulrc = DosExecPgm(szLoadError, /* Object name buffer */ sizeof(szLoadError), /* Length of object name buffer */ EXEC_SYNC, /* Asynchronous/Trace flags */ pszArgs, /* Argument string */ pszEnvs, /* Environment string */ &rcReturnCodes, /* Termination codes */ START_PROGRAM); /* Program file name */ if (ReturnCodes.codeResult == RETURN_OK) /* Check result code */ printf("Things are ok..."); else printf("Something is wrong..."); /*----------------SIMPLE.EXE------------------*/ #define INCL_DOSPROCESS /* Process and thread values */ #include #include #define RETURN_CODE 3 main() { printf("Hello!\n"); DosExit(EXIT_THREAD, /* End thread/process */ RETURN_CODE); /* Result code */ } When you specify DosExit for thread 1 (the initial thread of execution started by OS/2 for this process), all of the threads in the process are ended, and the process is ended. ═══ 5.4.9. Ending a Thread ═══ DosKillThread ends a thread in the current process. DosKillThread enables a thread in a process to end any other thread in the process. DosKillThread is used to force a thread within the current process to end without causing the entire process to be ended. #define INCL_DOSPROCESS /* Process and thread values */ #include TID tidThread; /* ThreadID of the thread to be ended */ DosCreateThread(&tidThread, ThreadFunction, 0, 0, 4096); . . . DosKillThread(tidThread); DosKillThread returns to the requestor without waiting for the target thread to complete its termination processing. It is an invalid operation to use DosKillThread to kill the current thread. Terminating thread 1 will cause the entire process to end similar to executing DosExit on thread 1. DosKillThread will not end a thread that is suspended. Instead the suspended thread will be ended when it resumes execution. For this reason, you should not kill the main thread of an application if there are any secondary threads that are suspended. If the target thread is executing 16-bit code or was created by a 16-bit requester, ERROR_BUSY is returned. ═══ 5.5. Using Sessions ═══ A session consists of at least one process and a virtual console (either full screen, or Presentation Manager window, and buffers for keyboard and mouse input). An application can manage its own child sessions by using DosStartSession, DosStopSession, DosSelectSession, and DosSetSession. ═══ 5.5.1. Starting a Session ═══ DosStartSession is used to start new sessions and to specify the name of the application to be started in the new session. There are five types of sessions that you can start: full screen, text window, Presentation Manager (PM), full screen DOS Session, and windowed DOS Session. OS/2 applications running in any of the OS/2 session types-full screen, text window, and PM-can start a session for any other application type, including DOS Sessions. Applications running in DOS Sessions cannot start sessions. 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 session of the caller, or one of the descendant sessions of the caller, is currently executing in the foreground. A session can be started as an unrelated session or as a child session. In the following code fragment, an unrelated, foreground session is created, and the application, SIMON.EXE, is started in the new session: #define INCL_DOSPROCESS /* Process and thread values */ #define INCL_DOSSESMGR #include #define HF_STDOUT 1 /* Standard output handle */ STARTDATA sd; PID pidProcess; CHAR szBuf[CCHMAXPATH]; ULONG ulSessionID, cbWritten; APIRET rc; CHAR szPgmName[] = "SIMON.EXE"; sd.Length = sizeof(sd); /* Length of the structure */ sd.Related = SSF_RELATED_INDEPENDENT; /* Unrelated session */ sd.FgBg = SSF_FGBG_FORE; /* In the foreground */ sd.TraceOpt = SSF_TRACEOPT_NONE; /* No tracing */ sd.PgmTitle = (PSZ) NULL; /* Title is PgmName */ sd.PgmName = szPgmName; /* Address of szPgmName */ sd.PgmInputs = (PBYTE) NULL; /* No command line args */ sd.TermQ = (PBYTE) NULL; /* No terminal queue */ sd.Environment = (PBYTE) NULL; /* Inherits environment */ sd.InheritOpt = SSF_INHERTOPT_PARENT; /* Uses parent environment */ sd.SessionType = SSF_TYPE_PM; /* PM session */ sd.IconFile = (PSZ) NULL; /* Uses default icon */ sd.PgmHandle = 0; /* Used by Win calls */ sd.PgmControl = SSF_CONTROL_MAXIMIZE; /* Starts app maximized */ sd.InitXPos = 0; /* Lower left corner */ sd.InitYPos = 0; /* Lower left corner */ sd.InitXSize = 0; /* Ignored for maximized */ sd.InitYSize = 0; /* Ignored for maximized */ sd.ObjectBuffer = szBuf; /* Fail-name buffer */ sd.ObjectBuffLen = sizeof(szBuf); /* Buffer length */ rc = DosStartSession(&sd, &ulSessionID, &pidProcess); if (rc) { DosBeep(750,250); DosWrite(HF_STDOUT, "error starting new session\r\n", 28, &cbWritten); DosExit(EXIT_PROCESS, rc); } Before calling DosStartSession, you must create a STARTDATA data structure that defines the session to be started. Different lengths for the data structure are supported to provide compatibility and various levels of application control. DosStartSession uses the STARTDATA structure to specify the details of the new session, such as the name of the application to start in the session, whether the new session should be started in the foreground or background, and whether the new session is unrelated or is a child session of the session calling DosStartSession. When a session is created, the title specified in STARTDATA, (or the application title if no title is specified in STARTDATA) is added to the Window List. The Related field in the STARTDATA structure specifies whether the new session is related to the session calling DosStartSession. If the InheritOpt field in the STARTDATA data structure is set to 1, the new session inherits the environment and open file handles of the calling process. This applies for both unrelated and related sessions. ═══ 5.5.2. Controlling the Execution of Child Sessions ═══ Once a process has started a child session, it can use DosSelectSession to control the child session. A process calls DosSetSession to set the selectability and bonding of a child session. ═══ 5.5.2.1. Setting User Selectability of a Child Session ═══ A process calls DosSetSession to set the selectability of a child session. When a child session is selectable, the user can select it from the Window List or by using Alt+Esc. When a child session is nonselectable, the user cannot select the session from the Window List or move to it by using the Alt+Esc keys. In the following code fragment, DosSetSession makes a child session nonselectable: #define INCL_DOSPROCESS /* Process and thread values */ #define INCL_DOSSESMGR #include ULONG ulSessionID; STATUSDATA stsdata; stsdata.Length = sizeof(stsdata); stsdata.SelectInd = SET_SESSION_NON_SELECTABLE; /* Non-selectable */ stsdata.BondInd = SET_SESSION_UNCHANGED; /* Leaves session bonding */ /* index unchanged */ DosSetSession(ulSessionID, &stsdata); Once a child session is made nonselectable, the user cannot select the session from the Window List or move to it by using the Alt+Esc keys. However, the parent session can still bring the child session to the foreground by using DosSelectSession. If the session contains a Presentation Manager application or is a windowed session, the user will still be able to select it with a mouse. The parent session can make a nonselectable child session selectable by setting the SelectInd field to SET_SESSION_SELECTABLE in the STATUSDATA structure. DosSetSession can be called only by a parent session and only for a child session. That is, the calling process must be the process that started the child session using DosStartSession. Neither the parent session itself nor any grandchild, nor any other descendant session beyond a child session can be the target of this call. Additionally, DosSetSession cannot be used to change the status of a session that was started as an unrelated session. The Related field in the STARTDATA structure must have been set to 1 when the session was started. ═══ 5.5.2.2. Binding Child Sessions to Parent Sessions ═══ An application can use DosSetSession to establish a bond between a parent session and one of its child sessions. When the two sessions are bound, OS/2 brings the child session to the foreground when the user selects the parent session. In the following code fragment, a parent session is bound to the child session specified by the ulSessionID parameter: #define INCL_DOSPROCESS /* Process and thread values */ #define INCL_DOSSESMGR #include ULONG ulSessionID; STATUSDATA stsdata; stsdata.Length = sizeof(stsdata); stsdata.SelectInd = SET_SESSION_UNCHANGED; /* Leaves select setting alone */ stsdata.BondInd = SET_SESSION_BOND; /* Binds parent and child */ DosSetSession(ulSessionID, &stsdata); When the application uses DosSetSession to establish a parent-child bond, any bond the parent has with another child session is broken. The application can remove the parent-child bond by calling DosSetSession with the BondInd field (in the STATUSDATA structure) set to SET_SESSION_NO_BOND. A parent session can be executing in either the foreground or the background when it calls DosSetSession. ═══ 5.5.2.3. Switching a Session to the Foreground ═══ An application can bring a session to the foreground, or select the session, by calling DosSelectSession. DosSelectSession can only be used to select the current session or one of the current session's child sessions. It cannot be used to select a grandchild session, or any other descendant session beyond a child session, or any sessions that were started as unrelated sessions. The session making the call, or one of its child sessions, must be executing in the foreground at the time the function is called. A process can call DosSelectSession with its own session identifier to switch itself to the foreground when one of its descendants is executing in the foreground. The following code fragment uses DosSelectSession to switch the child session specified by the ulSessionID parameter to the foreground for five seconds. The application then switches the parent session back to the foreground: #define INCL_DOSPROCESS /* Process and thread values */ #include ULONG ulSessionID; DosSelectSession(ulSessionID); /* Switches child to foreground */ DosSleep(5000); /* Sleeps for 5 seconds */ DosSelectSession(0); /* Switches parent back */ ═══ 5.5.3. Terminating a Session ═══ DosStopSession can be used by a parent session to stop one or all of its child sessions. If the child session specified in the call to DosStopSession has related sessions, the related sessions are also ended. The parent session can be running in the foreground or the background when it calls DosStopSession. If the child session is running in the foreground when it is ended, the parent session becomes the foreground session. DosStopSession can only be called by a parent session for a child session. Neither the parent session itself, nor any grandchild, nor any other descendant session beyond a child session, nor any unrelated session, can be the target of this call. In the following code fragment, the child session specified by the ulSessionID parameter is ended: #define INCL_DOSPROCESS /* Process and thread values */ #define INCL_DOSSESMGR #include ULONG ulSessionID; DosStopSession(STOP_SESSION_SPECIFIED, ulSessionID); An application can end all its child sessions by setting the first parameter to STOP_SESSION_ALL in the call to DosStopSession. If this is specified, the second parameter is ignored. A process running in a child session can ignore the request to end. If the process has set up its own exception handler, it might not end immediately after the call to DosStopSession. The only way the parent process can be certain that the child session has ended is to wait for notification through the termination queue specified in the call to DosStartSession that started the session. When the child session ends, OS/2 writes a data element into the termination queue, specifying the child process identifier and the termination status. If the process in the session specified by DosStopSession has not ended, then DosStopSession still returns a normal return code. You can ensure that a process in a session has ended by waiting for notification from the termination queue specified with DosStartSession. ═══ 6. Queues ═══ Communication between processes is valuable in a multitasking operating system to enable concurrent processes to work together. Queues are one of three forms of interprocess communication (IPC), the other forms of IPC being semaphores and pipes. This chapter describes how to create and use queues. Queues enable one or more processes to transfer data to a specific target process. Note: The queues used for interprocess communication should not to be confused with the message queues used for communication between Presentation Manager (PM) and PM applications, nor with the printer queues used by the print spooler in managing print jobs. The following topics are related to the information in this chapter:  Memory (shared memory)  Program execution and control  Semaphores  Pipes ═══ 6.1. About Queues ═══ A queue is a named, ordered list of elements that is used to pass information between threads of the same (related) process or between different (unrelated) processes. Processes pass information to a queue in the form of elements. An element is a 32-bit unit of information. Queue elements can be values, flags, pointers to shared memory regions, anything that can fit into 32 bits. The format of a queue element depends entirely on the process that creates the queue (the queue owner). Only the queue owner can read elements from the queue; other processes can only write to the queue. Reading an element automatically removes it from the queue. The process that creates the queue is known as the server process of the queue. The other processes that access the queue are known as client processes. The owner of the queue (the server process) can choose the order in which to read incoming information and can examine queue elements without removing them from the queue. Queue elements can be added and accessed in First-In-First-Out (FIFO), Last-In-First-Out (LIFO), or priority-based order. Any process that has the name of a queue can open the queue and write to it. The processes writing elements to the queue must use the format determined by the queue owner. Queues are very efficient. They pass only 32-bit sized elements, rather than large data structures. However, queues can be used only for one-way communication, because a client process can write to a queue but cannot read from one. Typically, processes use queues to transfer information about the contents of a shared memory. The elements in the queue could contain the address and length of data areas in shared memory objects. The sending process allocates a shared memory object and gives access to the shared memory to the queue owner. The sending process can free the shared memory after writing the elements to the queue because the shared memory will not be deallocated until the queue owner frees it. Any thread in the process that owns the queue can examine queue elements without removing them. This is called peeking at the queue. OS/2 supplies the process identifier of the process that writes an element to the queue, so that a process reading from or peeking at the queue can determine the origin of the element. The process identifier is returned as part of a REQUESTDATA data structure. Threads can use the ulData field of the REQUESTDATA data structure to pass additional information about the queue element. If the queue is empty when a process attempts to read from it, the process can either wait for an element to become available or continue executing without reading from the queue. Semaphores can be used to indicate when an element is in the queue. ═══ 6.1.1. Queues and Semaphores ═══ If a process manages only one queue, it typically waits for an element to become available. However, if a process manages several queues, waiting for one queue means that other queues cannot be read. To avoid wasting time while waiting, a process can supply an event semaphore when it calls DosReadQueue or DosPeekQueue. The process can then continue to execute without actually reading an element from the queue, because DosWriteQueue will post the semaphore only when an element is ready. The semaphore remains posted until someone resets it; usually the queue owner process resets the semaphore after it reads all the available information from the queue. If a process uses a unique semaphore for each queue, it can use DosWaitMuxWaitSem to wait for the first queue to receive an element. Only one semaphore is permitted per queue. ═══ 6.1.2. Queue Servers and Clients ═══ The server process and its threads have certain queue-managing privileges. Only the server process and its threads can:  Examine queue elements without removing them (DosPeekQueue)  Remove elements from the queue (DosReadQueue)  Purge all the elements in a queue (DosPurgeQueue)  Write to the queue without opening it first (DosWriteQueue)  Delete the queue (DosCloseQueue). Both server and client processes can query the number of elements in the queue using DosQueryQueue. Client processes can query the queue (DosQueryQueue) and add elements to it (DosWriteQueue), but they must first gain access to the queue by calling DosOpenQueue. When a client process is finished with a queue, it ends its access to the queue by calling DosCloseQueue. (Note that, unlike the server process and its threads, a client process cannot use DosCloseQueue to delete a queue.) When a queue is opened by a client process, an access count is set to 1. Each client process has its own access count. The access count is incremented whenever a thread in a process opens the queue and decremented whenever a thread in the process closes the queue. Access to the queue by the client process ends when the access count for the process reaches 0. When the server process closes the queue, the queue is terminated and removed from the system. ═══ 6.1.3. Queue Element Order ═══ DosReadQueue reads either a specified element or the first element in the queue. The first element in the queue depends on the queue type, which is specified when the queue is created. A queue can have FIFO, LIFO, or priority ordering. Priority values range from 0 (lowest priority) through 15 (highest priority). The writing process assigns a priority to a queue element when the element is written to the queue. DosReadQueue reads elements from the queue in descending order of priority, regardless of the order in which DosWriteQueue placed the elements in the queue. Elements with equal priority are read in FIFO order. ═══ 6.1.4. Obtaining Information about Queues and Queue Elements ═══ Any thread in the process that owns the queue can use DosPeekQueue to examine the elements in the queue to determine which one to actually read. Each call to DosPeekQueue returns the identifier of the next element in the queue, so the function can be called repeatedly to move through the queue. The identifier of the desired element can then be supplied to DosReadQueue. Any process that has opened a queue can use DosQueryQueue to determine the number of elements in the queue. This function also returns an error value if the queue owner has closed the queue. ═══ 6.2. Using Queues ═══ Queues are useful for a process to manage input from other processes. The examples in the following sections show how to create and use queues. Note: In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred. ═══ 6.2.1. Creating a Queue ═══ A thread creates a queue by using DosCreateQueue and specifying a queue name and the queue type as arguments. The queue name must be unique and have the following form: \QUEUES\QueName The "\QUEUES\" is required, though it need not be uppercase. It is not a subdirectory. The QueName parameter must conform to the rules for OS/2 file names, although no actual file is created for the queue. The process that creates the queue is known as the server process of the queue. The other processes that access the queue are known as client processes. The following code fragment creates a FIFO queue named \queues\sample.que: #define INCL_DOSQUEUES /* Queue values */ #include HQUEUE hq; DosCreateQueue(&hq, QUE_FIFO | QUE_CONVERT_ADDRESS, "\\queues\\sample.que"); When the server process creates the queue, it determines whether the ordering of queue elements is based on arrival (FIFO or LIFO) or priority. If the ordering is based on priority, then priority values must be assigned whenever data is added to the queue. The server must also specify whether OS/2 is to convert 16-bit addresses of elements placed in the queue by 16-bit processes to 32-bit addresses. After a process has created a queue, any thread in that process can access the queue with equal authority. ═══ 6.2.2. Allocating Memory for Queue Data ═══ When queues are used only to pass the addresses to data rather than the data itself, processes must allocate shared memory objects for storing queue data. The two most common methods of storing queue data are:  Using a named shared memory object - for related processes  Using unnamed shared memory objects - for unrelated processes ═══ 6.2.2.1. Named Shared Memory Objects ═══ Related processes generally use a single named shared memory object for storing queue data. The server process allocates the memory object by calling DosAllocSharedMem. Care must be taken to ensure that the memory object is large enough to meet application requirements. The name of the shared memory object is established by agreement among the server and the client processes. For simplicity, the name can be the same as the queue name, except that the prefix \SHAREMEM\ must be used instead of \QUEUES\. A client process accesses the named shared memory object by calling DosGetNamedSharedMem. It must then call DosOpenQueue to gain access to the queue of the server. Before the server process ends, it releases the memory object by calling DosFreeMem. ═══ 6.2.2.2. Unnamed Shared Memory Objects ═══ Unrelated processes generally use unnamed shared memory objects for storing queue data. This makes it possible for a client process to store data in a shared memory object without knowing its name. To use unnamed shared memory objects for storing queue data, the server process must take the following steps whenever it is called by a client: 1. Save the process identification (PID) of the client process. 2. Allocate an unnamed shared memory object for the data of the client process by calling DosAllocSharedMem. 3. Give the client process the capability of accessing the memory object by calling DosGiveSharedMem with the client's PID. The client process must then call DosOpenQueue to gain access to the server's queue. After each client completes its queue requests or ends, the server calls DosFreeMem to release the client's memory object. ═══ 6.2.3. Opening a Queue ═══ Once the queue is created, the server process of the queue and the threads of the server process have immediate access to the queue and can proceed to access the queue. A client process must request access to a queue by calling DosOpenQueue. Once the queue is open, the client process can add an element to the queue with DosWriteQueue, and it can query the number of elements in the queue with DosQueryQueue. DosOpenQueue retrieves the queue handle and the process identifier of the queue owner. The function also increments the queue's access count. The following code fragment shows how another process would open the queue created with DosCreateQueue. #define INCL_DOSQUEUES #include #define HF_STDOUT 1 /* Standard output handle */ HQUEUE hq; PID pidOwner; ULONG ulWritten; APIRET ulrc; ulrc = DosOpenQueue(&pidOwner, &hq, "\\queues\\sample.que"); if (ulrc) { DosWrite(HF_STDOUT, "\r\n Queue open failed. \r\n", 24, &ulWritten); DosExit(EXIT_PROCESS, 1); } else { DosWrite(HF_STDOUT, "\r\n Queue opened. \r\n", 19, &ulWritten); } When it is finished with the queue, a thread in the client process ends its access by calling DosCloseQueue. DosCloseQueue decrements the access count for the process each time it is called. When the access count reaches 0, the connection between the client process and the queue is terminated. After a process has opened a queue, any thread in that process can access the queue with equal authority. Note: If a queue was created by a call to the 16-bit DosCreateQueue, then it is not accessible to 32-bit DosOpenQueue requests, and ERROR_QUE_PROC_NO_ACCESS will be returned. ═══ 6.2.4. Writing to a Queue ═══ The server process and any of its threads can add an element to a queue simply by calling DosWriteQueue. A client process, however, must first request access to the queue by calling DosOpenQueue. Processes that communicate by passing the addresses of shared memory objects through the queue must have a shared memory object that they each have access to. Once a process opens the queue, it can allocate shared memory by using DosAllocMem with the OBJ_GIVEABLE attribute and then give the shared memory to the queue owner with DosGiveSharedMem. A process that has opened a queue can write to the queue by using DosWriteQueue. The writing process must create elements in a form that the queue owner can read. The following code fragment adds an element to a queue. Assume that the caller has placed the handle of the queue into QueueHandle already. Assume also that DataBuffer has been set to point to a data element in shared memory, and that DataLength has been set to contain the length of the data element in shared memory. #define INCL_DOSQUEUES /* Queue values */ #include #include HQUEUE hqQueueHandle; /* Queue handle */ ULONG ulRequest; /* Request-identification data */ ULONG ulDataLength; /* Length of element being added */ PVOID pDataBuffer; /* Element being added */ ULONG ulElemPriority; /* Priority of element being added */ APIRET ulrc; /* Return code */ ulRequest = 0; /* Assume that no special data is being */ /* sent along with this write request */ ulElemPriority = 0; /* For priority-based queues: add the */ /* new queue element at the logical end */ /* of the queue */ ulrc = DosWriteQueue(hqQueueHandle, ulRequest, ulDataLength, pDataBuffer, ulElemPriority); if (ulrc != 0) { printf("DosWriteQueue error: return code = %ld", ulrc); return; } Once the process has written to the queue, it frees the shared memory. However, the memory will not be freed until the queue owner also frees it. If the queue was created as a priority-based queue (as specified in the QueueFlags parameter of DosCreateQueue), then the priority of the element that is being added must be specified. If the server process has ended, or if it has closed the queue before DosWriteQueue is called, then ERROR_QUE_INVALID_HANDLE is returned. ═══ 6.2.5. Reading from a Queue ═══ The queue owner (server process) and its threads can read an element from the queue by using DosReadQueue. The owner can read the first element in the queue by specifying 0 as the element number. Alternatively, the owner can read a particular element in the queue by specifying an element code returned from DosPeekQueue. This function is not available to client processes. DosReadQueue can either remove queue elements in the order that was specified when the queue was created (FIFO, LIFO, or priority), or it can use an element identifier from DosPeekQueue as input to remove a previously examined element. The following code fragment reads an element from the queue. Assume that the caller has placed the handle of the queue into QueueHandle already and that the identifier of the process that owns the queue has been placed into OwningPID already. #define INCL_DOSQUEUES /* Queue values */ #include #include HQUEUE hqQueueHandle; /* Queue handle */ REQUESTDATA rqRequest; /* Request-identification data */ ULONG ulDataLength; /* Length of element received */ PULONG pulDataAddress; /* Address of element received */ ULONG ulElementCode; /* Request a particular element */ BOOL32 bNoWait; /* No wait if queue is empty */ BYTE bElemPriority; /* Priority of element received */ HEV hevSemHandle; /* Semaphore handle */ PID pidOwningPID; /* PID of queue owner */ APIRET ulrc; /* Return code */ rqRequest.pid = pidOwningPID; /* Set request data block to indicate */ /* queue owner */ ulElementCode = 0; /* Indicate that the read should start */ /* at the front of the queue */ bNoWait = 0; /* Indicate that the read should wait */ /* if the queue is currently empty */ hevSemHandle = 0; /* Unused since this is a call that */ /* waits synchronously */ ulrc = DosReadQueue(hqQueueHandle, &rqRequest, &ulDataLength, (PVOID *) &pulDataAddress, ulElementCode, bNoWait, &bElemPriority, hevSemHandle); if (ulrc != 0) { printf("DosReadQueue error: return code = %ld", ulrc); return; } On successful return, DataLength contains the size of the element on the queue that is pointed to by the pointer within DataAddress, ElemPriority has been updated to contain the priority of the queue element pointed to by DataAddress, and Request.ulData contains any special data that the DosWriteQueue caller placed into the queue. If the queue is empty and NoWait is set to DCWW_WAIT (0), the calling thread waits until an element is placed in the queue. If the queue is empty and NoWait is set to DCWW_NOWAIT (1), DosReadQueue returns immediately with ERROR_QUE_EMPTY. If NoWait is set to DCWW_NOWAIT, an event semaphore must be provided so that the calling thread can determine when an element has been placed in the queue. The semaphore is created by calling DosCreateEventSem, and its handle is supplied as a DosReadQueue parameter. The first time an event semaphore handle is supplied in a DosReadQueue or DosPeekQueue request for which DCWW_NOWAIT has been specified for a particular queue, the handle is saved by the system. The same handle must be supplied in all subsequent DosReadQueue and DosPeekQueue requests that are called for that queue; if a different handle is supplied, ERROR_INVALID_PARAMETER is returned. When a client process adds an element to the queue, the system automatically opens the semaphore (if necessary) and posts it. The server can either call DosQueryEventSem periodically to determine whether the semaphore has been posted, or it can call DosWaitEventSem. DosWaitEventSem causes the calling thread to block until the semaphore is posted. After the event semaphore has been posted, the calling thread must call DosReadQueue again to remove the newly added queue element. If QUE_CONVERT_ADDRESS is specified in the call to DosCreateQueue, OS/2 will automatically convert 16-bit addresses to 32-bit addresses. ═══ 6.2.6. Peeking at a Queue ═══ The server process and its threads can examine a queue element by calling DosPeekQueue. This function is not available to client processes. Unlike DosReadQueue, DosPeekQueue does not remove the element from the queue. DosPeekQueue can either examine elements in the order that was specified when the queue was created (FIFO, LIFO, or priority), or it can examine the next element in the queue after a previous DosPeekQueue request has been called. By making multiple DosPeekQueue requests, the server process can search through a queue, examining each element in turn. When it locates the element it is searching for, the server process can remove the element from the queue by calling DosReadQueue. If several threads are using the same queue, the process writing to the queue can use the ulData field of the REQUESTDATA data structure to indicate that an element is directed to a particular thread. The thread can peek at the queue whenever data is available and read any elements containing the appropriate value in the ulData field. The following code fragment shows how a thread can use DosPeekQueue to examine the elements in a queue. Assume that a previous call to DosOpenQueue provided the queue handle that is contained in QueueHandle. Assume that the identifier of the process that owns the queue has been placed into OwningPID already. #define INCL_DOSQUEUES /* Queue values */ #define INCL_DOSPROCESS /* needed for DCWW_WAIT */ #include #include HQUEUE hqQueueHandle; /* Queue handle */ REQUESTDATA rqRequest; /* Request-identification data */ ULONG ulDataLength; /* Length of examined element */ PVOID pDataAddress; /* Address of examined element */ ULONG ulElementCode; /* Indicator of examined element */ BOOL32 bNoWait; /* No wait if queue is empty */ BYTE bElemPriority; /* Priority of examined element */ HEV hevSemHandle; /* Semaphore handle */ PID pidOwningPID; /* PID of queue owner */ APIRET ulrc; /* Return code */ rqRequest.pid = pidOwningPID; /* Set request data block to indicate */ /* queue owner */ ulElementCode = 0; /* Indicate that the peek should start */ /* at the front of the queue */ bNoWait = DCWW_WAIT; /* Indicate that the peek call should */ /* wait if the queue is currently empty */ hevSemHandle = 0; /* Unused since this is a call that */ /* synchronously waits */ ulrc = DosPeekQueue(hqQueueHandle, &rqRequest, &ulDataLength, &pDataAddress, &ulElementCode, bNoWait, &bElemPriority, hevSemHandle); if (ulrc != 0) { printf("DosPeekQueue error: return code = %ld", ulrc); return; } On successful return, DataLength contains the size of the element on the queue that is pointed to by the pointer within DataAddress, ElementCode has been updated to indicate the next queue element, ElemPriority has been updated to contain the priority of the queue element pointed to by DataAddress, and Request.ulData contains any special data that the DosWriteQueue caller placed into the queue. If the queue is empty and NoWait is set to DCWW_WAIT (0), the calling thread waits until an element is placed in the queue. If the queue is empty and NoWait is set to DCWW_NOWAIT (1), DosPeekQueue returns immediately with ERROR_QUE_EMPTY. If NoWait is set to DCWW_NOWAIT, an event semaphore must be provided so that the calling thread can determine when an element has been placed in the queue. The semaphore is created by calling DosCreateEventSem, and its handle is supplied as a DosPeekQueue parameter. The first time an event semaphore handle is supplied in a DosPeekQueue or DosReadQueue request for which DCWW_NOWAIT has been specified for a particular queue, the handle is saved by the system. The same handle must be supplied in all subsequent DosPeekQueue and DosReadQueue requests that are called for that queue; if a different handle is supplied, ERROR_INVALID_PARAMETER is returned. When a client process adds an element to the queue, the system automatically opens the semaphore (if necessary) and posts it. The server can either call DosQueryEventSem periodically to determine whether the semaphore has been posted, or it can call DosWaitEventSem. DosWaitEventSem causes the calling thread to block until the semaphore is posted. After the event semaphore has been posted, the calling thread must call DosPeekQueue again to examine the newly added queue element. If QUE_CONVERT_ADDRESS is specified in the call to DosCreateQueue, OS/2 will automatically convert 16-bit addresses to 32-bit addresses. ═══ 6.2.7. Purging a Queue ═══ The server process or any of its threads can empty a queue of all its elements by calling DosPurgeQueue. This function is not available to client processes. Warning: This is an unconditional purge of all elements in the queue. The following code fragment shows how the owner of a queue can empty the queue of all data elements. Assume that the owner of the queue has saved the queue's handle (obtained in a previous call to DosCreateQueue) in QueueHandle. #define INCL_DOSQUEUES /* Queue values */ #include #include HQUEUE hqQueueHandle; /* Queue handle */ APIRET ulrc; /* Return code */ ulrc = DosPurgeQueue(hqQueueHandle); if (ulrc != 0) { printf("DosPurgeQueue error: return code = %ld", ulrc); return; } ═══ 6.2.8. Closing a Queue ═══ DosCloseQueue can be used both to end access to a queue and to delete a queue. The action taken as a result of a DosCloseQueue request depends on:  Whether the requesting process is the server process or a client process  The value of the access count, which is maintained for each client process by OS/2. The access count for a client process is incremented whenever DosOpenQueue is called, and decremented whenever DosCloseQueue is called. If the requesting process is a client, and the access count equals 0, DosCloseQueue ends the client's access to the queue, but the queue itself is not affected. If the access count does not equal 0, the count is decremented, but the process retains access to the queue. If the requesting process is the server, DosCloseQueue purges any outstanding elements from the queue and deletes the queue regardless of the access count; client processes that still have the queue open receive ERROR_QUE_INVALID_HANDLE on their next request. ═══ 7. Semaphores ═══ Communication between processes is valuable in a multitasking operating system to enable concurrent processes to work together. Semaphores are one of three forms of interprocess communication (IPC), the other forms of IPC being pipes and queues. This chapter describes how to create and use semaphores. Semaphores enable an application to signal the completion of tasks and control access to resources that are shared between more than one thread or process. The following topics are related to the information in this chapter:  Memory (shared memory)  Program execution and control  Pipes  Queues ═══ 7.1. About Semaphores ═══ Semaphores signal the beginning or ending of an operation and provide mutually exclusive ownership of resources. Typically, semaphores are used to prevent more than one process or thread within a process from accessing a resource, such as shared memory, at the same time. Semaphores are defined by OS/2 and reside in an internal memory buffer. They are divided into three types, according to the functionality they provide:  Event semaphores enable a thread to notify waiting threads that an event has occurred. The waiting threads then resume execution, performing operations that are dependent on the completion of the signaled event.  Mutual exclusion (mutex) semaphores enable threads to serialize their access to shared resources. That is, ownership of a mutex semaphore is used by cooperating threads as a prerequisite for performing operations on a resource. (Threads cooperate by using the mutex semaphore functions to ensure that access to the resource is mutually exclusive.)  Multiple wait (muxwait) semaphores enable threads to wait either for multiple events to occur, or for multiple resources to become available. Alternatively, a flag can be set so that a thread waits for any one of multiple events to occur, or for any one of multiple resources to become available. ═══ 7.1.1. Event Semaphores ═══ An event semaphore provides a signaling mechanism among threads or processes, ensuring that events occur in the desired sequence. Event semaphores are used by one thread to signal other threads that an event has occurred. An application can use this type of semaphore to block a thread or process until the event has occurred. An event semaphore has two states, reset and posted. When an event semaphore is in the reset state, OS/2 blocks any thread or process that is waiting on the semaphore. When an event semaphore is in the posted state, all threads or processes waiting on the semaphore resume execution. For example, assume thread 1 is allocating a shared memory object and threads 2 and 3 must wait for the memory to be allocated before they attempt to examine its contents. Before thread 1 allocates the memory, it creates an event semaphore, specifying the initial state of the semaphore as reset. (If the event semaphore has already been created, thread 1 simply resets the semaphore.) Threads 2 and 3 use DosWaitEventSem to wait for the semaphore to signal that the event, in this case the allocation and preparation of the shared memory object, has been completed. Because the semaphore was reset by thread 1, threads 2 and 3 are blocked when they call DosWaitEventSem. After thread 1 has finished allocating and placing data in the shared memory object, it signals the completion of its task by posting the event semaphore. The posting of the event semaphore unblocks threads 2 and 3, enabling them to resume execution. They can then proceed to examine the contents of the allocated memory. In the example above, one thread controls the resetting and posting of the event semaphore, while other threads merely wait on the semaphore. Another approach could be for an application or thread to reset an event semaphore, then block itself on that semaphore. At a later time, another application or thread would post the event semaphore, unblocking the first thread. ═══ 7.1.2. Mutual Exclusion (Mutex) Semaphores ═══ A mutual exclusion (mutex) semaphore protects resources (such as files, data in memory, and peripheral devices) from simultaneous access by several processes. Mutex semaphores enable threads to serialize their access to resources. It does so by preventing the processes from concurrently executing the sections of code through which access is made. These sections of code are called critical sections. For example, a mutex semaphore could be used to prevent two or more threads from simultaneously writing to the same file on a disk. Before a thread can execute a mutex-protected critical section, it must request and receive ownership of the mutex semaphore. Only the thread that has gained ownership of the mutex semaphore is permitted to perform operations on the protected resource. Only one thread at a time can own the mutex semaphore, and the owner thread retains ownership until it finishes executing its critical section. When finished, the owner thread releases the mutex semaphore, enabling another thread to become the owner. When a thread requests ownership of a mutex semaphore that is already owned, OS/2 blocks the thread. When more than one thread requests ownership of the same semaphore, OS/2 queues the requests and grants subsequent ownership based on the thread's priority and the order in which the requests were received. If more than one thread is blocked on a DosRequestMutexSem request, then ownership is given to the thread that has the highest priority level. If more than one of the waiting threads have the same priority level, then FIFO ordering is used to determine which thread is unblocked and given ownership of the semaphore. For example, both thread 1 and thread 2 must write information to the same disk file. Thread 1 claims ownership of an agreed-upon mutex semaphore and starts writing its information to the file. If thread 2 also requests ownership of the semaphore, it will be blocked. When thread 1 has finished writing to the file, it releases the semaphore. OS/2 then unblocks thread 2 and designates it as the new owner of the semaphore so that it can write to the file. During process termination, after delivery of process-termination exceptions and unwind exceptions, if any threads in the process aside from Thread 1 (the main thread) own a mutex semaphore, ownership of the semaphore (and therefore, the shared resource) passes to Thread 1. This gives Thread 1 a last chance to clean up the semaphore and the shared resource before the process ends. If Thread 1 ends without releasing the semaphore, all threads that are currently waiting on ownership of the semaphore will be unblocked with the SEM_OWNER_DIED return code. Any thread that attempts to open it or request ownership of the semaphore will receive a SEM_OWNER_DIED return code. The recommended way to clean up semaphores, and other resources, is for each thread, especially Thread 1, to have an exception handler to handle clean up during process termination (the XCPT_PROCESS_TERMINATE or XCPT_ASYNC_PROCESS_TERMINATE exceptions). When it is not possible to register an exception handler for a thread, (a DLL, for example, must de-register its exception handlers when it returns control to the thread that called it), you should add a clean up routine to the exit list of the process. ═══ 7.1.3. Multiple Wait (Muxwait) Semaphores ═══ A multiple wait (muxwait) semaphore enables a thread to wait on several event or mutex semaphores simultaneously. A muxwait semaphore is a compound semaphore that consists of a list of up to 64 event semaphores or mutex semaphores (the two types cannot be mixed). A flag is set when the muxwait semaphore is created to enable threads to use the semaphore in either of two ways:  Threads can wait for all of the mutex semaphores to be released, or for all of the event semaphores to be posted.  Threads can wait for any one of the mutex semaphores in the list to be released, or for any one of the event semaphores in the list to be posted. Depending on the value of the flag, a muxwait semaphore is said to have cleared when either any or all of the semaphores in the muxwait list have been posted or released. For example, suppose a thread requires access to several regions of shared memory at the same time. OS/2 blocks the thread until the thread acquires ownership of all the mutex semaphores protecting the shared regions. The thread can then access all the memory regions. Meanwhile, OS/2 prevents access by other threads. ═══ 7.1.4. Named and Anonymous Semaphores ═══ A semaphore can be either named or anonymous. A named semaphore is always shared; that is, it is always available to any process that knows the name. An anonymous semaphore can be either private to a process or shared among processes, depending on whether the application includes the DC_SEM_SHARED flag in the function that creates the semaphore. A semaphore intended for use solely among threads of the same process can be anonymous and private. OS/2 creates a named semaphore when an application specifies a name in the function that creates the semaphore. The name must have the following form: \SEM32\SemName The "\SEM32\" is required, though it need not be uppercase. The semaphore name must conform to the rules for OS/2 file names, although no actual file is created for the semaphore. If the application does not specify a name in the function that creates the semaphore, OS/2 creates an anonymous semaphore. OS/2 permits a system-wide maximum of 65536 (64K) shared semaphores. In addition, each process can use up to 65536 (64K) private semaphores. A shared muxwait semaphore must contain either all shared event semaphores or all shared mutex semaphores. However, a private muxwait semaphore can contain a combination of shared and private event or mutex semaphores. OS/2 generates a unique handle when it creates a semaphore. Processes must obtain this handle before they can access the semaphore. A semaphore's handle is always available to the process that created the semaphore. A process can obtain the handle of a named semaphore created in another process by using the appropriate semaphore-opening function. A process that requires access to an anonymous shared semaphore that was created in another process must obtain the handle of the semaphore through some other form of interprocess communication, such as a pipe or a queue. ═══ 7.1.5. Semaphore Management ═══ After one process creates a semaphore, threads in other processes must open the semaphore before they can access it. (Creating a semaphore automatically opens it for the creating process.) The open operation ensures that the process is a valid user of the semaphore. OS/2 keeps track of the number of open operations that each process performs on a semaphore. A process can have up to 65535 (64K - 1) open operations performed on a semaphore at any one time. If a process finishes using a semaphore and will not use it again, the process should close the semaphore so that OS/2 can free the memory the semaphore is using. OS/2 returns the ERROR_SEM_BUSY error value if a thread tries to close a semaphore that has another thread in the same process still waiting for it. If a process terminates with open semaphores, OS/2 automatically closes the semaphores for that process. Semaphores reside in a memory buffer rather than a disk file. Therefore, when the last process that has a semaphore open exits or closes that semaphore, OS/2 frees the associated handle or name. When an application calls a function that causes a thread to wait on a semaphore, the application can specify the amount of time for the thread to wait. When the interval elapses without the semaphore being posted or released, the function returns the ERROR_TIMEOUT error value and the thread continues running. The application can provide a specific time-out value in milliseconds, or it can specify either the SEM_INDEFINITE_WAIT or the SEM_IMMEDIATE_RETURN flag. If a thread is interrupted while it is waiting on a semaphore, the ERROR_INTERRUPT error value is returned to the caller. ═══ 7.2. Using Event Semaphores ═══ An application can use an event semaphore to trigger execution of other processes. This is useful if, for example, one process provides data to many other processes. Using an event semaphore frees the other process from the trouble of polling to determine when new data is available. Note: In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred. ═══ 7.2.1. Creating an Event Semaphore ═══ Processes create an event semaphore by using DosCreateEventSem. The process that controls the event or resource is usually the one that creates the semaphore, but it does not have to be. Threads in the process that creates the semaphore do not have to open the semaphore before using it. DosCreateEventSem obtains access to the semaphore for the calling process and its threads. Threads in other processes must call DosOpenEventSem to open the semaphore before they can use it. Event semaphores can be defined as either private or shared:  Private semaphores are always unnamed and are therefore always identified by their handles. They can be used only by threads within a single process.  Shared semaphores can be either named or unnamed. If named, they can be opened using either the name or the handle. The handle returned by DosOpenEventSem is then used to identify the semaphore for all other functions. Semaphore names must include the prefix \SEM32\ and must conform to file system naming conventions. Shared semaphores can be used by threads in multiple processes. In the following code fragment, the controlling process creates a named event semaphore and posts the semaphore after writing data to a shared file: #define INCL_DOSSEMAPHORES /* Semaphore values */ #include HEV hevWriteEvent; DosCreateEventSem("\\sem32\\wrtevent", /* Named-shared semaphore */ &hevWriteEvent, 0, FALSE); /* Initially reset */ . . /* Write data to shared file. */ . . DosPostEventSem(hevWriteEvent); /* Posts the event */ . . /* Continue execution. */ . There is a system-wide limit of 65536 (64K) shared semaphores (including mutex, event, and muxwait semaphores); in addition, each process can have up to 65536 (64K) private semaphores. When an event semaphore is created, a flag is used to specify the initial state of the event semaphore, either reset or posted. If the initial state is reset, a thread that calls DosWaitEventSem will be blocked until a process that has access to the semaphore uses DosPostEventSem to post the event semaphore. If the initial state is posted, then a thread that calls DosWaitEventSem will return immediately to continue its execution. If the thread calling DosWaitEventSem is not in the process that created the semaphore, the thread must open the semaphore with DosOpenEventSem before calling DosWaitEventSem. OS/2 maintains a usage count for each semaphore. DosCreateEventSem initializes the usage count to 1. Thereafter, each call to DosOpenEventSem increments the count, and each call to DosCloseEventSem decrements it. ═══ 7.2.2. Opening an Event Semaphore ═══ When a process creates an event semaphore, all of the threads that belong to the process have immediate access to the semaphore. Threads in other processes must open the semaphore by calling DosOpenEventSem before they can use the semaphore in any other event semaphore function. The following code fragment shows how processes can open an event semaphore that was created in a different process and then wait for the event to be posted: #define INCL_DOSSEMAPHORES /* Semaphore values */ #include HEV hevEventHandle = 0; /* Must be 0 because we are opening */ /* the semaphore by name */ DosOpenEventSem("\\sem32\\wrtevent", &hevEventHandle); DosWaitEventSem(hevEventHandle, SEM_INDEFINITE_WAIT); /* Waits until event is posted */ . . /* Read from file when event is posted. */ . Applications can open an event semaphore by name or by handle. If the name is used to open the semaphore, as in the code fragment above, the handle parameter must be 0. If the handle is used to open the semaphore, the name parameter must be NULL. Access to semaphores is on a per-process basis. Therefore, a semaphore that has been opened by one thread in a process is open to all other threads in that process as well. DosOpenEventSem merely provides access to an event semaphore. In order to wait for an event semaphore to be posted, a thread must call DosWaitEventSem. In order to post or reset an open event semaphore, a thread uses DosPostEventSem or DosResetEventSem respectively. When a process no longer requires access to an event semaphore, it closes the semaphore by calling DosCloseEventSem. If a process ends without closing an open semaphore, the semaphore is closed by OS/2. Each call to DosOpenEventSem increments the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is decremented by each call to DosCloseEventSem. When the usage count reaches 0, the semaphore is deleted by OS/2. Calls to DosOpenEventSem and DosCloseEventSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned. ═══ 7.2.3. Closing an Event Semaphore ═══ When a process no longer requires access to an event semaphore, it closes the semaphore by calling DosCloseEventSem. The following code fragment closes an event semaphore. Assume that the handle of the semaphore has been placed into HEV already. #define INCL_DOSSEMAPHORES /* Semaphore values */ #include #include HEV hev; /* Event semaphore handle */ APIRET ulrc; /* Return code */ ulrc = DosCloseEventSem(hev); if (ulrc != 0) { printf("DosCloseEventSem error: return code = %ld", ulrc); return; } Calls to DosOpenEventSem and DosCloseEventSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned. If a process ends without closing an open semaphore, the semaphore is closed by OS/2. Each call to DosCloseEventSem decrements the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is incremented by each call to DosOpenEventSem. When the usage count reaches 0, the semaphore is deleted from OS/2. The call to DosCloseEventSem that decrements the usage count to 0 and causes the semaphore to be deleted is referred to as the final close. If a thread attempts to perform the final close for a semaphore while another thread in the same process is still waiting for it, ERROR_SEM_BUSY is returned. ═══ 7.2.4. Resetting an Event Semaphore ═══ DosResetEventSem resets an event semaphore if it is not already reset, and returns the number of times the semaphore was posted since it was last reset. All threads that subsequently call DosWaitEventSem for this semaphore will be blocked. Any thread belonging to the process that created the event semaphore can change the state of the semaphore to reset by calling DosResetEventSem. Threads in other processes can also call DosResetEventSem, but they must first gain access to the semaphore by calling DosOpenEventSem. When an event semaphore is in the reset state, any thread that calls DosWaitEventSem to wait for the semaphore will be blocked. When the event semaphore is posted, all of the threads that are waiting for the semaphore are released to continue execution. The following code fragment resets an event semaphore. Assume that the handle of the semaphore has been placed into HEV already. #define INCL_DOSSEMAPHORES /* Semaphore values */ #include #include HEV hev; /* Event semaphore handle */ ULONG ulPostCt; /* Post count for the event semaphore (returned) */ APIRET ulrc; /* Return code */ ulrc = DosResetEventSem(hev, &ulPostCt); if (ulrc != 0) { printf("DosResetEventSem error: return code = %ld", ulrc); return; } DosResetEventSem returns the post count of the event semaphore and resets the post count to 0. The post count is the number of times the semaphore has been posted (using DosPostEventSem) since the last time the semaphore was in the reset state. (An event semaphore can be reset when it is created, as well as by calling DosResetEventSem.) The post count can also be obtained by calling DosQueryEventSem. If the event semaphore is already reset when DosResetEventSem is called, ERROR_ALREADY_RESET is returned, along with a post count of 0. The semaphore is not reset a second time. ═══ 7.2.5. Posting an Event Semaphore ═══ DosPostEventSem posts the semaphore, if it is not already posted, and increments the post count. All threads that have called DosWaitEventSem for this semaphore are unblocked and resume execution. Threads that call DosWaitEventSem after the event semaphore has been posted and before the next time it is reset, will return immediately from a call to DosWaitEventSem and continue execution. If the semaphore is subsequently reset, threads that call DosWaitEventSem will again be blocked. Any thread in the process that created an event semaphore can post the semaphore by calling DosPostEventSem. Threads in other processes can also call DosPostEventSem, but they must first gain access to the semaphore by calling DosOpenEventSem. The following code fragment posts a system event semaphore. Assume that the handle of the semaphore has been placed into HEV already. #define INCL_DOSSEMAPHORES /* Semaphore values */ #include #include HEV hev; /* Event semaphore handle */ APIRET ulrc; /* Return code */ ulrc = DosPostEventSem(hev); if (ulrc != 0) { printf("DosPostEventSem error: return code = %ld", ulrc); return; } OS/2 maintains a post count for each event semaphore. The post count is the number of times the semaphore has been posted (with DosPostEventSem) since the last time the semaphore was in the reset state. If the event semaphore is reset when DosPostEventSem is called, the semaphore is posted and the post count is set to 1. If the event semaphore is already posted when DosPostEventSem is called, the post count is incremented, and ERROR_ALREADY_POSTED is returned to the calling thread. The post count is returned as output by DosResetEventSem; it can also be obtained by calling DosQueryEventSem. The maximum number of times an event semaphore can be posted is 65535. The value of the post count cannot exceed 65535. If an attempt is made to exceed this number, DosPostEventSem returns ERROR_TOO_MANY_POSTS. ═══ 7.2.6. Waiting for an Event Semaphore ═══ Any thread in the process that created an event semaphore can wait for the semaphore to be posted by calling DosWaitEventSem. Threads in other processes can also call DosWaitEventSem, but they must first gain access to the semaphore by calling DosOpenEventSem. If the semaphore is already posted when DosWaitEventSem is called, the function returns immediately, and the thread continues to run. Otherwise, the thread is blocked until the semaphore is posted. The following code fragment causes the calling thread to wait until the specified event semaphore is posted. Assume that the handle of the semaphore has been placed into HEV already. ulTimeout is the number of milliseconds that the calling thread will wait for the event semaphore to be posted. If the specified event semaphore is not posted during this time interval, the request times out. #define INCL_DOSSEMAPHORES /* Semaphore values */ #define INCL_DOSERRORS /* error codes */ #include #include HEV hev; /* Event semaphore handle */ ULONG ulTimeout; /* Number of milliseconds to wait */ APIRET ulrc; /* Return code */ ulTimeout = 60000; /* Wait for a maximum of 1 minute */ ulrc = DosWaitEventSem(hev, ulTimeout); if (ulrc == ERROR_TIMEOUT) { printf("DosWaitEventSem call timed out"); return; } if (ulrc == ERROR_INTERRUPT) { printf("DosWaitEventSem call was interrupted"); return; } if (ulrc != 0) { printf("DosWaitEventSem error: return code = %ld", ulrc); return; } If the time limit specified in ulTimeout is reached before the semaphore is posted, ERROR_TIMEOUT is returned. If the waiting period is interrupted for some reason before the semaphore is posted, ERROR_INTERRUPT is returned. If SEM_IMMEDIATE_RETURN is specified for the time limit, DosWaitEventSem returns to the calling thread immediately. If SEM_INDEFINITE_WAIT is specified for the time limit, the thread waits indefinitely. Unlike multiple event semaphores in a muxwait list, which are level-triggered, single event semaphores are edge-triggered. This means that if an event semaphore is posted and then reset before a waiting thread gets a chance to run, the semaphore is considered to be posted for the rest of that thread's waiting period; the thread does not have to wait for the semaphore to be posted again. ═══ 7.2.7. Querying an Event Semaphore ═══ DosQueryEventSem returns the current post count of a semaphore. The post count is the number of times that the semaphore has been posted (with DosPostEventSem) since the last time the semaphore was reset. A count of 0 indicates that the semaphore is in the reset state; therefore, OS/2 will block any threads that call DosWaitEventSem to wait on the semaphore. Any thread in the process that created an event semaphore can obtain the post count for the semaphore by calling DosQueryEventSem. Threads in other processes can also call DosQueryEventSem, but they must first gain access to the semaphore by calling DosOpenEventSem. The following code fragment retrieves the post count for an event semaphore. Assume that the handle of the semaphore has been placed into HEV already. #define INCL_DOSSEMAPHORES /* Semaphore values */ #include #include HEV hev; /* Event semaphore handle */ ULONG ulPostCt; /* Current post count for the semaphore (returned) */ APIRET ulrc; /* Return code */ ulrc = DosQueryEventSem(hev, &ulPostCt); if (ulrc != 0) { printf("DosQueryEventSem error: return code = %ld", ulrc); return; } If the specified event semaphore does not exist, ERROR_INVALID_HANDLE is returned. ═══ 7.3. Using Mutex Semaphores ═══ An application can use a mutual exclusion (mutex) semaphore to protect a shared resource from simultaneous access by multiple threads or processes. For example, if several processes must write to the same disk file, the mutex semaphore ensures that only one process at a time writes to the file. ═══ 7.3.1. Creating a Mutex Semaphore ═══ Mutex semaphores are created by calling DosCreateMutexSem. This function also opens the semaphore for the calling process and its threads. When a mutex semaphore is created, a flag is set to specify the initial state of the semaphore, owned or unowned. If the semaphore is owned by a thread, other threads requesting the semaphore are blocked. If the semaphore is unowned-not owned by any thread- then any thread requesting ownership will be granted ownership immediately. If the calling thread sets the initial state to owned, it owns the semaphore as soon as OS/2 creates the semaphore and can proceed to access the resource that the semaphore was created to protect. If the semaphore is unowned, any thread in the creating process can subsequently request ownership of the semaphore by calling DosRequestMutexSem. Threads in other processes can gain ownership of the semaphore, but they must call DosOpenMutexSem to acquire access to the semaphore before they can call DosRequestMutexSem. Mutex semaphores can be defined as either private or shared.  Private semaphores are always unnamed and are therefore identified by their handles. They can be used only by threads within a single process.  Shared semaphores can be either named or unnamed. If named, they can be opened using either the name or the handle. The handle returned by DosOpenMutexSem is then used to identify the semaphore for all other functions. Semaphore names must include the prefix \SEM32\ and must conform to file system naming conventions. Shared semaphores can be used by threads in multiple processes. The following code fragment creates a mutex semaphore: #define INCL_DOSSEMAPHORES /* Semaphore values */ #include HMTX hmtxProtFile; DosCreateMutexSem("\\sem32\\ProtFile", /* Named-shared semaphore */ &hmtxProtFile, 0, FALSE); /* Initially unowned */ . . /* Get data to write to shared file. */ . There is a system-wide limit of 65536 shared semaphores (including mutex, event, and muxwait semaphores); in addition, each process can have up to 65536 private semaphores. OS/2 maintains a usage count for each semaphore. DosCreateMutexSem initializes the usage count to 1. Thereafter, each call to DosOpenMutexSem increments the count, and each call to DosCloseMutexSem decrements it. ═══ 7.3.2. Opening a Mutex Semaphore ═══ All of the threads belonging to the process that creates a mutex semaphore have immediate access to the semaphore. Threads in other processes must request access to the semaphore by calling DosOpenMutexSem before they can use the semaphore in other mutex semaphore functions. Access to system resources is granted on a per-process basis. Therefore, a semaphore that has been opened by one thread in a process is open to all other threads in that process as well. DosOpenMutexSem merely provides access to a mutex semaphore. To request ownership of a mutex semaphore, a thread must call DosRequestMutexSem. When a process no longer requires access to a mutex semaphore, it should close the semaphore by calling DosCloseMutexSem. However, if a process ends without closing an open semaphore, the semaphore is closed by OS/2. Each call to DosOpenMutexSem. increments the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is decremented by each call to DosCloseMutexSem. When the usage count reaches 0, the semaphore is deleted by the system. Calls to DosOpenMutexSem and DosCloseMutexSem. can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned. If a process ends without releasing a mutex semaphore that it owns, any other thread that subsequently tries to open the semaphore will receive ERROR_SEM_OWNER_DIED. This return code indicates that the owning process ended abnormally, leaving the protected resource in an indeterminate state. However, the semaphore is still opened for the calling thread, enabling the thread to call DosQueryMutexSem to find out which process ended without releasing the semaphore. The thread can then take appropriate action concerning the semaphore and the protected resource. ═══ 7.3.3. Requesting a Mutex Semaphore ═══ In order to access a shared resource, a process must own the mutex semaphore that is protecting the shared resource. Ownership is obtained by first opening the mutex semaphore with DosOpenMutexSem, then using DosRequestMutexSem to request ownership of the semaphore. If another process already owns the semaphore, the requesting process is blocked. If the semaphore is not owned, OS/2 grants ownership to the requesting process and the process can access the shared resource. When the process is finished using the shared resource, it uses DosReleaseMutexSem to relinquish its ownership of the semaphore, thereby enabling another process to gain ownership. A process can gain ownership of a mutex semaphore in three ways: 1. The thread that creates a mutex semaphore can designate itself as the owner by setting a flag when it calls DosCreateMutexSem. 2. Any thread in the process that created the semaphore can request ownership by calling DosRequestMutexSem. 3. A thread in another process must request access to the semaphore with DosOpenMutexSem before it can call DosRequestMutexSem. Note that ownership of a mutex semaphore is given only to the requesting thread; it is not shared by other threads in the same process. If a mutex semaphore is unowned, DosRequestMutexSem sets it as owned and returns immediately to the caller. If the semaphore is already owned, the calling thread is blocked until either the owning thread calls DosReleaseMutexSem to release the semaphore, or a specified time limit is reached. The following code fragment shows how a process opens a mutex semaphore, requests it, and, after writing to the shared file, releases and closes the semaphore: #define INCL_DOSSEMAPHORES /* Semaphore values */ #include HMTX hmtxProtFile; DosOpenMutexSem("\\sem32\\ProtFile", &hmtxProtFile); /* Opens for this process */ DosRequestMutexSem(hmtxProtFile, 5000); /* Returns in 5 seconds if */ . /* Ownership not obtained */ . . /* Write data to shared file */ . . DosReleaseMutexSem(hmtxProtFile); /* Releases ownership */ . . /* Continue execution */ . . DosCloseMutexSem(hmtxProtFile); /* Finished with shared file */ If more than one thread is blocked on a DosRequestMutexSem request, the thread with the highest priority level is the first to be unblocked and given ownership of the semaphore. If more than 1 of the waiting threads have the same priority level, then FIFO ordering is used to determine which thread is unblocked and given ownership. The time-out parameter (5000 milliseconds in the example above) places a limit on the amount of time a thread blocks on a DosRequestMutexSem request. If the time limit is reached before the thread gains ownership of the semaphore, ERROR_TIMEOUT is returned. If SEM_IMMEDIATE_RETURN is specified for the time limit, DosRequestMutexSem returns without blocking the thread. The thread can then perform other operations and call DosRequestMutexSem again later if it still requires access to the protected resource. If SEM_INDEFINITE_WAIT is specified for the time limit, the thread waits indefinitely. If the thread is unblocked by an external event while it is waiting for the mutex semaphore (as when a No Wait I/O request has just been completed), ERROR_INTERRUPT is returned to the caller. In addition to the usage count that OS/2 maintains for all semaphores, OS/2 maintains a request count for each mutex semaphore. Each call to DosRequestMutexSem increments the count, and each call to DosReleaseMutexSem decrements it. Calls to DosRequestMutexSem and DosReleaseMutexSem can be nested, but the request count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_SEM_REQUESTS is returned. When calls to DosRequestMutexSem and DosReleaseMutexSem are nested, a call to DosReleaseMutexSem merely decrements the request count for the semaphore; the semaphore is not actually released to another thread until its request count is 0. If a process ends while it owns a mutex semaphore, all of the currently blocked DosRequestMutexSem requests, as well as any future requests for the semaphore, return ERROR_SEM_OWNER_DIED. This return code indicates that the owning process ended abnormally, leaving the protected resource in an indeterminate state. An application that receives this error should close the mutex semaphore (so that it can be deleted from OS/2), because it is no longer valid. Appropriate action should also be taken concerning the protected resource. ═══ 7.3.4. Releasing a Mutex Semaphore ═══ A thread can release ownership of a mutex semaphore by calling DosReleaseMutexSem. Each call to DosReleaseMutexSem decrements the request count that is maintained for the semaphore by OS/2. Each call to DosRequestMutexSem increments the count. The following code fragment relinquishes ownership of a mutex semaphore. Assume that the handle of the semaphore has been placed into hmtx already. #define INCL_DOSSEMAPHORES /* Semaphore values */ #include #include HMTX hmtx; /* Mutex semaphore handle */ APIRET ulrc; /* Return code */ ulrc = DosReleaseMutexSem(hmtx); if (ulrc != 0) { printf("DosReleaseMutexSem error: return code = %ld", ulrc); return; } Calls to DosRequestMutexSem and DosReleaseMutexSem can be nested, but the request count cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_SEM_REQUESTS is returned. When calls to DosRequestMutexSem and DosReleaseMutexSem are nested, a call to DosReleaseMutexSem merely decrements the request count for the semaphore; the semaphore is not actually released to another thread until its request count is 0. ═══ 7.3.5. Closing a Mutex Semaphore ═══ When a process no longer requires access to a mutex semaphore, it can close the semaphore by calling DosCloseMutexSem. However, if a process ends without closing an open semaphore, the semaphore is closed by OS/2. The following code fragment closes a mutex semaphore. Assume that the handle of the semaphore has been placed into hmtx already. #define INCL_DOSSEMAPHORES /* Semaphore values */ #include #include HMTX hmtx; /* Mutex semaphore handle */ APIRET ulrc; /* Return code */ ulrc = DosCloseMutexSem(hmtx); if (ulrc != 0) { printf("DosCloseMutexSem error: return code = %ld", ulrc); return; } Each call to DosCloseMutexSem decrements the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is incremented by each call to DosOpenMutexSem. When the usage count reaches 0, the semaphore is deleted by OS/2. The call to DosCloseMutexSem that decrements the usage count to 0 and causes the semaphore to be deleted is referred to as the final close. The final close will not succeed if either of the following conditions exists:  The semaphore is owned by another thread in the same process.  Another thread in the same process is still blocked on a DosRequestMutexSem request for the semaphore. For both conditions, ERROR_SEM_BUSY is returned. ERROR_SEM_BUSY is also returned if a thread tries to close a mutex semaphore that it still owns. The thread must first relinquish ownership of the semaphore by calling DosReleaseMutexSem. Calls to DosOpenMutexSem and DosCloseMutexSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned. ═══ 7.3.6. Querying a Mutex Semaphore ═══ An application can use DosQueryMutexSem to determine the current owner of a mutex semaphore and to obtain a count of the number of requests on it. If the mutex semaphore is not owned, the request count is 0. Any thread in the process that created a mutex semaphore can obtain information about the semaphore by calling DosQueryMutexSem. Threads in other processes can also call DosQueryMutexSem, but they must first gain access to the semaphore by calling DosOpenMutexSem. If the mutex semaphore exists and is owned, DosQueryMutexSem returns the process and thread identifications of the owner, as well as the request count for the semaphore. The request count is the number of DosRequestMutexSem calls minus the number of DosReleaseMutexSem calls that have been made for the semaphore by the owning thread. If DosQueryMutexSem returns a request count of 0, the mutex semaphore is unowned. If the owning process ended without calling DosCloseMutexSem, then ERROR_SEM_OWNER_DIED is returned, and the output parameters contain information about the ended owning process. ═══ 7.4. Using Muxwait Semaphores ═══ A process that requires exclusive use of several shared resources at once can use a multiple wait (muxwait) semaphore to obtain ownership of all the mutex semaphores protecting the shared resources. A process can also use a muxwait semaphore to wait on a group of event semaphores so that the process continues running whenever events of interest occur. A muxwait semaphore can refer to up to 64 event or mutex semaphores. An application cannot refer to event and mutex semaphores in a single muxwait semaphore, or include a muxwait semaphore in another muxwait semaphore. ═══ 7.4.1. Creating a Muxwait Semaphore ═══ DosCreateMuxWaitSem is used to create muxwait semaphores. This function also opens (obtains access to) the semaphore for the calling process and its threads. Threads in other processes must call DosOpenMuxWaitSem to open the semaphore before they can use it in any other muxwait semaphore function. All the semaphores in the muxwait list must be created and opened before the muxwait list can be created. The following code fragment creates five event semaphores and a corresponding array of semaphore records. The array is used to specify the semaphores included in the muxwait semaphore in the subsequent call to DosCreateMuxWaitSem. #define INCL_DOSSEMAPHORES /* DOS semaphore values */ #define INCL_DOSERRORS /* DOS error values */ #include #include int main(VOID) { HMUX hmuxHandAny = NULLHANDLE; /* Muxwait handle */ HEV hev[5] = {0}; /* Event semaphores */ SEMRECORD apsr[5] = {{0}}; /* Semaphore records */ APIRET ulrc = NO_ERROR; /* Return code */ ULONG ulLoop = 0; /* Loop count */ ULONG ulSem = 0; for (ulLoop = 0; ulLoop < 5; ulLoop++) { ulrc = DosCreateEventSem((PSZ) NULL, &hev[ulLoop], 0, FALSE); if (ulrc != NO_ERROR) { printf("DosCreateEventSem error: return code = %u\n", ulrc); return 1; } apsr[ulLoop].hsemCur = (HSEM) hev[ulLoop], apsr[ulLoop].ulUser = 0; } /* end for */ ulrc = DosCreateMuxWaitSem((PSZ) NULL, &hmuxHandAny, 5L, /* Number of semaphores in list */ apsr, /* Semaphore list */ DCMW_WAIT_ANY); /* Wait for any semaphore */ if (ulrc != NO_ERROR) { printf("DosCreateMuxWaitSem error: return code = %u\n", ulrc); return 1; } ulrc = DosWaitMuxWaitSem(hmuxHandAny, SEM_IMMEDIATE_RETURN, &ulSem); /* No semaphores have been posted, so we should see a timeout below... */ if (ulrc != ERROR_TIMEOUT) { printf("DosWaitMuxWaitSem error: return code = %u\n", ulrc); return 1; } ulrc = DosDeleteMuxWaitSem(hmuxHandAny, apsr[0].hsemCur); if (ulrc != NO_ERROR) { printf("DosDeleteMuxWaitSem error: return code = %u\n", ulrc); return 1; } ulrc = DosCloseMuxWaitSem(hmuxHandAny); if (ulrc != NO_ERROR) { printf("DosCloseMuxWaitSem error: return code = %u\n", ulrc); return 1; } return NO_ERROR; } Muxwait semaphores can be defined as either private or shared:  Private semaphores are always unnamed and are therefore always identified by their handles. They can be used only by threads within a single process.  Shared semaphores can be either named or unnamed. If named, they can be opened using either the name or the handle. The handle returned by DosOpenMuxWaitSem is then used to identify the semaphore for all other functions. Semaphore names must include the prefix \SEM32\ and must conform to file system naming conventions. Shared semaphores can be used by threads in multiple processes. There is a system-wide limit of 65536 (64K) shared semaphores (including mutex, event, and muxwait semaphores); in addition, each process can have up to 65536 (64K) private semaphores. The following conditions apply to the kinds of semaphores that can be included in a muxwait-semaphore list:  The list must contain either mutex semaphores or event semaphores. It cannot contain both at the same time and it cannot contain other muxwait semaphores.  If the muxwait semaphore is shared, then all the semaphores in the list must also be shared.  If the muxwait semaphore is private, then the semaphores in its list can be either private or shared. If any of these conditions is violated, ERROR_WRONG_TYPE is returned. The muxwait list can contain a maximum of 64 event semaphores or mutex semaphores. If an attempt is made to exceed this maximum, ERROR_TOO_MANY_SEMAPHORES is returned. If the owners of any of the mutex semaphores in the muxwait semaphore list have ended without releasing them, ERROR_SEM_OWNER_DIED is returned. The thread should call DosQueryMutexSem for each mutex semaphore in the muxwait-semaphore list so that it can determine which semaphores are in the Owner Died state. Each mutex semaphore that returns ERROR_SEM_OWNER_DIED from the query should be closed by calling DosCloseMutexSem. Also, because semaphore handles can be reused, the mutex semaphores that are closed must be deleted from the muxwait-semaphore list by calling DosDeleteMuxWaitSem. OS/2 maintains a usage count for each semaphore. DosCreateMuxWaitSem initializes the usage count to 1. Thereafter, each call to DosOpenMuxWaitSem increments the count, and each call to DosCloseMuxWaitSem decrements it. One parameter of this function is a pointer to an array of SEMRECORD data structures. Each data structure contains one semaphore record for each of the semaphores to be included in the muxwait semaphore. A semaphore record contains the handle and a programmer-defined identifier for that semaphore. ═══ 7.4.2. Opening a Muxwait Semaphore ═══ Processes other than the semaphore-creating process must use DosOpenMuxWaitSem to gain access to the muxwait semaphore before they can use the semaphore in any other muxwait semaphore function. All of the threads that belong to the process that creates the muxwait semaphore have immediate access to the semaphore. The following code fragment opens a system muxwait semaphore. #define INCL_DOSSEMAPHORES /* Semaphore values */ #include #include #include UCHAR ucName[40]; /* Semaphore name */ HMUX hmux; /* Muxwait semaphore handle */ APIRET ulrc; /* Return code */ strcpy(ucName, "\\SEM32\\MUXWAIT1"); /* Name of the system muxwait semaphore */ ulrc = DosOpenMuxWaitSem(ucName, &hmux); if (ulrc != 0) { printf("DosOpenMuxWaitSem error: return code = %ld", ulrc); return; } On successful return, hmux contains the handle of the system muxwait semaphore. Opening a muxwait semaphore does not open the semaphores in its muxwait list. A process must open each of the semaphores included in a muxwait semaphore before it opens the muxwait semaphore. Otherwise, DosOpenMuxWaitSem returns the ERROR_INVALID_HANDLE error value to the calling function. Access to semaphores is on a per-process basis. Therefore, a semaphore that has been opened by one thread in a process is open to all other threads in that process as well. Note that DosOpenMuxWaitSem merely provides access to a muxwait semaphore. In order to wait for a muxwait semaphore to clear, a thread must call DosWaitMuxWaitSem. When a process no longer requires access to a muxwait semaphore, it closes the semaphore by calling DosCloseMuxWaitSem. However, if a process ends without closing an open semaphore, the semaphore is closed by OS/2. Each call to DosOpenMuxWaitSem increments the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is decremented by each call to DosCloseMuxWaitSem. When the usage count reaches 0, the semaphore is deleted by OS/2. Calls to DosOpenMuxWaitSem and DosCloseMuxWaitSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned. Even if the owner of a mutex semaphore in a muxwait-semaphore list has ended without releasing the semaphore, the muxwait semaphore is still opened. Subsequent calls to the muxwait semaphore will return ERROR_SEM_OWNER_DIED. But because the process has opened the semaphore, it can then call DosQueryMuxWaitSem to identify all the mutex semaphores in the muxwait list. Next, the process can call DosQueryMutexSem for each mutex semaphore in the list to find out which ones are in the Owner Died state. Each mutex semaphore that returns ERROR_SEM_OWNER_DIED from the query should be closed by calling DosCloseMutexSem. Also, because semaphore handles can be reused, the mutex semaphores that are closed should be deleted from the muxwait-semaphore list by calling DosDeleteMuxWaitSem. ═══ 7.4.3. Closing a Muxwait Semaphore ═══ When a process no longer requires access to a muxwait semaphore, it closes the semaphore by calling DosCloseMuxWaitSem. However, if a process ends without closing an open semaphore, the semaphore is closed by OS/2. Each call to DosCloseMuxWaitSem decrements the usage count of the semaphore. This count is initialized to 1 when the semaphore is created and is incremented by each call to DosOpenMuxWaitSem. When the usage count reaches 0, the semaphore is deleted by OS/2. The call to DosCloseMuxWaitSem that decrements the usage count to 0 and causes the semaphore to be deleted is referred to as the final close. If a thread attempts to perform the final close for a semaphore while another thread in the same process is still waiting for it, ERROR_SEM_BUSY is returned. Calls to DosOpenMuxWaitSem and DosCloseMuxWaitSem can be nested, but the usage count for a semaphore cannot exceed 65535. If an attempt is made to exceed this number, ERROR_TOO_MANY_OPENS is returned. ═══ 7.4.4. Waiting for a Muxwait Semaphore ═══ A thread can wait on a muxwait semaphore by using DosWaitMuxWaitSem. Any thread in the process that created a muxwait semaphore can wait for the semaphore to clear by calling DosWaitMuxWaitSem. Threads in other processes can also call DosWaitMuxWaitSem, but they must first gain access to the semaphore by calling DosOpenMuxWaitSem. The following code fragment waits for a muxwait semaphore to clear. Assume that the handle of the semaphore has been placed into hmux already. ulTimeout is the number of milliseconds that the calling thread will wait for the muxwait semaphore to clear. If the specified muxwait semaphore is not cleared during this time interval, the request times out. #define INCL_DOSSEMAPHORES /* Semaphore values */ #include #include HMUX hmux; /* Muxwait semaphore handle */ ULONG ulTimeout; /* Number of milliseconds to wait */ ULONG ulUser; /* User field for the semaphore that was */ /* posted or released (returned) */ APIRET ulrc; /* Return code */ ulTimeout = 60000; /* Wait for a maximum of 1 minute */ ulrc = DosWaitMuxWaitSem(hmux, ulTimeout, &ulUser); if (ulrc == ERROR_TIMEOUT) { printf("DosWaitMuxWaitSem call timed out"); return; } if (ulrc == ERROR_INTERRUPT) { printf("DosWaitMuxWaitSem call was interrupted"); return; } if (ulrc != 0) { printf("DosWaitMuxWaitSem error: return code = %ld", ulrc); return; } On successful return, the ulUser variable contains the user identifier of the semaphore that caused the wait to terminate. If the caller had to wait for all the semaphores within the muxwait semaphore to clear, then the value corresponds to the last semaphore within the muxwait semaphore to clear. If the caller had to wait for any semaphore with the muxwait semaphore to clear, then the value corresponds to that semaphore. An application can use the DCMW_WAIT_ANY flag in DosCreateMuxWaitSem to block a thread until any one of the event or mutex semaphores included in the muxwait semaphore is posted or released. If the muxwait semaphore refers to mutex semaphores, the thread only gains ownership of the one mutex semaphore that was released. An application can use the DCMW_WAIT_ALL flag in DosCreateMuxWaitSem to block a thread until all of the event or mutex semaphores included in the muxwait semaphore are posted or released. If the muxwait semaphore refers to mutex semaphores, the thread does not gain ownership of any of the mutex semaphores until they are all released. When all are released, the thread becomes owner of all the mutex semaphores included in the muxwait semaphore. If the muxwait semaphore refers to event semaphores, the thread will not run until all of the event semaphores are in the posted state at the same time. This is because event semaphores in a muxwait list are level-triggered, unlike individual event semaphores, which are edge-triggered. For example, suppose that a thread is waiting for five event semaphores in a muxwait list to be posted. The first semaphore is posted and then reset. Next, the remaining semaphores are all posted, and they remain in the posted state. The thread that is waiting for the muxwait semaphore will not run until the first semaphore is posted again. If an application specifies the DCMW_WAIT_ANY flag when the semaphore is created, DosWaitMuxWaitSem returns the programmer-defined identifier of the semaphore that is subsequently posted or released. If an application specifies the DCMW_WAIT_ALL flag, DosWaitMuxWaitSem returns the programmer-defined identifier of the last semaphore that was posted or released. The ulTimeout parameter places a limit on the amount of time a thread blocks on a DosWaitMuxWaitSem request. If the time limit is reached before the semaphore has cleared, ERROR_TIMEOUT is returned. If SEM_IMMEDIATE_RETURN is specified as the time limit, DosWaitMuxWaitSem returns without blocking the thread. The thread can then go on to perform other operations and call DosWaitMuxWaitSem again later to wait for the event or mutex semaphores in the muxwait list to be posted or released. If a time limit of SEM_INDEFINITE_WAIT is specified, the thread waits (is blocked) indefinitely. If the thread is unblocked by an external event while it is waiting for the muxwait semaphore (as when a "no wait" I/O request has just been completed), DosWaitMuxWaitSem returns ERROR_INTERRUPT. When a thread is waiting for any one of the semaphores in a muxwait list to be posted or released, the semaphores are checked in the order in which they are defined in the list. Waiting for Multiple Event Semaphores The following information pertains only to muxwait semaphores that consist of multiple event semaphores: Unlike individual event semaphores, which are edge-triggered, event semaphores in a muxwait list are level-triggered. This means that if a thread is waiting for all of the event semaphores in the muxwait list, it will not run until all of the event semaphores are in the posted state at the same time. For example, a thread is waiting for five event semaphores in a muxwait list to be posted. The first semaphore is posted and then reset. Next, the remaining semaphores are all posted, and they remain in the posted state. The thread that is waiting for the muxwait semaphore will not run until the first semaphore is posted again. Waiting for Multiple Mutex Semaphores The following information pertains only to muxwait semaphores that consist of multiple mutex semaphores:  If a thread is waiting for all of the mutex semaphores in a muxwait list to be released, it does not receive ownership of any of the semaphores until all of the semaphores have been released.  If a thread is waiting for any one of the mutex semaphores in a muxwait list, then the thread gains ownership only of the first mutex semaphore that is released. The ownership of all other mutex semaphores in the muxwait list remains unchanged.  If two threads have the same priority, then a thread that is waiting for a mutex semaphore in a muxwait list takes precedence over a thread that has requested ownership of only the individual semaphore, provided all other mutex semaphores in the muxwait list have been released. For example, a mutex semaphore that is part of a muxwait semaphore is released. One thread has requested ownership of that single mutex semaphore, and another thread with the same priority is waiting for the muxwait semaphore that contains the same mutex semaphore. If all of the other mutex semaphores in the muxwait list are unowned and ready to be given to the muxwait semaphore, then the thread that is waiting for the muxwait semaphore will run first.  If the owners of any of the mutex semaphores in the muxwait semaphore list have ended without releasing them, ERROR_SEM_OWNER_DIED is returned. The thread must then call DosQueryMuxWaitSem to obtain the records of all the semaphores in the muxwait list. Next, the thread must call DosQueryMutexSem for each mutex semaphore in the muxwait-semaphore list so that it can determine which semaphores are in the Owner Died state. Each mutex semaphore that returns ERROR_SEM_OWNER_DIED from the query should be closed by calling DosCloseMutexSem. Also, because semaphore handles can be reused, the mutex semaphores that are closed should be deleted from the muxwait-semaphore list by calling DosDeleteMuxWaitSem.  If any of the mutex semaphores in the muxwait list are owned by the calling thread, ERROR_MUTEX_OWNED is returned. ═══ 7.4.5. Adding a Semaphore to a Muxwait List ═══ An application uses DosAddMuxWaitSem to add semaphores to a muxwait semaphore that has already been created, even while threads are waiting on the muxwait semaphore. Any thread in the process that created a muxwait semaphore can add a mutex semaphore or an event semaphore to the muxwait list by calling DosAddMuxWaitSem. Threads in other processes can also use this function, but they must first gain access to the semaphore by calling DosOpenMuxWaitSem. A maximum of 64 semaphores can be included in a muxwait-semaphore list. If an attempt is made to exceed this maximum, ERROR_TOO_MANY_SEMAPHORES is returned. All of the semaphores in a muxwait-semaphore list must be of the same type. That is, if a mutex semaphore is being added, then the other semaphores in the list must be mutex semaphores. If an event semaphore is being added, then the other semaphores in the list must be event semaphores. A shared muxwait semaphore can contain only shared semaphores in its list. A private muxwait semaphore can contain both private and shared semaphores. If any of these conditions is violated, ERROR_WRONG_TYPE is returned. If the semaphore is successfully added to the muxwait list, DosAddMuxWaitSem checks to see whether each thread that is waiting for the muxwait semaphore has the newly added semaphore open in its process. The muxwait semaphore is invalid for any waiting threads that do not have the newly added semaphore open in their process; these threads are unblocked with a return code of ERROR_INVALID_HANDLE. Any processes that opened the muxwait semaphore before the add operation and that do not have the new semaphore open, will have to open the new semaphore before making any further use of the muxwait semaphore. Any future calls concerning the muxwait semaphore by processes that do not have the new semaphore open will have ERROR_INVALID_HANDLE returned until the new semaphore is opened. A thread that receives a return code of ERROR_INVALID_HANDLE can take the following corrective action: 1. First, the thread can obtain the records of all the semaphores in the muxwait list by calling DosQueryMuxWaitSem. 2. Next, it can query each semaphore in the muxwait list, using DosQueryMutexSem or DosQueryEventSem, to find out which semaphore is not open to its process. 3. Finally, it can open the semaphores that are not open by calling DosOpenMutexSem or DosOpenEventSem. As soon as this semaphore is opened, the muxwait semaphore becomes valid again for the process, as long as no other changes have been made to the muxwait list to make it invalid. However, in order to successfully wait for the muxwait semaphore, the process must call DosWaitMuxWaitSem again. A semaphore must be open for a process before the process can add that semaphore to a muxwait semaphore. If it is not open and a thread is waiting on the muxwait semaphore, DosAddMuxWaitSem returns ERROR_INVALID_HANDLE to the process adding the new semaphore, and the waiting thread continues waiting. ═══ 7.4.6. Deleting a Semaphore from a Muxwait List ═══ An application can delete semaphores from a muxwait semaphore by using DosDeleteMuxWaitSem. Any thread in the process that created a muxwait semaphore can delete a mutex or event semaphore from the muxwait list by calling DosDeleteMuxWaitSem. Threads in other processes can also use this function, but they must first gain access to the semaphore by calling DosOpenMuxWaitSem. Semaphores can be deleted from the muxwait list even while threads are currently waiting for the semaphore. If the deleted semaphore is the only one in the muxwait list that has not yet been posted or released, then threads that are waiting for the muxwait semaphore are unblocked. Also, if the deleted semaphore happens to be the last one that a particular thread was waiting for, that thread is unblocked. Also, if the deleted semaphore is the last one in the muxwait list (that is, if the list is now empty), then all the threads that are waiting for the muxwait semaphore are unblocked. ═══ 7.4.7. Querying a Muxwait Semaphore ═══ Processes use DosQueryMuxWaitSem to obtain the semaph ore records for each of the semaphores included in the muxwait semaphore. Any thread in the process that created a muxwait semaphore can obtain information about the semaphores in the muxwait list by calling DosQueryMuxWaitSem. Threads in other processes can also use this function, but they must first gain access to the semaphore by calling DosOpenMuxWaitSem. An application must provide this function with an array in which to store the semaphore records. If the array is not large enough to hold all of the semaphore records that are in the muxwait list, then ERROR_PARAM_TOO_SMALL is returned, and the record-counting parameter of DosQueryMuxWaitSem will contain the number of semaphore records that are in the muxwait list. The calling thread can then allocate the correct amount of space and call DosQueryMuxWaitSem again with the correct amount of space for the list of records. If the owner of any mutex semaphore in the muxwait-semaphore list has ended without releasing the semaphore, the records of all the semaphores in the list are still returned, but DosQueryMuxWaitSem also returns ERROR_SEM_OWNER_DIED. The calling thread can call DosQueryMutexSem for each mutex semaphore in the muxwait-semaphore list so that it can determine which semaphores are in the Owner Died state. The process can then close the unowned mutex semaphores. Each mutex semaphore that returns ERROR_SEM_OWNER_DIED from the query should be closed by calling DosCloseMutexSem. Also, because semaphore handles can be reused, the mutex semaphores that are closed should be deleted from the muxwait-semaphore list by calling DosDeleteMuxWaitSem. If the specified muxwait semaphore does not exist, ERROR_INVALID_HANDLE is returned. ═══ 8. Timers ═══ This chapter describes how to create and use timers. Timers enable applications to time events by waiting for an interval to elapse or by waiting for a semaphore to be posted. The following topics are related to the information in this chapter:  Program execution and control  Semaphores ═══ 8.1. About Timers ═══ Because OS/2 is a multitasking system, an application cannot predict when it will lose execution control or how much time will elapse before control returns. A timer enables an application to suspend operation for a specific length of time, to block a thread until an interval has elapsed, or to post an event semaphore at repeated intervals. Timers are managed by OS/2. When an application requests a timer, the system monitors the system clock and notifies the application when the interval has elapsed. The system clock counts the number of system-clock interrupts (clock ticks) that have occurred since the system was started. On most hardware, clock ticks occur approximately 32 times a second, so the length of a tick is approximately 31.25 milliseconds. When an application specifies a timer interval, the system rounds up the interval to the next clock tick. For example, if an application requests a 10 millisecond interval, it will sleep for at least 31.25 milliseconds. If an application requests a 100 millisecond interval, the actual interval will be at least 125 milliseconds (4 ticks). Because OS/2 is a preemptive operating system, there is no guarantee that a thread will resume immediately after the timer interval. If a higher priority process or thread is executing, the timed thread must wait. Although timers are not absolutely accurate, they can be used where the inaccuracy can be ignored. In a real-time control application, for example, an event can be timed in seconds or minutes, so an error of a few milliseconds is unimportant. If an application requires as much accuracy as the system can provide, it can dedicate a thread to managing timer intervals and then elevate the priority of that thread. ═══ 8.1.1. Suspending Threads ═══ An application can use DosSleep to suspend operation of a thread for a specified interval. The system waits the specified number of milliseconds (subject to the round-off error just discussed) before returning control to the application. Because a sleeping application yields execution control to the system, the system can execute other processes or threads while the application sleeps. The following code fragment shows how to suspend the calling thread for one minute: #define INCL_DOSPROCESS /* Process and thread values */ #include #include ULONG ulTimeInterval; /* Interval in milliseconds */ APIRET ulrc; /* Return code */ ulTimeInterval = 60000; ulrc = DosSleep(ulTimeInterval); if (ulrc != 0) { printf("DosSleep error: return code = %ld", ulrc); return; } See Suspending the Current Thread for more information on DosSleep. ═══ 8.1.2. Asynchronous Timers ═══ DosSleep is useful for temporarily suspending a thread but is much less useful for timing. Typically, an application carries out part of its task and then waits an interval. If the execution time varies (as it will if the application runs on different hardware), the overall interval varies. In these situations, asynchronous timers provide greater precision than DosSleep. OS/2 supports two types of asynchronous timers, single-interval (one-shot) and repeated. DosAsyncTimer starts a single-interval timer. During the timing interval, the application can carry out other tasks. The system posts an event semaphore when the timing interval elapses. The application can reset the semaphore with DosResetEventSem before starting the timer. DosAsyncTimer yields a more accurate timing interval than DosSleep because the interval is independent of the execution time. DosStartTimer starts a repeated timer. The system posts an event semaphore each time the interval expires. The application can reset the semaphore before starting the timer and after each posting. When the application resets the semaphore with DosResetEventSem, it can check the cPosts value to determine how many times the semaphore has been posted. If the semaphore has been posted more than once, the application has missed a timer interval. ═══ 8.2. Using Timers ═══ Applications frequently need to synchronize the execution of threads, to cause an event to occur after a specified interval, or to cause an event to occur at regular intervals. Timers are typically used to enable an application to pause before processing user input or to carry out a task at a given time. OS/2 provides the following timer functions:  DosSleep suspends the execution of the calling thread, enabling other threads to run while the calling thread sleeps.  DosAsyncTimer starts a single-interval timer.  DosStartTimer starts a repeated-interval timer.  DosStopTimer stops a single-interval or repeated-interval timer. The system also provides two functions, DosGetDateTime and DosSetDateTime, for getting and setting the system date and time. The timers that are started by DosAsyncTimer and DosStartTimer are asynchronous timers; that is, the timers run independently of the calling thread, enabling the calling thread to perform other operations while the timer is running. When an asynchronous timer interval expires, the system notifies the application by posting an event semaphore. Time intervals for DosAsyncTimer, DosStartTimer, and DosSleep are specified in milliseconds; however, it is important to recognize that the actual duration of the specified time interval will be affected by two factors:  First, the system clock keeps track of time in less precise units known as clock ticks. On most hardware, clock ticks occur approximately 32 times a second, so each tick interval lasts approximately 31.25 milliseconds. (To determine the duration of a clock tick on your computer, call DosQuerySysInfo, and examine the timer-interval field.) Because clock ticks are less precise than millisecond values, any time interval that is specified in milliseconds will essentially be rounded up to the next clock tick.  Second, because OS/2 is a priority-based, multitasking operating system, there is no guarantee that a thread will resume execution immediately after the timer interval expires. If a higher priority process or thread is running, or if a hardware interrupt occurs, the timed thread blocks. (To minimize delays caused by preemptive scheduling, an application can dedicate a thread to managing time-critical tasks, and then raise that thread to a higher priority.) These factors usually cause the timer interval to be longer than requested; however, it will generally be within a few clock ticks. Timers for Presentation Manager applications are provided through the message queue. Therefore, a Presentation Manager application will not use the timer functions unless it performs some real-time control task. Note: In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred. ═══ 8.2.1. Suspending the Current Thread ═══ An application can suspend a thread by using DosSleep. DosSleep suspends the execution of the calling thread for a specified time interval. DosSleep requires one argument-the amount of time (in milliseconds) to suspend the thread. This value is rounded up to the nearest clock tick. If a time interval of 0 is specified, the thread gives up the remainder of the current time slice, enabling other ready threads of equal or higher priority to run; the calling thread will run again during its next scheduled time slice. If there is no other ready thread of equal or higher priority, DosSleep returns immediately; it does not yield to a thread of lower priority. If there is a round-off error or if other threads in the system have higher priority, a thread might not resume execution immediately after the sleep interval. The following DosSleep call suspends a thread for at least 5 seconds: DosSleep(5000); Note that the specified time interval refers to execution time (accumulated scheduled time slices), not to elapsed real time. Elapsed real time will be longer and will vary, depending on the hardware and on the number and priorities of other threads running in the system. In addition, even though the calling thread is scheduled for execution as soon as the specified time interval has elapsed, its execution could be delayed if a higher priority thread is running or if a hardware interrupt occurs. Because the above factors usually cause the sleep interval to be longer than requested (though generally within a few clock ticks), DosSleep should not be used as a substitute for a real-time clock. Note: 1. Elapsed real time for the asynchronous timers (started by DosAsyncTimer and DosStartTimer) will be much closer to their specified time intervals because these timers run independent of the execution of the calling thread. 2. To ensure optimal performance, do not use DosSleep in a single-thread Presentation Manager application. (Use WinStartTimer.) ═══ 8.2.2. Timing a Single Interval ═══ To carry out other tasks while the timer counts an interval, an application can use DosAsyncTimer. This function sets a single-interval timer without stopping the application-the timer runs asynchronously to the calling thread, enabling the thread to perform other operations while it is waiting for the specified time interval to expire. When the interval elapses, OS/2 notifies the application of the expiration of the timer by posting an event semaphore. The application resets the semaphore before starting the timer and monitors the semaphore to determine when the time has elapsed. The application can use DosCreateEventSem with the initial state FALSE to create a reset semaphore. For more information on semaphores, see Semaphores. The following code fragment creates an event semaphore and then calls DosAsyncTimer to count an interval while the application performs other tasks: #define INCL_DOSDATETIME /* Date/Time and Timer Support */ #define INCL_DOSSEMAPHORES /* for semaphores */ #include HEV hev; HTIMER hTimer; /* First create a private, reset, event semaphore. */ DosCreateEventSem((PSZ) NULL, &hev, DC_SEM_SHARED, FALSE); /* Start async (one-shot) timer; post semaphore in 10 seconds. */ DosAsyncTimer(10000, /* Time in milliseconds (10 sec) */ (HSEM) hev, /* Semaphore handle */ &hTimer); /* Timer handle (used to stop timer) */ . . /* Do other processing here, then wait for semaphore. */ . DosWaitEventSem(hev, SEM_INDEFINITE_WAIT); Before starting the timer, the thread creates the event semaphore with DosCreateEventSem, specifying its initial state as reset. If the semaphore was previously created by the same process, the thread resets it by calling DosResetEventSem. If the semaphore was previously created by another process, then the thread must call DosOpenEventSem to gain access to the semaphore before calling DosResetEventSem. Next, the thread calls DosAsyncTimer, specifying the handle of the event semaphore and the desired time interval. The thread can then perform other tasks. However, in order for the application to be notified of the expiration of the timer, one or more threads in the application must call DosWaitEventSem. When the time interval expires, the system posts the semaphore, and any threads that were blocked on DosWaitEventSem requests can resume their execution. If another time interval is required, the semaphore is reset, and both DosAsyncTimer and DosWaitEventSem are called again. (To time regular repeated intervals, use DosStartTimer.) The timer can be canceled before its time interval expires by calling DosStopTimer. ═══ 8.2.3. Timing Repeated Intervals ═══ To count an interval repeatedly, an application can use DosStartTimer. This function starts a repeated-interval timer. Unlike DosAsyncTimer, DosStartTimer does not stop after the first interval is counted. The timer runs asynchronously to the calling thread, enabling the thread to perform other operations while it is waiting for the specified time intervals to expire. The system notifies the application of the timer's expirations by posting an event semaphore. The application resets the semaphore before starting the timer and whenever the system posts the semaphore. The application can use the value returned in the post-counting parameter by DosResetEventSem to assure the semaphore was posted only once before it was reset. The following code fragment starts a timer and then waits on and resets the associated event semaphore. Assume that the handle of the targeted event semaphore has been placed into SemHandle. #define INCL_DOSDATETIME /* Date and time values */ #include #include ULONG ulTimeInterval; /* Interval (milliseconds) */ HSEM hsemSemHandle; /* Event-semaphore handle */ HTIMER hHandle; /* Timer handle (returned) */ APIRET ulrc; /* Return code */ ulTimeInterval = 30000; /* Set the periodic time interval to */ /* elapse every 30 seconds */ ulrc = DosStartTimer(ulTimeInterval, hsemSemHandle, &hHandle); if (ulrc != 0) { printf("DosStartTimer error: return code = %ld", ulrc); return; } On successful return, Handle will contain the handle of this periodic timer. DosStopTimer can be used later to stop the periodic timer. A repeated timer will continue to count the interval and post the semaphore until the application terminates or the application uses DosStopTimer to stop the timer explicitly. The following code fragment shows how to stop a periodic timer that has been started previously with DosStartTimer: #define INCL_DOSDATETIME /* Date and time values */ #include #include HTIMER hHandle; /* Handle of the timer */ APIRET ulrc; /* Return code */ ulrc = DosStopTimer(hHandle); if (ulrc != 0) { printf("DosStopTimer error: return code = %ld", ulrc); return; } Before starting the timer, the event semaphore must be reset. If the semaphore does not exist, the thread can create it with DosCreateEventSem, specifying its initial state as reset. If the semaphore was previously created by the same process, the thread resets it by calling DosResetEventSem. If the semaphore was previously created by another process, then the thread calls DosOpenEventSem to gain access to the semaphore before calling DosResetEventSem. Next, the thread calls DosStartTimer, specifying the handle of the event semaphore and the desired time interval. The thread can then perform other tasks. However, in order for the application to be notified of the timer's expirations, one or more threads in the application must call DosWaitEventSem. When the time interval expires, the system posts the semaphore, and any threads that were blocked on DosWaitEventSem requests can resume their execution. Each time the semaphore is posted, it must be reset with DosResetEventSem before the next timer expiration. DosWaitEventSem must also be called to wait for the semaphore to be posted again. In addition to resetting the event semaphore, DosResetEventSem returns the semaphore's post count (the number of times the semaphore has been posted since the last time it was in the set state). An application can use the post count to ensure that it has not missed a timer interval; if the post count is greater than one, the application missed a timer interval. ═══ 9. Glossary ═══ This glossary defines many of the terms used in this book. It includes terms and definitions from the IBM Dictionary of Computing, as well as terms specific to the OS/2 operating system and the Presentation Manager. It is not a complete glossary for the entire OS/2 operating system; nor is it a complete dictionary of computer terms. Other primary sources for these definitions are:  The American National Standard Dictionary for Information Systems, ANSI X3.172-1990, copyrighted 1990 by the American National Standards Institute, 11 West 42nd Street, New York, New York 10036. These definitions are identified by the symbol (A) after the definition.  The Information Technology Vocabulary, developed by Subcommittee 1, Joint Technical Committee 1, of the International Organization for Standardization and the International Electrotechnical Commission (ISO/IEC JTC1/SC1). Definitions of published parts of this vocabulary are identified by the symbol (I) after the definition; definitions taken from draft international standards, committee drafts, and working papers being developed by ISO/IEC JTC1/SC1 are identified by the symbol (T) after the definition, indicating that final agreement has not yet been reached among the participating National Bodies of SC1. ═══ 9.1. Glossary Listing ═══ Select a starting letter of glossary terms: A N B O C P D Q E R F S G T H U I V J W K X L Y M Z ═══ Glossary - A ═══ accelerator - In SAA Common User Access architecture, a key or combination of keys that invokes an application-defined function. accelerator table - A table used to define which key strokes are treated as accelerators and the commands they are translated into. access mode - The manner in which an application gains access to a file it has opened. Examples of access modes are read-only, write-only, and read/write. access permission - All access rights that a user has regarding an object. (I) action - One of a set of defined tasks that a computer performs. Users request the application to perform an action in several ways, such as typing a command, pressing a function key, or selecting the action name from an action bar or menu. action bar - In SAA Common User Access architecture, the area at the top of a window that contains choices that give a user access to actions available in that window. action point - The current position on the screen at which the pointer is pointing. Contrast with hot spot and input focus. active program - A program currently running on the computer. An active program can be interactive (running and receiving input from the user) or noninteractive (running but not receiving input from the user). See also interactive program and noninteractive program. active window - The window with which the user is currently interacting. address space - (1) The range of addresses available to a program. (A) (2) The area of virtual storage available for a particular job. alphanumeric video output - Output to the logical video buffer when the video adapter is in text mode and the logical video buffer is addressed by an application as a rectangular array of character cells. American National Standard Code for Information Interchange - The standard code, using a coded character set consisting of 7-bit coded characters (8 bits including parity check), that is used for information interchange among data processing systems, data communication systems, and associated equipment. The ASCII set consists of control characters and graphic characters. (A) Note: IBM has defined an extension to ASCII code (characters 128-255). anchor - A window procedure that handles Presentation Manager message conversions between an icon procedure and an application. anchor block - An area of Presentation-Manager-internal resources to allocated process or thread that calls WinInitialize. anchor point - A point in a window used by a program designer or by a window manager to position a subsequently appearing window. ANSI - American National Standards Institute. APA - All points addressable. API - Application programming interface. application - A collection of software components used to perform specific types of work on a computer; for example, a payroll application, an airline reservation application, a network application. application object - In SAA Advanced Common User Access architecture, a form that an application provides for a user; for example, a spreadsheet form. Contrast with user object. application programming interface (API) - A functional interface supplied by the operating system or by a separately orderable licensed program that allows an application program written in a high-level language to use specific data or functions of the operating system or the licensed program. application-modal - Pertaining to a message box or dialog box for which processing must be completed before further interaction with any other window owned by the same application may take place. area - In computer graphics, a filled shape such as a solid rectangle. ASCII - American National Standard Code for Information Interchange. ASCIIZ - A string of ASCII characters that is terminated with a byte containing the value 0. aspect ratio - In computer graphics, the width-to-height ratio of an area, symbol, or shape. asynchronous (ASYNC) - (1) Pertaining to two or more processes that do not depend upon the occurrence of specific events such as common timing signals. (T) (2) Without regular time relationship; unexpected or unpredictable with respect to the execution of program instructions. See also synchronous. atom - A constant that represents a string. As soon as a string has been defined as an atom, the atom can be used in place of the string to save space. Strings are associated with their respective atoms in an atom table. See also integer atom. atom table - A table used to relate atoms with the strings that they represent. Also in the table is the mechanism by which the presence of a string can be checked. atomic operation - An operation that completes its work on an object before another operation can be performed on the same object. attribute - A characteristic or property that can be controlled, usually to obtain a required appearance; for example, the color of a line. See also graphics attributes and segment attributes. automatic link - In Information Presentation Facility (IPF), a link that begins a chain reaction at the primary window. When the user selects the primary window, an automatic link is activated to display secondary windows. AVIO - Advanced Video Input/Output. ═══ Glossary - B ═══ BВzier curve - (1) A mathematical technique of specifying smooth continuous lines and surfaces, which require a starting point and a finishing point with several intermediate points that influence or control the path of the linking curve. Named after Dr. P. BВzier. (2) (D of C) In the AIX Graphics Library, a cubic spline approximation to a set of four control points that passes through the first and fourth control points and that has a continuous slope where two spline segments meet. Named after Dr. P. BВzier. background - (1) In multiprogramming, the conditions under which low-priority programs are executed. Contrast with foreground. (2) An active session that is not currently displayed on the screen. background color - The color in which the background of a graphic primitive is drawn. background mix - An attribute that determines how the background of a graphic primitive is combined with the existing color of the graphics presentation space. Contrast with mix. background program - In multiprogramming, a program that executes with a low priority. Contrast with foreground program. bit map - A representation in memory of the data displayed on an APA device, usually the screen. block - (1) A string of data elements recorded or transmitted as a unit. The elements may be characters, words, or logical records. (T) (2) To record data in a block. (3) A collection of contiguous records recorded as a unit. Blocks are separated by interblock gaps and each block may contain one or more records. (A) block device - A storage device that performs I/O operations on blocks of data called sectors. Data on block devices can be randomly accessed. Block devices are designated by a drive letter (for example, C:). blocking mode - A condition set by an application that determines when its threads might block. For example, an application might set the Pipemode parameter for the DosCreateNPipe function so that its threads perform I/O operations to the named pipe block when no data is available. border - A visual indication (for example, a separator line or a background color) of the boundaries of a window. boundary determination - An operation used to compute the size of the smallest rectangle that encloses a graphics object on the screen. breakpoint - (1) A point in a computer program where execution may be halted. A breakpoint is usually at the beginning of an instruction where halts, caused by external intervention, are convenient for resuming execution. (T) (2) A place in a program, specified by a command or a condition, where the system halts execution and gives control to the workstation user or to a specified program. broken pipe - When all of the handles that access one end of a pipe have been closed. bucket - One or more fields in which the result of an operation is kept. buffer - (1) A portion of storage used to hold input or output data temporarily. (2) To allocate and schedule the use of buffers. (A) button - A mechanism used to request or initiate an action. See also barrel buttons, bezel buttons, mouse button, push button, and radio button. byte pipe - Pipes that handle data as byte streams. All unnamed pipes are byte pipes. Named pipes can be byte pipes or message pipes. See byte stream. byte stream - Data that consists of an unbroken stream of bytes. ═══ Glossary - C ═══ cache - A high-speed buffer storage that contains frequently accessed instructions and data; it is used to reduce access time. cached micro presentation space - A presentation space from a Presentation-Manager-owned store of micro presentation spaces. It can be used for drawing to a window only, and must be returned to the store when the task is complete. CAD - Computer-Aided Design. call - (1) The action of bringing a computer program, a routine, or a subroutine into effect, usually by specifying the entry conditions and jumping to an entry point. (I) (A) (2) To transfer control to a procedure, program, routine, or subroutine. calling sequence - A sequence of instructions together with any associated data necessary to execute a call. (T) Cancel - An action that removes the current window or menu without processing it, and returns the previous window. cascaded menu - In the OS/2 operating system, a menu that appears when the arrow to the right of a cascading choice is selected. It contains a set of choices that are related to the cascading choice. Cascaded menus are used to reduce the length of a menu. See also cascading choice. cascading choice - In SAA Common User Access architecture, a choice in a menu that, when selected, produces a cascaded menu containing other choices. An arrow () appears to the right of the cascading choice. CASE statement - In PM programming, provides the body of a window procedure. There is usually one CASE statement for each message type supported by an application. CGA - Color graphics adapter. chained list - A list in which the data elements may be dispersed but in which each data element contains information for locating the next. (T)Synonymous with linked list. character - A letter, digit, or other symbol. character box - In computer graphics, the boundary that defines, in world coordinates, the horizontal and vertical space occupied by a single character from a character set. See also character mode. Contrast with character cell. character cell - The physical, rectangular space in which any single character is displayed on a screen or printer device. Position is addressed by row and column coordinates. Contrast with character box. character code - The means of addressing a character in a character set, sometimes called code point. character device - A device that performs I/O operations on one character at a time. Because character devices view data as a stream of bytes, character-device data cannot be randomly accessed. Character devices include the keyboard, mouse, and printer, and are referred to by name. character mode - A mode that, in conjunction with the font type, determines the extent to which graphics characters are affected by the character box, shear, and angle attributes. character set - (1) An ordered set of unique representations called characters; for example, the 26 letters of English alphabet, Boolean 0 and 1, the set of symbols in the Morse code, and the 128 ASCII characters. (A) (2) All the valid characters for a programming language or for a computer system. (3) A group of characters used for a specific reason; for example, the set of characters a printer can print. check box - In SAA Advanced Common User Access architecture, a square box with associated text that represents a choice. When a user selects a choice, an X appears in the check box to indicate that the choice is in effect. The user can clear the check box by selecting the choice again. Contrast with radio button. check mark - (1) (D of C) In SAA Advanced Common User Access architecture, a symbol that shows that a choice is currently in effect. (2) The symbol that is used to indicate a selected item on a pull-down menu. child process - In the OS/2 operating system, a process started by another process, which is called the parent process. Contrast with parent process. child window - A window that appears within the border of its parent window (either a primary window or another child window). When the parent window is resized, moved, or destroyed, the child window also is resized, moved, or destroyed; however, the child window can be moved or resized independently from the parent window, within the boundaries of the parent window. Contrast with parent window. choice - (1) An option that can be selected. The choice can be presented as text, as a symbol (number or letter), or as an icon (a pictorial symbol). (2) (D of C) In SAA Common User Access architecture, an item that a user can select. chord - (1) To press more than one button on a pointing device while the pointer is within the limits that the user has specified for the operating environment. (2) (D of C) In graphics, a short line segment whose end points lie on a circle. Chords are a means for producing a circular image from straight lines. The higher the number of chords per circle, the smoother the circular image. class - A way of categorizing objects based on their behavior and shape. A class is, in effect, a definition of a generic object. In SOM, a class is a special kind of object that can manufacture other objects that all have a common shape and exhibit similar behavior (more precisely, all of the objects manufactured by a class have the same memory layout and share a common set of methods). New classes can be defined in terms of existing classes through a technique known as inheritance. class method - A class method of class is a method provided by the metaclass of class . Class methods are executed without requiring any instances of class to exist, and are frequently used to create instances. In System Object Model, an action that can be performed on a class object. class object - In System Object Model, the run-time implementation of a class. class style - The set of properties that apply to every window in a window class. client - (1) A functional unit that receives shared services from a server. (T) (2) A user, as in a client process that uses a named pipe or queue that is created and owned by a server process. client area - The part of the window, inside the border, that is below the menu bar. It is the user's work space, where a user types information and selects choices from selection fields. In primary windows, it is where an application programmer presents the objects that a user works on. client program - An application that creates and manipulates instances of classes. client window - The window in which the application displays output and receives input. This window is located inside the frame window, under the window title bar and any menu bar, and within any scroll bars. clip limits - The area of the paper that can be reached by a printer or plotter. clipboard - In SAA Common User Access architecture, an area of computer memory, or storage, that temporarily holds data. Data in the clipboard is available to other applications. clipping - In computer graphics, removing those parts of a display image that lie outside a given boundary. (I) (A) clipping area - The area in which the window can paint. clipping path - A clipping boundary in world-coordinate space. clock tick - The minimum unit of time that the system tracks. If the system timer currently counts at a rate of X Hz, the system tracks the time every 1/X of a second. Also known as time tick. CLOCK$ - Character-device name reserved for the system clock. code page - An assignment of graphic characters and control-function meanings to all code points. code point - (1) Synonym for character code. (2) (D of C) A 1-byte code representing one of 256 potential characters. code segment - An executable section of programming code within a load module. color dithering - See dithering. color graphics adapter (CGA) - An adapter that simultaneously provides four colors and is supported by all IBM Personal Computer and Personal System/2 models. command - The name and parameters associated with an action that a program can perform. command area - An area composed of a command field prompt and a command entry field. command entry field - An entry field in which users type commands. command line - On a display screen, a display line, sometimes at the bottom of the screen, in which only commands can be entered. command mode - A state of a system or device in which the user can enter commands. command prompt - A field prompt showing the location of the command entry field in a panel. Common Programming Interface (CPI) - Definitions of those application development languages and services that have, or are intended to have, implementations on and a high degree of commonality across the SAA environments. One of the three SAA architectural areas. See also Common User Access architecture. Common User Access (CUA) architecture - Guidelines for the dialog between a human and a workstation or terminal. One of the three SAA architectural areas. See also Common Programming Interface. compile - To translate a program written in a higher-level programming language into a machine language program. composite window - A window composed of other windows (such as a frame window, frame-control windows, and a client window) that are kept together as a unit and that interact with each other. computer-aided design (CAD) - The use of a computer to design or change a product, tool, or machine, such as using a computer for drafting or illustrating. COM1, COM2, COM3 - Character-device names reserved for serial ports 1 through 3. CON - Character-device name reserved for the console keyboard and screen. conditional cascaded menu - A pull-down menu associated with a menu item that has a cascade mini-push button beside it in an object's pop-up menu. The conditional cascaded menu is displayed when the user selects the mini-push button. container - In SAA Common User Access architecture, an object that holds other objects. A folder is an example of a container object. See also folder and object. contextual help - In SAA Common User Access Architecture, help that gives specific information about the item the cursor is on. The help is contextual because it provides information about a specific item as it is currently being used. Contrast with extended help. contiguous - Touching or joining at a common edge or boundary, for example, an unbroken consecutive series of storage locations. control - In SAA Advanced Common User Access architecture, a component of the user interface that allows a user to select choices or type information; for example, a check box, an entry field, a radio button. control area - A storage area used by a computer program to hold control information. (I) (A) Control Panel - In the Presentation Manager, a program used to set up user preferences that act globally across the system. Control Program - (1) The basic functions of the operating system, including DOS emulation and the support for keyboard, mouse, and video input/output. (2) A computer program designed to schedule and to supervise the execution of programs of a computer system. (I) (A) control window - A window that is used as part of a composite window to perform simple input and output tasks. Radio buttons and check boxes are examples. control word - An instruction within a document that identifies its parts or indicates how to format the document. coordinate space - A two-dimensional set of points used to generate output on a video display of printer. Copy - A choice that places onto the clipboard, a copy of what the user has selected. See also Cut and Paste. correlation - The action of determining which element or object within a picture is at a given position on the display. This follows a pick operation. coverpage window - A window in which the application's help information is displayed. CPI - Common Programming Interface. critical extended attribute - An extended attribute that is necessary for the correct operation of the system or a particular application. critical section - (1) In programming languages, a part of an asynchronous procedure that cannot be executed simultaneously with a certain part of another asynchronous procedure. (I) Note: Part of the other asynchronous procedure also is a critical section. (2) A section of code that is not reentrant; that is, code that can be executed by only one thread at a time. CUA architecture - Common User Access architecture. current position - In computer graphics, the position, in user coordinates, that becomes the starting point for the next graphics routine, if that routine does not explicitly specify a starting point. cursor - A symbol displayed on the screen and associated with an input device. The cursor indicates where input from the device will be placed. Types of cursors include text cursors, graphics cursors, and selection cursors. Contrast with pointer and input focus. Cut - In SAA Common User Access architecture, a choice that removes a selected object, or a part of an object, to the clipboard, usually compressing the space it occupied in a window. See also Copy and Paste. ═══ Glossary - D ═══ daisy chain - A method of device interconnection for determining interrupt priority by connecting the interrupt sources serially. data segment - A nonexecutable section of a program module; that is, a section of a program that contains data definitions. data structure - The syntactic structure of symbolic expressions and their storage-allocation characteristics. (T) data transfer - The movement of data from one object to another by way of the clipboard or by direct manipulation. DBCS - Double-byte character set. DDE - Dynamic data exchange. deadlock - (1) Unresolved contention for the use of a resource. (2) An error condition in which processing cannot continue because each of two elements of the process is waiting for an action by, or a response from, the other. (3) An impasse that occurs when multiple processes are waiting for the availability of a resource that will not become available because it is being held by another process that is in a similar wait state. debug - To detect, diagnose, and eliminate errors in programs. (T) decipoint - In printing, one tenth of a point. There are 72 points in an inch. default procedure - A function provided by the Presentation Manager Interface that may be used to process standard messages from dialogs or windows. default value - A value assumed when no value has been specified. Synonymous with assumed value. For example, in the graphics programming interface, the default line-type is 'solid'. definition list - A type of list that pairs a term and its description. delta - An application-defined threshold, or number of container items, from either end of the list. descendant - See child process. descriptive text - Text used in addition to a field prompt to give more information about a field. Deselect all - A choice that cancels the selection of all of the objects that have been selected in that window. Desktop Manager - In the Presentation Manager, a window that displays a list of groups of programs, each of which can be started or stopped. desktop window - The window, corresponding to the physical device, against which all other types of windows are established. detached process - A background process that runs independent of the parent process. detent - A point on a slider that represents an exact value to which a user can move the slider arm. device context - A logical description of a data destination such as memory, metafile, display, printer, or plotter. See also direct device context, information device context, memory device context, metafile device context, queued device context, and screen device context. device driver - A file that contains the code needed to attach and use a device such as a display, printer, or plotter. device space - (1) Coordinate space in which graphics are assembled after all GPI transformations have been applied. Device space is defined in device-specific units. (2) ( D of C) In computer graphics, a space defined by the complete set of addressable points of a display device. (A) dialog - The interchange of information between a computer and its user through a sequence of requests by the user and the presentation of responses by the computer. dialog box - In SAA Advanced Common User Access architecture, a movable window, fixed in size, containing controls that a user uses to provide information required by an application so that it can continue to process a user request. See also message box, primary window, secondary window. Also known as a pop-up window. Dialog Box Editor - A WYSIWYG editor that creates dialog boxes for communicating with the application user. dialog item - A component (for example, a menu or a button) of a dialog box. Dialog items are also used when creating dialog templates. dialog procedure - A dialog window that is controlled by a window procedure. It is responsible for responding to all messages sent to the dialog window. dialog tag language - A markup language used by the DTL compiler to create dialog objects. dialog template - The definition of a dialog box, which contains details of its position, appearance, and window ID, and the window ID of each of its child windows. direct device context - A logical description of a data destination that is a device other than the screen (for example, a printer or plotter), and where the output is not to go through the spooler. Its purpose is to satisfy queries. See also device context. direct manipulation - The user's ability to interact with an object by using the mouse, typically by dragging an object around on the Desktop and dropping it on other objects. direct memory access (DMA) - A technique for moving data directly between main storage and peripheral equipment without requiring processing of the data by the processing unit.(T) directory - A type of file containing the names and controlling information for other files or other directories. display point - Synonym for pel. dithering - (1) The process used in color displays whereby every other pel is set to one color, and the intermediate pels are set to another. Together they produce the effect of a third color at normal viewing distances. This process can only be used on solid areas of color; it does not work, for example, on narrow lines. (2) (D of C ) In computer graphics, a technique of interleaving dark and light pixels so that the resulting image looks smoothly shaded when viewed from a distance. DMA - Direct memory access. DOS Protect Mode Interface (DPMI) - An interface between protect mode and real mode programs. double-byte character set (DBCS) - A set of characters in which each character is represented by two bytes. Languages such as Japanese, Chinese, and Korean, which contain more characters than can be represented by 256 code points, require double-byte character sets. Since each character requires two bytes, the entering, displaying, and printing of DBCS characters requires hardware and software that can support DBCS. doubleword - A contiguous sequence of bits or characters that comprises two computer words and is capable of being addressed as a unit. (A) DPMI - DOS Protect Mode Interface. drag - In SAA Common User Access, to use a pointing device to move an object; for example, clicking on a window border, and dragging it to make the window larger. dragging - (1) In computer graphics, moving an object on the display screen as if it were attached to the pointer. (2) (D of C) In computer graphics, moving one or more segments on a display surface by translating. (I) (A) drawing chain - See segment chain. drop - To fix the position of an object that is being dragged, by releasing the select button of the pointing device. See also drag. DTL - Dialog tag language. dual-boot function - A feature of the OS/2 operating system that allows the user to start DOS from within the operating system, or an OS/2 session from within DOS. duplex - Pertaining to communication in which data can be sent and received at the same time. Synonymous with full duplex. dynamic data exchange (DDE) - A message protocol used to communicate between applications that share data. The protocol uses shared memory as the means of exchanging data between applications. dynamic data formatting - A formatting procedure that enables you to incorporate text, bit maps or metafiles in an IPF window at execution time. dynamic link library - A collection of executable programming code and data that is bound to an application at load time or run time, rather than during linking. The programming code and data in a dynamic link library can be shared by several applications simultaneously. dynamic linking - The process of resolving external references in a program module at load time or run time rather than during linking. dynamic segments - Graphics segments drawn in exclusive-OR mix mode so that they can be moved from one screen position to another without affecting the rest of the displayed picture. dynamic storage - (1) A device that stores data in a manner that permits the data to move or vary with time such that the specified data is not always available for recovery. (A) (2) A storage in which the cells require repetitive application of control signals in order to retain stored data. Such repetitive application of the control signals is called a refresh operation. A dynamic storage may use static addressing or sensing circuits. (A) (3) See also static storage. dynamic time slicing - Varies the size of the time slice depending on system load and paging activity. dynamic-link module - A module that is linked at load time or run time. ═══ Glossary - E ═══ EBCDIC - Extended binary-coded decimal interchange code. A coded character set consisting of 8-bit coded characters (9 bits including parity check), used for information interchange among data processing systems, data communications systems, and associated equipment. edge-triggered - Pertaining to an event semaphore that is posted then reset before a waiting thread gets a chance to run. The semaphore is considered to be posted for the rest of that thread's waiting period; the thread does not have to wait for the semaphore to be posted again. EGA - Extended graphics adapter. element - An entry in a graphics segment that comprises one or more graphics orders and that is addressed by the element pointer. EMS - Expanded Memory Specification. encapsulation - Hiding an object's implementation, that is, its private, internal data and methods. Private variables and methods are accessible only to the object that contains them. entry field - In SAA Common User Access architecture, an area where a user types information. Its boundaries are usually indicated. See also selection field. entry panel - A defined panel type containing one or more entry fields and protected information such as headings, prompts, and explanatory text. entry-field control - The component of a user interface that provides the means by which the application receives data entered by the user in an entry field. When it has the input focus, the entry field displays a flashing pointer at the position where the next typed character will go. environment segment - The list of environment variables and their values for a process. environment strings - ASCII text strings that define the value of environment variables. environment variables - Variables that describe the execution environment of a process. These variables are named by the operating system or by the application. Environment variables named by the operating system are PATH, DPATH, INCLUDE, INIT, LIB, PROMPT, and TEMP. The values of environment variables are defined by the user in the CONFIG.SYS file, or by using the SET command at the OS/2 command prompt. error message - An indication that an error has been detected. (A) event semaphore - A semaphore that enables a thread to signal a waiting thread or threads that an event has occurred or that a task has been completed. The waiting threads can then perform an action that is dependent on the completion of the signaled event. exception - An abnormal condition such as an I/O error encountered in processing a data set or a file. exclusive system semaphore - A system semaphore that can be modified only by threads within the same process. executable file - (1) A file that contains programs or commands that perform operations or actions to be taken. (2) A collection of related data records that execute programs. exit - To execute an instruction within a portion of a computer program in order to terminate the execution of that portion. Such portions of computer programs include loops, subroutines, modules, and so on. (T) Repeated exit requests return the user to the point from which all functions provided to the system are accessible. Contrast with cancel. expanded memory specification (EMS) - Enables DOS applications to access memory above the 1MB real mode addressing limit. extended attribute - An additional piece of information about a file object, such as its data format or category. It consists of a name and a value. A file object may have more than one extended attribute associated with it. extended help - In SAA Common User Access architecture, a help action that provides information about the contents of the application window from which a user requested help. Contrast with contextual help. extended-choice selection - A mode that allows the user to select more than one item from a window. Not all windows allow extended choice selection. Contrast with multiple-choice selection. extent - Continuous space on a disk or diskette that is occupied by or reserved for a particular data set, data space, or file. external link - In Information Presentation Facility, a link that connects external online document files. ═══ Glossary - F ═══ family-mode application - An application program that can run in the OS/2 environment and in the DOS environment; however, it cannot take advantage of many of the OS/2-mode facilities, such as multitasking, interprocess communication, and dynamic linking. FAT - File allocation table. FEA - Full extended attribute. field-level help - Information specific to the field on which the cursor is positioned. This help function is "contextual" because it provides information about a specific item as it is currently used; the information is dependent upon the context within the work session. FIFO - First-in-first-out. (A) file - A named set of records stored or processed as a unit. (T) file allocation table (FAT) - In IBM personal computers, a table used by the operating system to allocate space on a disk for a file, and to locate and chain together parts of the file that may be scattered on different sectors so that the file can be used in a random or sequential manner. file attribute - Any of the attributes that describe the characteristics of a file. File Manager - In the Presentation Manager, a program that displays directories and files, and allows various actions on them. file specification - The full identifier for a file, which includes its drive designation, path, file name, and extension. file system - The combination of software and hardware that supports storing information on a storage device. file system driver (FSD) - A program that manages file I\O and controls the format of information on the storage media. fillet - A curve that is tangential to the end points of two adjoining lines. See also polyfillet. filtering - An application process that changes the order of data in a queue. first-in-first-out (FIFO) - A queuing technique in which the next item to be retrieved is the item that has been in the queue for the longest time. (A) flag - (1) An indicator or parameter that shows the setting of a switch. (2) A character that signals the occurrence of some condition, such as the end of a word. (A) (3) (D of C) A characteristic of a file or directory that enables it to be used in certain ways. See also archive flag, hidden flag, and read-only flag. focus - See input focus. folder - A container used to organize objects. font - A particular size and style of typeface that contains definitions of character sets, marker sets, and pattern sets. Font Editor - A utility program provided with the IBM Developers Toolkit that enables the design and creation of new fonts. foreground program - (1) The program with which the user is currently interacting. Also known as interactive program. Contrast with background program. (2) (D of C) In multiprogramming, a high-priority program. frame - The part of a window that can contain several different visual elements specified by the application, but drawn and controlled by the Presentation Manager. The frame encloses the client area. frame styles - Standard window layouts provided by the Presentation Manager. FSD - File system driver. full-duplex - Synonym for duplex. full-screen application - An application that has complete control of the screen. function - (1) In a programming language, a block, with or without formal parameters, whose execution is invoked by means of a call. (2) A set of related control statements that cause one or more programs to be performed. function key - A key that causes a specified sequence of operations to be performed when it is pressed, for example, F1 and Alt-K. function key area - The area at the bottom of a window that contains function key assignments such as F1=Help. ═══ Glossary - G ═══ GDT - Global Descriptor Table. general protection fault - An exception condition that occurs when a process attempts to use storage or a module that has some level of protection assigned to it, such as I/O privilege level. See also IOPL code segment. Global Descriptor Table (GDT) - A table that defines code and data segments available to all tasks in an application. global dynamic-link module - A dynamic-link module that can be shared by all processes in the system that refer to the module name. global file-name character - Either a question mark (?) or an asterisk (*) used as a variable in a file name or file name extension when referring to a particular file or group of files. glyph - A graphic symbol whose appearance conveys information. GPI - Graphics programming interface. graphic primitive - In computer graphics, a basic element, such as an arc or a line, that is not made up of smaller parts and that is used to create diagrams and pictures. See also graphics segment. graphics - (1) A picture defined in terms of graphic primitives and graphics attributes. (2) (D of C) The making of charts and pictures. (3) Pertaining to charts, tables, and their creation. (4) See computer graphics, coordinate graphics, fixed-image graphics, interactive graphics, passive graphics, raster graphics. graphics attributes - Attributes that apply to graphic primitives. Examples are color, line type, and shading-pattern definition. See also segment attributes. graphics field - The clipping boundary that defines the visible part of the presentation-page contents. graphics mode - One of several states of a display. The mode determines the resolution and color content of the screen. graphics model space - The conceptual coordinate space in which a picture is constructed after any model transforms have been applied. Also known as model space. Graphics programming interface - The formally defined programming language that is between an IBM graphics program and the user of the program. graphics segment - A sequence of related graphic primitives and graphics attributes. See also graphic primitive. graying - The indication that a choice on a pull-down is unavailable. group - A collection of logically connected controls. For example, the buttons controlling paper size for a printer could be called a group. See also program group. ═══ Glossary - H ═══ handle - (1) An identifier that represents an object, such as a device or window, to the Presentation Interface. (2) (D of C) In the Advanced DOS and OS/2 operating systems, a binary value created by the system that identifies a drive, directory, and file so that the file can be found and opened. hard error - An error condition on a network that requires either that the system be reconfigured or that the source of the error be removed before the system can resume reliable operation. header - (1) System-defined control information that precedes user data. (2) The portion of a message that contains control information for the message, such as one or more destination fields, name of the originating station, input sequence number, character string indicating the type of message, and priority level for the message. heading tags - A document element that enables information to be displayed in windows, and that controls entries in the contents window controls placement of push buttons in a window, and defines the shape and size of windows. heap - An area of free storage available for dynamic allocation by an application. Its size varies according to the storage requirements of the application. help function - (1) A function that provides information about a specific field, an application panel, or information about the help facility. (2) (D of C) One or more display images that describe how to use application software or how to do a system operation. Help index - In SAA Common User Access architecture, a help action that provides an index of the help information available for an application. help panel - A panel with information to assist users that is displayed in response to a help request from the user. help window - A Common-User-Access-defined secondary window that displays information when the user requests help. hidden file - An operating system file that is not displayed by a directory listing. hide button - In the OS/2 operating system, a small, square button located in the right-hand corner of the title bar of a window that, when selected, removes from the screen all the windows associated with that window. Contrast with maximize button. See also restore button. hierarchical inheritance - The relationship between parent and child classes. An object that is lower in the inheritance hierarchy than another object, inherits all the characteristics and behaviors of the objects above it in the hierarchy. hierarchy - A tree of segments beginning with the root segment and proceeding downward to dependent segment types. high-performance file system (HPFS) - In the OS/2 operating system, an installable file system that uses high-speed buffer storage, known as a cache, to provide fast access to large disk volumes. The file system also supports the coexistence of multiple, active file systems on a single personal computer, with the capability of multiple and different storage devices. File names used with the HPFS can have as many as 254 characters. hit testing - The means of identifying which window is associated with which input device event. hook - A point in a system-defined function where an application can supply additional code that the system processes as though it were part of the function. hook chain - A sequence of hook procedures that are "chained" together so that each event is passed, in turn, to each procedure in the chain. hot spot - The part of the pointer that must touch an object before it can be selected. This is usually the tip of the pointer. Contrast with action point. HPFS - high-performance file system. hypergraphic link - A connection between one piece of information and another through the use of graphics. hypertext - A way of presenting information online with connections between one piece of information and another, called hypertext links. See also hypertext link. hypertext link - A connection between one piece of information and another. ═══ Glossary - I ═══ I/O operation - An input operation to, or output operation from a device attached to a computer. I-beam pointer - A pointer that indicates an area, such as an entry field in which text can be edited. icon - In SAA Advanced Common User Access architecture, a graphical representation of an object, consisting of an image, image background, and a label. Icons can represent items (such as a document file) that the user wants to work on, and actions that the user wants to perform. In the Presentation Manager, icons are used for data objects, system actions, and minimized programs. icon area - In the Presentation Manager, the area at the bottom of the screen that is normally used to display the icons for minimized programs. Icon Editor - The Presentation Manager-provided tool for creating icons. IDL - Interface Definition Language. image font - A set of symbols, each of which is described in a rectangular array of pels. Some of the pels in the array are set to produce the image of one of the symbols. Contrast with outline font. implied metaclass - Subclassing the metaclass of a parent class without a separate CSC for the resultant metaclass. indirect manipulation - Interaction with an object through choices and controls. information device context - A logical description of a data destination other than the screen (for example, a printer or plotter), but where no output will occur. Its purpose is to satisfy queries. See also device context. information panel - A defined panel type characterized by a body containing only protected information. Information Presentation Facility (IPF) - A facility provided by the OS/2 operating system, by which application developers can produce online documentation and context-sensitive online help panels for their applications. inheritance - The technique of specifying the shape and behavior of one class (called a subclass) as incremental differences from another class (called the parent class or superclass). The subclass inherits the superclass' state representation and methods, and can provide additional data elements and methods. The subclass also can provide new functions with the same method names used by the superclass. Such a subclass method is said to override the superclass method, and will be selected automatically by method resolution on subclass instances. An overriding method can elect to call upon the superclass' method as part of its own implementation. input focus - (1) The area of a window where user interaction is possible using an input device, such as a mouse or the keyboard. (2) The position in the active window where a user's normal interaction with the keyboard will appear. input router - An internal OS/2 process that removes messages from the system queue. input/output control - A device-specific command that requests a function of a device driver. installable file system (IFS) - A file system in which software is installed when the operating system is started. instance - (Or object instance). A specific object, as distinguished from the abstract definition of an object referred to as its class. instance method - A method valid for a particular object. instruction pointer - In System/38, a pointer that provides addressability for a machine interface instruction in a program. integer atom - An atom that represents a predefined system constant and carries no storage overhead. For example, names of window classes provided by Presentation Manager are expressed as integer atoms. interactive graphics - Graphics that can be moved or manipulated by a user at a terminal. interactive program - (1) A program that is running (active) and is ready to receive (or is receiving) input from a user. (2) A running program that can receive input from the keyboard or another input device. Compare with active program and contrast with noninteractive program. Also known as a foreground program. interchange file - A file containing data that can be sent from one Presentation Manager interface application to another. Interface Definition Language (IDL) - Language-neutral interface specification for a SOM class. interpreter - A program that translates and executes each instruction of a high-level programming language before it translates and executes. interprocess communication (IPC) - In the OS/2 operating system, the exchange of information between processes or threads through semaphores, pipes, queues, and shared memory. interval timer - (1) A timer that provides program interruptions on a program-controlled basis. (2) An electronic counter that counts intervals of time under program control. IOCtl - Input/output control. IOPL - Input/output privilege level. IOPL code segment - An IOPL executable section of programming code that enables an application to directly manipulate hardware interrupts and ports without replacing the device driver. See also privilege level. IPC - Interprocess communication. IPF - Information Presentation Facility. IPF compiler - A text compiler that interpret tags in a source file and converts the information into the specified format. IPF tag language - A markup language that provides the instructions for displaying online information. item - A data object that can be passed in a DDE transaction. ═══ Glossary - J ═══ journal - A special-purpose file that is used to record changes made in the system. ═══ Glossary - K ═══ Kanji - A graphic character set used in Japanese ideographic alphabets. KBD$ - Character-device name reserved for the keyboard. kernel - The part of an operating system that performs basic functions, such as allocating hardware resources. kerning - The design of graphics characters so that their character boxes overlap. Used to space text proportionally. keyboard accelerator - A keystroke that generates a command message for an application. keyboard augmentation - A function that enables a user to press a keyboard key while pressing a mouse button. keyboard focus - A temporary attribute of a window. The window that has a keyboard focus receives all keyboard input until the focus changes to a different window. Keys help - In SAA Common User Access architecture, a help action that provides a listing of the application keys and their assigned functions. ═══ Glossary - L ═══ label - In a graphics segment, an identifier of one or more elements that is used when editing the segment. LAN - Local area network. language support procedure - A function provided by the Presentation Manager Interface for applications that do not, or cannot (as in the case of COBOL and FORTRAN programs), provide their own dialog or window procedures. lazy drag - See pickup and drop. lazy drag set - See pickup set. LDT - In the OS/2 operating system, Local Descriptor Table. LIFO stack - A stack from which data is retrieved in last-in, first-out order. linear address - A unique value that identifies the memory object. linked list - Synonym for chained list. list box - In SAA Advanced Common User Access architecture, a control that contains scrollable choices from which a user can select one choice. Note: In CUA architecture, this is a programmer term. The end user term is selection list. list button - A button labeled with an underlined down-arrow that presents a list of valid objects or choices that can be selected for that field. list panel - A defined panel type that displays a list of items from which users can select one or more choices and then specify one or more actions to work on those choices. load time - The point in time at which a program module is loaded into main storage for execution. load-on-call - A function of a linkage editor that allows selected segments of the module to be disk resident while other segments are executing. Disk resident segments are loaded for execution and given control when any entry point that they contain is called. local area network (LAN) - (1) A computer network located on a user's premises within a limited geographical area. Communication within a local area network is not subject to external regulations; however, communication across the LAN boundary may be subject to some form of regulation. (T) Note: A LAN does not use store and forward techniques. (2) A network in which a set of devices are connected to one another for communication and that can be connected to a larger network. Local Descriptor Table (LDT) - Defines code and data segments specific to a single task. lock - A serialization mechanism by means of which a resource is restricted for use by the holder of the lock. logical storage device - A device that the user can map to a physical (actual) device. LPT1, LPT2, LPT3 - Character-device names reserved for parallel printers 1 through 3. ═══ Glossary - M ═══ main window - The window that is positioned relative to the desktop window. manipulation button - The button on a pointing device a user presses to directly manipulate an object. map - (1) A set of values having a defined correspondence with the quantities or values of another set. (I) (A) (2) To establish a set of values having a defined correspondence with the quantities or values of another set. (I) marker box - In computer graphics, the boundary that defines, in world coordinates, the horizontal and vertical space occupied by a single marker from a marker set. marker symbol - A symbol centered on a point. Graphs and charts can use marker symbols to indicate the plotted points. marquee box - The rectangle that appears during a selection technique in which a user selects objects by drawing a box around them with a pointing device. Master Help Index - In the OS/2 operating system, an alphabetic list of help topics related to using the operating system. maximize - To enlarge a window to its largest possible size. media window - The part of the physical device (display, printer, or plotter) on which a picture is presented. memory block - Part memory within a heap. memory device context - A logical description of a data destination that is a memory bit map. See also device context. memory management - A feature of the operating system for allocating, sharing, and freeing main storage. memory object - Logical unit of memory requested by an application, which forms the granular unit of memory manipulation from the application viewpoint. menu - In SAA Advanced Common User Access architecture, an extension of the menu bar that displays a list of choices available for a selected choice in the menu bar. After a user selects a choice in menu bar, the corresponding menu appears. Additional pop-up windows can appear from menu choices. menu bar - In SAA Advanced Common User Access architecture, the area near the top of a window, below the title bar and above the rest of the window, that contains choices that provide access to other menus. menu button - The button on a pointing device that a user presses to view a pop-up menu associated with an object. message - (1) In the Presentation Manager, a packet of data used for communication between the Presentation Manager interface and Presentation Manager applications (2) In a user interface, information not requested by users but presented to users by the computer in response to a user action or internal process. message box - (1) A dialog window predefined by the system and used as a simple interface for applications, without the necessity of creating dialog-template resources or dialog procedures. (2) (D of C) In SAA Advanced Common User Access architecture, a type of window that shows messages to users. See also dialog box, primary window, secondary window. message filter - The means of selecting which messages from a specific window will be handled by the application. message queue - A sequenced collection of messages to be read by the application. message stream mode - A method of operation in which data is treated as a stream of messages. Contrast with byte stream. metacharacter - See global file-name character. metaclass - A class whose instances are all classes. In SOM, any class descended from SOMClass is a metaclass. The methods of a metaclass are sometimes called "class" methods. metafile - A file containing a series of attributes that set color, shape and size, usually of a picture or a drawing. Using a program that can interpret these attributes, a user can view the assembled image. metafile device context - A logical description of a data destination that is a metafile, which is used for graphics interchange. See also device context. metalanguage - A language used to specify another language. For example, data types can be described using a metalanguage so as to make the descriptions independent of any one computer language. method - One of the units that makes up the behavior of an object. A method is a combination of a function and a name, such that many different functions can have the same name. Which function the name refers to at any point in time depends on the object that is to execute the method and is the subject of method resolution. method override - The replacement, by a child class, of the implementation of a method inherited from a parent and an ancestor class. mickey - A unit of measurement for physical mouse motion whose value depends on the mouse device driver currently loaded. micro presentation space - A graphics presentation space in which a restricted set of the GPI function calls is available. minimize - To remove from the screen all windows associated with an application and replace them with an icon that represents the application. mix - An attribute that determines how the foreground of a graphic primitive is combined with the existing color of graphics output. Also known as foreground mix. Contrast with background mix. mixed character string - A string containing a mixture of one-byte and Kanji or Hangeul (two-byte) characters. mnemonic - (1) A method of selecting an item on a pull-down by means of typing the highlighted letter in the menu item. (2) (D of C) In SAA Advanced Common User Access architecture, usually a single character, within the text of a choice, identified by an underscore beneath the character. If all characters in a choice already serve as mnemonics for other choices, another character, placed in parentheses immediately following the choice, can be used. When a user types the mnemonic for a choice, the choice is either selected or the cursor is moved to that choice. modal dialog box - In SAA Advanced Common User Access architecture, a type of movable window, fixed in size, that requires a user to enter information before continuing to work in the application window from which it was displayed. Contrast with modeless dialog box. Also known as a serial dialog box. Contrast with parallel dialog box. Note: In CUA architecture, this is a programmer term. The end user term is pop-up window. model space - See graphics model space. modeless dialog box - In SAA Advanced Common User Access architecture, a type of movable window, fixed in size, that allows users to continue their dialog with the application without entering information in the dialog box. Also known as a parallel dialog box. Contrast with modal dialog box. Note: In CUA architecture, this is a programmer term. The end user term is pop-up window. module definition file - A file that describes the code segments within a load module. For example, it indicates whether a code segment is loadable before module execution begins (preload), or loadable only when referred to at run time (load-on-call). mouse - In SAA usage, a device that a user moves on a flat surface to position a pointer on the screen. It allows a user to select a choice o function to be performed or to perform operations on the screen, such as dragging or drawing lines from one position to another. MOUSE$ - Character-device name reserved for a mouse. multiple-choice selection - In SAA Basic Common User Access architecture, a type of field from which a user can select one or more choices or select none. See also check box. Contrast with extended-choice selection. multiple-line entry field - In SAA Advanced Common User Access architecture, a control into which a user types more than one line of information. See also single-line entry field. multitasking - The concurrent processing of applications or parts of applications. A running application and its data are protected from other concurrently running applications. mutex semaphore - (Mutual exclusion semaphore). A semaphore that enables threads to serialize their access to resources. Only the thread that currently owns the mutex semaphore can gain access to the resource, thus preventing one thread from interrupting operations being performed by another. muxwait semaphore - (Multiple wait semaphore). A semaphore that enables a thread to wait either for multiple event semaphores to be posted or for multiple mutex semaphores to be released. Alternatively, a muxwait semaphore can be set to enable a thread to wait for any ONE of the event or mutex semaphores in the muxwait semaphore's list to be posted or released. ═══ Glossary - N ═══ named pipe - A named buffer that provides client-to-server, server-to-client, or full duplex communication between unrelated processes. Contrast with unnamed pipe. national language support (NLS) - The modification or conversion of a United States English product to conform to the requirements of another language or country. This can include the enabling or retrofitting of a product and the translation of nomenclature, MRI, or documentation of a product. nested list - A list that is contained within another list. NLS - national language support. non-8.3 file-name format - A file-naming convention in which file names can consist of up to 255 characters. See also 8.3 file-name format. noncritical extended attribute - An extended attribute that is not necessary for the function of an application. nondestructive read - Reading that does not erase the data in the source location. (T) noninteractive program - A running program that cannot receive input from the keyboard or other input device. Compare with active program, and contrast with interactive program. nonretained graphics - Graphic primitives that are not remembered by the Presentation Manager interface when they have been drawn. Contrast with retained graphics. null character (NUL) - (1) Character-device name reserved for a nonexistent (dummy) device. (2) (D of C) A control character that is used to accomplish media-fill or time-fill and that may be inserted into or removed from a sequence of characters without affecting the meaning of the sequence; however, the control of equipment or the format may be affected by this character. (I) (A) null-terminated string - A string of (n+1) characters where the (n+1)th character is the 'null' character (0x00) Also known as 'zero-terminated' string and 'ASCIIZ' string. ═══ Glossary - O ═══ object - The elements of data and function that programs create, manipulate, pass as arguments, and so forth. An object is a way of associating specific data values with a specific set of named functions (called methods) for a period of time (referred to as the lifetime of the object). The data values of an object are referred to as its state. In SOM, objects are created by other objects called classes. The specification of what comprises the set of functions and data elements that make up an object is referred to as the definition of a class. SOM objects offer a high degree of encapsulation. This property permits many aspects of the implementation of an object to change without affecting client programs that depend on the object's behavior. object definition - See class. object instance - See instance. Object Interface Definition Language (OIDL) - Specification language used in SOM Version 1 for defining classes. Replaced by Interface Definition Language (IDL). object window - A window that does not have a parent but which might have child windows. An object window cannot be presented on a device. OIDL - Object Interface Definition Language. open - To start working with a file, directory, or other object. ordered list - Vertical arrangements of items, with each item in the list preceded by a number or letter. outline font - A set of symbols, each of which is created as a series of lines and curves. Synonymous with vector font. Contrast with image font. output area - An area of storage reserved for output. (A) owner window - A window into which specific events that occur in another (owned) window are reported. ownership - The determination of how windows communicate using messages. owning process - The process that owns the resources that might be shared with other processes. ═══ Glossary - P ═══ page - (1) A 4KB segment of contiguous physical memory. (2) (D of C) A defined unit of space on a storage medium. page viewport - A boundary in device coordinates that defines the area of the output device in which graphics are to be displayed. The presentation-page contents are transformed automatically to the page viewport in device space. paint - (1) The action of drawing or redrawing the contents of a window. (2) In computer graphics, to shade an area of a display image; for example, with crosshatching or color. panel - In SAA Basic Common User Access architecture, a particular arrangement of information that is presented in a window or pop-up. If some of the information is not visible, a user can scroll through the information. panel area - An area within a panel that contains related information. The three major Common User Access-defined panel areas are the action bar, the function key area, and the panel body. panel area separator - In SAA Basic Common User Access architecture, a solid, dashed, or blank line that provides a visual distinction between two adjacent areas of a panel. panel body - The portion of a panel not occupied by the action bar, function key area, title or scroll bars. The panel body can contain protected information, selection fields, and entry fields. The layout and content of the panel body determine the panel type. panel body area - See client area. panel definition - A description of the contents and characteristics of a panel. A panel definition is the application developer's mechanism for predefining the format to be presented to users in a window. panel ID - In SAA Basic Common User Access architecture, a panel identifier, located in the upper-left corner of a panel. A user can choose whether to display the panel ID. panel title - In SAA Basic Common User Access architecture, a particular arrangement of information that is presented in a window or pop-up. If some of the information is not visible, a user can scroll through the information. paper size - The size of paper, defined in either standard U.S. or European names (for example, A, B, A4), and measured in inches or millimeters respectively. parallel dialog box - See modeless dialog box. parameter list - A list of values that provides a means of associating addressability of data defined in a called program with data in the calling program. It contains parameter names and the order in which they are to be associated in the calling and called program. parent class - See inheritance. parent process - In the OS/2 operating system, a process that creates other processes. Contrast with child process. parent window - In the OS/2 operating system, a window that creates a child window. The child window is drawn within the parent window. If the parent window is moved, resized, or destroyed, the child window also will be moved, resized, or destroyed. However, the child window can be moved and resized independently from the parent window, within the boundaries of the parent window. Contrast with child window. partition - (1) A fixed-size division of storage. (2) On an IBM personal computer fixed disk, one of four possible storage areas of variable size; one may be accessed by DOS, and each of the others may be assigned to another operating system. Paste - A choice in the Edit pull-down that a user selects to move the contents of the clipboard into a preselected location. See also Copy and Cut. path - The route used to locate files; the storage location of a file. A fully qualified path lists the drive identifier, directory name, subdirectory name (if any), and file name with the associated extension. PDD - Physical device driver. peeking - An action taken by any thread in the process that owns the queue to examine queue elements without removing them. pel - (1) The smallest area of a display screen capable of being addressed and switched between visible and invisible states. Synonym for display point, pixel, and picture element. (2) (D of C) Picture element. persistent object - An object whose instance data and state are preserved between system shutdown and system startup. physical device driver (PDD) - A system interface that handles hardware interrupts and supports a set of input and output functions. pick - To select part of a displayed object using the pointer. pickup - To add an object or set of objects to the pickup set. pickup and drop - A drag operation that does not require the direct manipulation button to be pressed for the duration of the drag. pickup set - The set of objects that have been picked up as part of a pickup and drop operation. picture chain - See segment chain. picture element - (1) Synonym for pel. (2) (D of C) In computer graphics, the smallest element of a display surface that can be independently assigned color and intensity. (T) . (3) The area of the finest detail that can be reproduced effectively on the recording medium. PID - Process identification. pipe - (1) A named or unnamed buffer used to pass data between processes. A process reads from or writes to a pipe as if the pipe were a standard-input or standard-output file. See also named pipe and unnamed pipe. (2) (D of C) To direct data so that the output from one process becomes the input to another process. The standard output of one command can be connected to the standard input of another with the pipe operator (|). pixel - (1) Synonym for pel. (2) (D of C) Picture element. plotter - An output unit that directly produces a hardcopy record of data on a removable medium, in the form of a two-dimensional graphic representation. (T) PM - Presentation Manager. pointer - (1) The symbol displayed on the screen that is moved by a pointing device, such as a mouse. The pointer is used to point at items that users can select. Contrast with cursor. (2) A data element that indicates the location of another data element. (T) POINTER$ - Character-device name reserved for a pointer device (mouse screen support). pointing device - In SAA Advanced Common User Access architecture, an instrument, such as a mouse, trackball, or joystick, used to move a pointer on the screen. pointings - Pairs of x-y coordinates produced by an operator defining positions on a screen with a pointing device, such as a mouse. polyfillet - A curve based on a sequence of lines. The curve is tangential to the end points of the first and last lines, and tangential also to the midpoints of all other lines. See also fillet. polygon - One or more closed figures that can be drawn filled, outlined, or filled and outlined. polyline - A sequence of adjoining lines. polymorphism - The ability to have different implementations of the same method for two or more classes of objects. pop - To retrieve an item from a last-in-first-out stack of items. Contrast with push. pop-up menu - A menu that lists the actions that a user can perform on an object. The contents of the pop-up menu can vary depending on the context, or state, of the object. pop-up window - (1) A window that appears on top of another window in a dialog. Each pop-up window must be completed before returning to the underlying window. (2) (D of C) In SAA Advanced Common User Access architecture, a movable window, fixed in size, in which a user provides information required by an application so that it can continue to process a user request. presentation drivers - Special purpose I/O routines that handle field device-independent I/O requests from the PM and its applications. Presentation Manager (PM) - The interface of the OS/2 operating system that presents, in windows a graphics-based interface to applications and files installed and running under the OS/2 operating system. presentation page - The coordinate space in which a picture is assembled for display. presentation space (PS) - (1) Contains the device-independent definition of a picture. (2) (D of C) The display space on a display device. primary window - In SAA Common User Access architecture, the window in which the main interaction between the user and the application takes place. In a multiprogramming environment, each application starts in its own primary window. The primary window remains for the duration of the application, although the panel displayed will change as the user's dialog moves forward. See also secondary window. primitive - In computer graphics, one of several simple functions for drawing on the screen, including, for example, the rectangle, line, ellipse, polygon, and so on. primitive attribute - A specifiable characteristic of a graphic primitive. See graphics attributes. print job - The result of sending a document or picture to be printed. Print Manager - In the Presentation Manager, the part of the spooler that manages the spooling process. It also allows users to view print queues and to manipulate print jobs. privilege level - A protection level imposed by the hardware architecture of the IBM personal computer. There are four privilege levels (number 0 through 3). Only certain types of programs are allowed to execute at each privilege level. See also IOPL code segment. procedure call - In programming languages, a language construct for invoking execution of a procedure. process - An instance of an executing application and the resources it is using. program - A sequence of instructions that a computer can interpret and execute. program details - Information about a program that is specified in the Program Manager window and is used when the program is started. program group - In the Presentation Manager, several programs that can be acted upon as a single entity. program name - The full file specification of a program. Contrast with program title. program title - The name of a program as it is listed in the Program Manager window. Contrast with program name. prompt - A displayed symbol or message that requests input from the user or gives operational information; for example, on the display screen of an IBM personal computer, the DOS A> prompt. The user must respond to the prompt in order to proceed. protect mode - A method of program operation that limits or prevents access to certain instructions or areas of storage. Contrast with real mode. protocol - A set of semantic and syntactic rules that determines the behavior of functional units in achieving communication. (I) pseudocode - An artificial language used to describe computer program algorithms without using the syntax of any particular programming language. (A) pull-down - (1) An action bar extension that displays a list of choices available for a selected action bar choice. After users select an action bar choice, the pull-down appears with the list of choices. Additional pop-up windows may appear from pull-down choices to further extend the actions available to users. (2) (D of C) In SAA Common User Access architecture, pertaining to a choice in an action bar pull-down. push - To add an item to a last-in-first-out stack of items. Contrast with pop. push button - In SAA Advanced Common User Access architecture, a rectangle with text inside. Push buttons are used in windows for actions that occur immediately when the push button is selected. putback - To remove an object or set of objects from the lazy drag set. This has the effect of undoing the pickup operation for those objects putdown - To drop the objects in the lazy drag set on the target object. ═══ Glossary - Q ═══ queue - (1) A linked list of elements waiting to be processed in FIFO order. For example, a queue may be a list of print jobs waiting to be printed. (2) (D of C) A line or list of items waiting to be processed; for example, work to be performed or messages to be displayed. queued device context - A logical description of a data destination (for example, a printer or plotter) where the output is to go through the spooler. See also device context. ═══ Glossary - R ═══ radio button - (1) A control window, shaped like a round button on the screen, that can be in a checked or unchecked state. It is used to select a single item from a list. Contrast with check box. (2) In SAA Advanced Common User Access architecture, a circle with text beside it. Radio buttons are combined to show a user a fixed set of choices from which only one can be selected. The circle is partially filled when a choice is selected. RAS - Reliability, availability, and serviceability. raster - (1) In computer graphics, a predetermined pattern of lines that provides uniform coverage of a display space. (T) (2) The coordinate grid that divides the display area of a display device. (A) read-only file - A file that can be read from but not written to. real mode - A method of program operation that does not limit or prevent access to any instructions or areas of storage. The operating system loads the entire program into storage and gives the program access to all system resources. Contrast with protect mode. realize - To cause the system to ensure, wherever possible, that the physical color table of a device is set to the closest possible match in the logical color table. recursive routine - A routine that can call itself, or be called by another routine that was called by the recursive routine. reentrant - The attribute of a program or routine that allows the same copy of the program or routine to be used concurrently by two or more tasks. reference phrase - (1) A word or phrase that is emphasized in a device-dependent manner to inform the user that additional information for the word or phrase is available. (2) (D of C) In hypertext, text that is highlighted and preceded by a single-character input field used to signify the existence of a hypertext link. reference phrase help - In SAA Common User Access architecture, highlighted words or phrases within help information that a user selects to get additional information. refresh - To update a window, with changed information, to its current status. region - A clipping boundary in device space. register - A part of internal storage having a specified storage capacity and usually intended for a specific purpose. (T) remote file system - A file-system driver that gains access to a remote system without a block device driver. resource - The means of providing extra information used in the definition of a window. A resource can contain definitions of fonts, templates, accelerators, and mnemonics; the definitions are held in a resource file. resource file - A file containing information used in the definition of a window. Definitions can be of fonts, templates, accelerators, and mnemonics. restore - To return a window to its original size or position following a sizing or moving action. retained graphics - Graphic primitives that are remembered by the Presentation Manager interface after they have been drawn. Contrast with nonretained graphics. return code - (1) A value returned to a program to indicate the results of an operation requested by that program. (2) A code used to influence the execution of succeeding instructions.(A) reverse video - (1) A form of highlighting a character, field, or cursor by reversing the color of the character, field, or cursor with its background; for example, changing a red character on a black background to a black character on a red background. (2) In SAA Basic Common User Access architecture, a screen emphasis feature that interchanges the foreground and background colors of an item. REXX Language - Restructured Extended Executor. A procedural language that provides batch language functions along with structured programming constructs such as loops; conditional testing and subroutines. RGB - (1) Color coding in which the brightness of the additive primary colors of light, red, green, and blue, are specified as three distinct values of white light. (2) Pertaining to a color display that accepts signals representing red, green, and blue. roman - Relating to a type style with upright characters. root segment - In a hierarchical database, the highest segment in the tree structure. round-robin scheduling - A process that allows each thread to run for a specified amount of time. run time - (1) Any instant at which the execution of a particular computer program takes place. (T) (2) The amount of time needed for the execution of a particular computer program. (T) (3) The time during which an instruction in an instruction register is decoded and performed. Synonym for execution time. ═══ Glossary - S ═══ SAA - Systems Application Architecture. SBCS - Single-byte character set. scheduler - A computer program designed to perform functions such as scheduling, initiation, and termination of jobs. screen - In SAA Basic Common User Access architecture, the physical surface of a display device upon which information is shown to a user. screen device context - A logical description of a data destination that is a particular window on the screen. See also device context. SCREEN$ - Character-device name reserved for the display screen. scroll bar - In SAA Advanced Common User Access architecture, a part of a window, associated with a scrollable area, that a user interacts with to see information that is not currently allows visible. scrollable entry field - An entry field larger than the visible field. scrollable selection field - A selection field that contains more choices than are visible. scrolling - Moving a display image vertically or horizontally in a manner such that new data appears at one edge, as existing data disappears at the opposite edge. secondary window - A window that contains information that is dependent on information in a primary window and is used to supplement the interaction in the primary window. sector - On disk or diskette storage, an addressable subdivision of a track used to record one block of a program or data. segment - See graphics segment. segment attributes - Attributes that apply to the segment as an entity, as opposed to the individual primitives within the segment. For example, the visibility or detectability of a segment. segment chain - All segments in a graphics presentation space that are defined with the 'chained' attribute. Synonym for picture chain. segment priority - The order in which segments are drawn. segment store - An area in a normal graphics presentation space where retained graphics segments are stored. select - To mark or choose an item. Note that select means to mark or type in a choice on the screen; enter means to send all selected choices to the computer for processing. select button - The button on a pointing device, such as a mouse, that is pressed to select a menu choice. Also known as button 1. selection cursor - In SAA Advanced Common User Access architecture, a visual indication that a user has selected a choice. It is represented by outlining the choice with a dotted box. See also text cursor. selection field - (1) In SAA Advanced Common User Access architecture, a set of related choices. See also entry field. (2) In SAA Basic Common User Access architecture, an area of a panel that cannot be scrolled and contains a fixed number of choices. semantics - The relationships between symbols and their meanings. semaphore - An object used by applications for signalling purposes and for controlling access to serially reusable resources. separator - In SAA Advanced Common User Access architecture, a line or color boundary that provides a visual distinction between two adjacent areas. serial dialog box - See modal dialog box. serialization - The consecutive ordering of items. serialize - To ensure that one or more events occur in a specified sequence. serially reusable resource (SRR) - A logical resource or object that can be accessed by only one task at a time. session - (1) A routing mechanism for user interaction via the console; a complete environment that determines how an application runs and how users interact with the application. OS/2 can manage more than one session at a time, and more than one process can run in a session. Each session has its own set of environment variables that determine where OS/2 looks for dynamic-link libraries and other important files. (2) (D of C) In the OS/2 operating system, one instance of a started program or command prompt. Each session is separate from all other sessions that might be running on the computer. The operating system is responsible for coordinating the resources that each session uses, such as computer memory, allocation of processor time, and windows on the screen. Settings Notebook - A control window that is used to display the settings for an object and to enable the user to change them. shadow - An object that refers to another object. A shadow is not a copy of another object, but is another representation of the object. shadow box - The area on the screen that follows mouse movements and shows what shape the window will take if the mouse button is released. shared data - Data that is used by two or more programs. shared memory - In the OS/2 operating system, a segment that can be used by more than one program. shear - In computer graphics, the forward or backward slant of a graphics symbol or string of such symbols relative to a line perpendicular to the baseline of the symbol. shell - (1) A software interface between a user and the operating system of a computer. Shell programs interpret commands and user interactions on devices such as keyboards, pointing devices, and touch-sensitive screens, and communicate them to the operating system. (2) Software that allows a kernel program to run under different operating-system environments. shutdown - The process of ending operation of a system or a subsystem, following a defined procedure. sibling processes - Child processes that have the same parent process. sibling windows - Child windows that have the same parent window. simple list - A list of like values; for example, a list of user names. Contrast with mixed list. single-byte character set (SBCS) - A character set in which each character is represented by a one-byte code. Contrast with double-byte character set. slider box - In SAA Advanced Common User Access architecture: a part of the scroll bar that shows the position and size of the visible information in a window relative to the total amount of information available. Also known as thumb mark. SOM - System Object Model. source file - A file that contains source statements for items such as high-level language programs and data description specifications. source statement - A statement written in a programming language. specific dynamic-link module - A dynamic-link module created for the exclusive use of an application. spin button - In SAA Advanced Common User Access architecture, a type of entry field that shows a scrollable ring of choices from which a user can select a choice. After the last choice is displayed, the first choice is displayed again. A user can also type a choice from the scrollable ring into the entry field without interacting with the spin button. spline - A sequence of one or more BВzier curves. spooler - A program that intercepts the data going to printer devices and writes it to disk. The data is printed or plotted when it is complete and the required device is available. The spooler prevents output from different sources from being intermixed. stack - A list constructed and maintained so that the next data element to be retrieved is the most recently stored. This method is characterized as last-in-first-out (LIFO). standard window - A collection of window elements that form a panel. The standard window can include one or more of the following window elements: sizing borders, system menu icon, title bar, maximize/minimize/restore icons, action bar and pull-downs, scroll bars, and client area. static control - The means by which the application presents descriptive information (for example, headings and descriptors) to the user. The user cannot change this information. static storage - (1) A read/write storage unit in which data is retained in the absence of control signals. (A) Static storage may use dynamic addressing or sensing circuits. (2) Storage other than dynamic storage. (A) style - See window style. subclass - A class that inherits from another class. See also Inheritance. subdirectory - In an IBM personal computer, a file referred to in a root directory that contains the names of other files stored on the diskette or fixed disk. superclass - A class from which another class inherits. See also inheritance. swapping - (1) A process that interchanges the contents of an area of real storage with the contents of an area in auxiliary storage. (I) (A) (2) In a system with virtual storage, a paging technique that writes the active pages of a job to auxiliary storage and reads pages of another job from auxiliary storage into real storage. (3) The process of temporarily removing an active job from main storage, saving it on disk, and processing another job in the area of main storage formerly occupied by the first job. switch - (1) In SAA usage, to move the cursor from one point of interest to another; for example, to move from one screen or window to another or from a place within a displayed image to another place on the same displayed image. (2) In a computer program, a conditional instruction and an indicator to be interrogated by that instruction. (3) A device or programming technique for making a selection, for example, a toggle, a conditional jump. switch list - See Task List. symbolic identifier - A text string that equates to an integer value in an include file, which is used to identify a programming object. symbols - In Information Presentation Facility, a document element used to produce characters that cannot be entered from the keyboard. synchronous - Pertaining to two or more processes that depend upon the occurrence of specific events such as common timing signals. (T) See also asynchronous. System Menu - In the Presentation Manager, the pull-down in the top left corner of a window that allows it to be moved and sized with the keyboard. System Object Model (SOM) - A mechanism for language-neutral, object-oriented programming in the OS/2 environment. system queue - The master queue for all pointer device or keyboard events. system-defined messages - Messages that control the operations of applications and provides input an other information for applications to process. Systems Application Architecture (SAA) - A set of IBM software interfaces, conventions, and protocols that provide a framework for designing and developing applications that are consistent across systems. ═══ Glossary - T ═══ table tags - In Information Presentation Facility, a document element that formats text in an arrangement of rows and columns. tag - (1) One or more characters attached to a set of data that contain information about the set, including its identification. (I) (A) (2) In Generalized Markup Language markup, a name for a type of document or document element that is entered in the source document to identify it. target object - An object to which the user is transferring information. Task List - In the Presentation Manager, the list of programs that are active. The list can be used to switch to a program and to stop programs. terminate-and-stay-resident (TSR) - Pertaining to an application that modifies an operating system interrupt vector to point to its own location (known as hooking an interrupt). text - Characters or symbols. text cursor - A symbol displayed in an entry field that indicates where typed input will appear. text window - Also known as the VIO window. text-windowed application - The environment in which the operating system performs advanced-video input and output operations. thread - A unit of execution within a process. It uses the resources of the process. thumb mark - The portion of the scroll bar that describes the range and properties of the data that is currently visible in a window. Also known as a slider box. thunk - Term used to describe the process of address conversion, stack and structure realignment, etc., necessary when passing control between 16-bit and 32-bit modules. tilde - A mark used to denote the character that is to be used as a mnemonic when selecting text items within a menu. time slice - (1) An interval of time on the processing unit allocated for use in performing a task. After the interval has expired, processing-unit time is allocated to another task, so a task cannot monopolize processing-unit time beyond a fixed limit. (2) In systems with time sharing, a segment of time allocated to a terminal job. time-critical process - A process that must be performed within a specified time after an event has occurred. timer - A facility provided under the Presentation Manager, whereby Presentation Manager will dispatch a message of class WM_TIMER to a particular window at specified intervals. This capability may be used by an application to perform a specific processing task at predetermined intervals, without the necessity for the application to explicitly keep track of the passage of time. timer tick - See clock tick. title bar - In SAA Advanced Common User Access architecture, the area at the top of each window that contains the window title and system menu icon. When appropriate, it also contains the minimize, maximize, and restore icons. Contrast with panel title. TLB - Translation lookaside buffer. transaction - An exchange between a workstation and another device that accomplishes a particular action or result. transform - (1) The action of modifying a picture by scaling, shearing, reflecting, rotating, or translating. (2) The object that performs or defines such a modification; also referred to as a transformation. Translation lookaside buffer (TLB) - A hardware-based address caching mechanism for paging information. Tree - In the Presentation Manager, the window in the File Manager that shows the organization of drives and directories. truncate - (1) To terminate a computational process in accordance with some rule (A) (2) To remove the beginning or ending elements of a string. (3) To drop data that cannot be printed or displayed in the line width specified or available. (4) To shorten a field or statement to a specified length. TSR - Terminate-and-stay-resident. ═══ Glossary - U ═══ unnamed pipe - A circular buffer, created in memory, used by related processes to communicate with one another. Contrast with named pipe. unordered list - In Information Presentation Facility, a vertical arrangement of items in a list, with each item in the list preceded by a special character or bullet. update region - A system-provided area of dynamic storage containing one or more (not necessarily contiguous) rectangular areas of a window that are visually invalid or incorrect, and therefore are in need of repainting. user interface - Hardware, software, or both that allows a user to interact with and perform operations on a system, program, or device. User Shell - A component of OS/2 that uses a graphics-based, windowed interface to allow the user to manage applications and files installed and running under OS/2. utility program - (1) A computer program in general support of computer processes; for example, a diagnostic program, a trace program, a sort program. (T) (2) A program designed to perform an everyday task such as copying data from one storage device to another. (A) ═══ Glossary - V ═══ value set control - A visual component that enables a user to select one choice from a group of mutually exclusive choices. vector font - A set of symbols, each of which is created as a series of lines and curves. Synonymous with outline font. Contrast with image font. VGA - Video graphics array. view - A way of looking at an object's information. viewing pipeline - The series of transformations applied to a graphic object to map the object to the device on which it is to be presented. viewing window - A clipping boundary that defines the visible part of model space. VIO - Video Input/Output. virtual memory (VM) - Synonymous with virtual storage. virtual storage - (1) The storage space that may be regarded as addressable main storage by the user of a computer system in which virtual addresses are mapped into real addresses. The size of virtual storage is limited by the addressing scheme of the computer system and by the amount of auxiliary storage available, not by the actual number of main storage locations. (I) (A) (2) Addressable space that is apparent to the user as the processor storage space, from which the instructions and the data are mapped into the processor storage locations. (3) Synonymous with virtual memory. visible region - A window's presentation space, clipped to the boundary of the window and the boundaries of any overlying window. volume - (1) A file-system driver that uses a block device driver for input and output operations to a local or remote device. (I) (2) A portion of data, together with its data carrier, that can be handled conveniently as a unit. ═══ Glossary - W ═══ wildcard character - Synonymous with global file-name character. window - (1) A portion of a display surface in which display images pertaining to a particular application can be presented. Different applications can be displayed simultaneously in different windows. (A) (2) An area of the screen with visible boundaries within which information is displayed. A window can be smaller than or the same size as the screen. Windows can appear to overlap on the screen. window class - The grouping of windows whose processing needs conform to the services provided by one window procedure. window coordinates - A set of coordinates by which a window position or size is defined; measured in device units, or pels. window handle - Unique identifier of a window, generated by Presentation Manager when the window is created, and used by applications to direct messages to the window. window procedure - Code that is activated in response to a message. The procedure controls the appearance and behavior of its associated windows. window rectangle - The means by which the size and position of a window is described in relation to the desktop window. window resource - A read-only data segment stored in the .EXE file of an application o the .DLL file of a dynamic link library. window style - The set of properties that influence how events related to a particular window will be processed. window title - In SAA Advanced Common User Access architecture, the area in the title bar that contains the name of the application and the OS/2 operating system file name, if applicable. Workplace Shell - The OS/2 object-oriented, graphical user interface. workstation - (1) A display screen together with attachments such as a keyboard, a local copy device, or a tablet. (2) (D of C) One or more programmable or nonprogrammable devices that allow a user to do work. world coordinates - A device-independent Cartesian coordinate system used by the application program for specifying graphical input and output. (I) (A) world-coordinate space - Coordinate space in which graphics are defined before transformations are applied. WYSIWYG - What-You-See-Is-What-You-Get. A capability of a text editor to continually display pages exactly as they will be printed. ═══ Glossary - X ═══ There are no glossary terms for this starting letter. ═══ Glossary - Y ═══ There are no glossary terms for this starting letter. ═══ Glossary - Z ═══ z-order - The order in which sibling windows are presented. The topmost sibling window obscures any portion of the siblings that it overlaps; the same effect occurs down through the order of lower sibling windows. zooming - The progressive scaling of an entire display image in order to give the visual impression of movement of all or part of a display group toward or away from an observer. (I) (A) 8.3 file-name format - A file-naming convention in which file names are limited to eight characters before and three characters after a single dot. Usually pronounced "eight-dot-three." See also non-8.3 file-name format. ═══ 10. Notices ═══ November 1996 The following paragraph does not apply to the United Kingdom or any country where such provisions are inconsistent with local law: INTERNATIONAL BUSINESS MACHINES CORPORATION PROVIDES THIS PUBLICATION "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Some states do not allow disclaimer of express or implied warranties in certain transactions, therefore, this statement may not apply to you. This publication could include technical inaccuracies or typographical errors. Changes are periodically made to the information herein; these changes will be incorporated in new editions of the publication. IBM may make improvements and/or changes in the product(s) and/or the program(s) described in this publication at any time. It is possible that this publication may contain reference to, or information about, IBM products (machines and programs), programming, or services that are not announced in your country. Such references or information must not be construed to mean that IBM intends to announce such IBM products, programming, or services in your country. Requests for technical information about IBM products should be made to your IBM reseller or IBM marketing representative. ═══ 10.1. Copyright Notices ═══ COPYRIGHT LICENSE: This publication contains printed sample application programs in source language, which illustrate OS/2 programming techniques. You may copy, modify, and distribute these sample programs in any form without payment to IBM, for the purposes of developing, using, marketing or distributing application programs conforming to the OS/2 application programming interface. Each copy of any portion of these sample programs or any derivative work, which is distributed to others, must include a copyright notice as follows: "(C) (your company name) (year). All rights reserved." (C) Copyright International Business Machines Corporation 1994,1996. All rights reserved. Note to U.S. Government Users - Documentation related to restricted rights - Use, duplication or disclosure is subject to restrictions set forth in GSA ADP Schedule Contract with IBM Corp. ═══ 10.2. Disclaimers ═══ References in this publication to IBM products, programs, or services do not imply that IBM intends to make these available in all countries in which IBM operates. Any reference to an IBM product, program, or service is not intended to state or imply that only that IBM product, program, or service may be used. Subject to IBM's valid intellectual property or other legally protectable rights, any functionally equivalent product, program, or service may be used instead of the IBM product, program, or service. The evaluation and verification of operation in conjunction with other products, except those expressly designated by IBM, are the responsibility of the user. IBM may have patents or pending patent applications covering subject matter in this document. The furnishing of this document does not give you any license to these patents. You can send license inquiries, in writing, to: IBM Director of Licensing IBM Corporation 500 Columbus Avenue Thornwood, NY 10594 U.S.A. ═══ 10.3. Trademarks ═══ The following terms are trademarks of the IBM Corporation in the United States or other countries or both: AT Common User Access CUA IBM IBM PC AT OS/2 Personal System/2 Presentation Manager PS/2 SAA Systems Application Architecture The following terms are trademarks of other companies: Intel Intel Corporation