Implementation of OS/2 Multi-Threading Support in Smalltalk/V PM

The Smalltalk Report; May 1992

Smalltalk/V PM (hereafter referred to as VPM) is an excellent OS/2 application development environment that provides most of the needed facilities (especially in release 1.3).  When combined with a third party window editing product, such as WindowBuilder from Cooper and Peters, applications can be created easily without the OS/2 Toolkit.  In a multitasking system such as OS/2, however, complex applications frequently need to execute multiple tasks concurrently using system facilities.  This article explores the implementation of VPM and discusses how the non-Smalltalk parts of an application can communicate with the Smalltalk parts, even when the non-Smalltalk parts are running in their own threads.

Interfacing VPM to Other Products

One common requirement is an interface between application programming interfaces (APIs) provided by third parties and the VPM environment.  Typically, these APIs are packaged as one or more dynamic link libraries.  The most obvious wa to provide the interface is to follow the VPM developer's guide and create a subclass or DynamicLinkLibrary (DLL).  While this work for DLL calls that return control rapidly, it fails when control is kept for any length of time.  The effect of this failure is to "hang" the workstation until the DLL call returns.  The see this for yourself, evaluate the following with "Do it":
DosLibrary sleep: 10000
This will cause your entire workstation to hang for the 10 seconds it takes DosSleep to return control.  Clearly, if you can't guarantee the DLL call will return control quickly, another implementation must be found.  This causes particular problems for communications packages, since the time it takes to return control depends on the network and the partner program!  In fact, all of the research for this article was done while developing an APPC interface for VPM.

Solving the Interfacing Problem

To an OS/2 programmer, the solution to this problem would be simple: Create another thread of execution, and let it wait for the response from the DLL call.  In the VPM environment, however, life is a little more complicated since it does not support OS/2 threads directly.  An additional OS/2 thread is the best way to solve this problem.  The only thing to be worked out is the means of communicating between VPM and the thread.  OS/2 provides many ways to accomplish multithread communication, using PM messages in the easiest and most robust way because PM messages are already used extensively in VPM.  To understand how to do this, some background information is needed.

VPM Implementation

VPM provides support for multitasking via the Process, ProcessScheduler, and Semaphore classes.  As long as all of the sub-tasks of the application are strictly Smalltalk code, this method works quite well.  Most VPM application developers don't care if this multiprocessing is simulated and does not actually use OS/2's thread capability.  This fact becomes critical, however, when interfacing VPM to other products that will be called from a lower-level language such as C.

The first thing to understand is how VPM uses OS/2 threads.  In VPM 1.3, two OS/2 threads execute when the environment is running: a Presentation Manager (PM) processing thread and a Smalltalk code executor thread.  this design is based on a PM requirement that an application return control to it quickly after processing a message.  Since a PM message might (and usually does) cause Smalltalk code to be executed, this PM requirement could not be guaranteed using a single OS/2 thread.

In the two-thread implementation, a PM message is processed by adding it to a global OrderedCollection named CurrentEvents by the PM message processing thread.  This thread immediately returns control to PM, allowing other applications to process their PM messages.  Some (typically very short) time later,  the Smalltalk code executor thread checks the CurrentEvents collection to see if there are any messages.  If any are pending, they are routed to their respective window objects.  Class NotificationManager performs this service for the code executor thread.  See its instance method #run for more details.

Since PM messages are identified by a unique message number, VPM must have a way to translate between messages numbers and method names.  This translation is done by using two global objects, PMEvents and PMExtraEvents.  PMEvents is an array of symbols, indexed by PM message number.  that is, (PMEvents at: 7) contains the value #wmSize:with:.  Seven is the message number assigned to the WM_SIZE message by PM.  The PMEvents array is not large enough to map every possible PM message.  Due to memory considerations, only about the first 478 are mapped here.  Any message numbers received that are greater than the size of PMEvents are looked up in the global Dictionary PMEventsExtra.  the PM message number (558, for example) is the ky for this dictionary, and the method name symbol is its value (#wmerror:with:).  Once the PM message has been mapped to a method name via either PMEvents or PMEventsExtra, the PM message processing thread sends a Smalltalk message to the appropriate window object using the two arguments provided by PM.  For more details look at any of the #wm... instance methods in the Window class.
 

Extension of the VPM PM Interface Model

The existing VPM message interface with PM is extended to support communications with the additional thread.  This communication is carried out by having the thread create a PM message queue and passing its handle to the VPM environment.  this handle is the used with the WinPostQueueMsg PM call to post messages to the thread.  One of the message parameters should the the handle of the notifier window that is to receive notification when the request is complete.  the thread performs whatever is requested, and posts a PM message to the notifier window.  See Figure 1 for a diagram of this interaction.

The first issue to be addressed is the selection of message numbers for communications with the thread.  A consecutive block of message numbers should be chosen for each application, avoiding conflicts.  Message numbers must be unique within one VPM image.  User-defined message numbers must be greater than 4,096 to avoid conflicts with Pm messages.  For the APPC interface, message numbers range from 29,500 to 29,505.  Each message number must have an entry in PMEventsExtra that specifies the method name associated with it.  For example, message number 29,501 is associated with the #vpmAppcVerbDone:with: method.  the message definitions used in the APPC interface are:

PMEventsExtra
    at: 29501 put: #vpmAppcVerbDone:with:
    at: 29503 put: #vpmAppcThreadQueue:with:.
To create an additional thread from the VPM environment, a DLL must be created to issue the DosCreateThread OS/2 call and to contain the code to be executed by the thread.  In Listing 1, the CreateThread function performs this action.  Parameters to CreateThread are the stack area to be used by the new thread, the size of the stack area, and the window handle to be notified when the thread is created and ready for work.  The stack is passed to the thread from the VPM environment to ensure that it is properly freed after thread termination.  The code in the Thread function initializes PM and creates a message queue.  It then posts the VPM_APPC_THREAD_QUEUE message to the AppcNotifierWindow instance, passing the queue handle as a message parameter.  The Thread function then loops until a VPM_APPC_STOP_THREAD is received, processing messages.  After each message is processed, a VPM_APPC_VERB_DONE message is posted to the AppcNotifierWindow instance.  After the VPM_APPC_STOP_THREAD message is received, the Thread function stops looping and posts the VPM_APPC_THREAD_STOPPED message, which allows us to be absolutely sure the thread has stopped before freeing the stack area.  A subclass of DynamicLinkLibrary must be created to allow the CreateThread function to be called.  The AppcDLL class is shown in Listing 2.

To communicate with the thread using PM messages, there must be a PM window to receive them.  Since this window will not perform any other functions, it should not be visible.  this may be accomplished by making it a subclass of DDEAuxWindow.  To facilitate reuse, an abstract superclass named InvisibleNotifierWindow has been created as a subclass of DDEAuxWindow.  Notifier windows for various functions are subclasses of this class.  InvisibleNotifierWindow implements the same #when:perform: interface as SubPane, allowing notifier windows to send messages to their owners when important events occur.  Listing 3 shows some of the methods defined for the AppcNotifierWindow.  as you can see, there is a method corresponding to each PM message number defined in PMEventsExtra.  Companion methods are also defined for each.  The #vpmAppc... methods are executed on behalf of the PM message processing thread.  they copy the message parameters to instance variables if necessary and add the message event to CurrentEvents via the #sendInputEvent: method.  The return from a #vpmAppc... method causes VM to return control to PM, allowing other applications to perform window operations.

A Real Example

Let's trace through a complete interaction sequence between VPM and the thread.  The interaction of interest is the thread notifying VPM that it has created its message queue and passing the queue handle as a message parameter.  This is implemented in the initialize instance method of the AppcConversation class and is shown in Listing 4.  Here are the steps:

Summary

Using the concepts explored in this article, interfaces may be written from VPM to any long-running application without adversely affecting other applications running in the system.

Send mail to webmaster@dsbconsulting.com with questions or comments about this web site.
Copyright © 1997-2003 DSB Consulting Inc.