3.12 Application Processes, Threads and Blocking Functions

An application process contains one or more threads of execution. The WOSA/XFS interface is designed to work in both the single-threaded versions of the Windows operating systems (Windows 3.1 and Windows for Workgroups) and in the multi-threaded versions of Windows (Windows NT and future versions of Windows). All references to threads in this document refer to actual threads in multi-threaded Windows environments. In single-threaded environments, “thread” is synonymous with “process.”

Within the XFS Manager, a blocking (synchronous) function is handled as follows: The XFS Manager initiates the operation, and then enters a loop in which it dispatches any Windows messages (thus yielding the processor to other applications as necessary) and checks for the completion of the operation. When the operation completes, or WFSCancelBlockingCall is invoked, the blocking operation completes with an appropriate result.

When a Windows message is received for a thread for which a blocking operation is in progress, the thread is not permitted to issue any WOSA/XFS calls during the processing of the message, other than the two specific functions provided to assist the programmer in this situation:

· WFSIsBlocking determines whether or not a blocking call is in progress.

· WFSCancelBlockingCall cancels a blocking call in progress.

Any other WOSA/XFS function called when a blocking call is in progress fails with the error WFS_ERR_OP_IN_PROGRESS. This restriction applies to requests for both blocking and non-blocking operations.

Although this mechanism is sufficient for simple applications, it cannot support those applications which require more complex message processing while blocked for a synchronous call, such as processing messages relating to MDI (multiple document interface) events, accelerator key translations, and modeless dialogs. For such applications, the WOSA/XFS API includes the function WFSSetBlockingHook, which allows the programmer to define a special routine which will be called instead of the default message dispatch routine described above. This function gives an application the ability to execute its own routine at blocking time in place of the default routine. It is not intended as a mechanism for performing general application functions while blocked; it is still true that the only WOSA/XFS functions that may be called from a blocking routine are WFSIsBlocking and WFSCancelBlockingCall. The asynchronous versions of the WOSA/XFS functions must be used to allow an application to continue processing while an operation is in progress.

This mechanism is provided to allow a Windows 3.x or Windows for Workgroups application to make blocking calls without blocking the rest of the system. Under Windows NT and future multi-threaded, preemptive versions of Windows, the default blocking action is to suspend the calling application's thread until the request completes. This is because the system is not blocked by a single application waiting for an operation to complete, and hence not calling PeekMessage or GetMessage, which are required in the non-preemptive systems in order to cause the application to yield control.

Therefore, if a single-threaded application is targeted at both single- and multi-threaded environments, and relies on this functionality, it should install a specific blocking hook by calling WFSSetBlockingHook, even if the default hook would suffice. This maximizes the portability of applications that depend on the blocking hook behavior. Programmers who are constrained to use blocking mode—for example, as part of an existing application which is being ported—should be aware of the semantics of blocking operations.

In the WOSA/XFS implementation in a single-threaded environment, the blocking function operates as follows. When an application requests a blocking WOSA/XFS API function, the XFS Manager initiates the requested function and then enters a loop which is equivalent to the following pseudocode:
for(;;) {
 /* flush messages for good user response */
 DefaultBlockingHook();
 /* check for WFSCancelBlockingCall() */
 if( operation_cancelled() )
  break;   
 /* check to see if operation completed */
 if( operation_complete() )
  break;   /* normal completion */
}

The DefaultBlockingHook routine is equivalent to:
BOOL DefaultBlockingHook(void) {
 MSG msg;
 BOOL ret;
 /* Wait for the next message */ 

ret = GetMessage(&msg, NULL, 0, 0);

 if( (int) ret != -1 ) {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }
 /* FALSE if we got a WM_QUIT message */
 return( ret );
}

In a multi-threaded environment, the developer of a multi-threaded application must be aware that it is the responsibility of the application, not the XFS Manager, to synchronize access to a service by multiple threads. Failure to synchronize calls to a service leads to unpredictable results; for example, if two threads "simultaneously" issue WFSExecute requests to send data to the same service, there is no guarantee as to the order in which the data is sent. This is true in general; the application is responsible for coordinating access by multiple threads to any object (e.g., other forms of I/O, such as file I/O), using appropriate synchronization mechanisms. The XFS Manager can not, and will not, address these issues. The possible consequences of failing to observe these rules are beyond the scope of this specification.

In order to allow maximum flexibility in the design and implementation of applications, especially in multi-threaded environments, the concept of "application identity" can optionally be managed explicitly by the application developer using the concept of application handles. See Sections 3.5 and 3.8.2 for additional discussion of this concept.