DISCLAIMER ---------- Nothing in this package should be take to imply any plans are in place to satisfy any of the requirements listed in this document. The interfaces and functions supplied by this prototype will certainly change before this type of support appears in the product, if it ever appears in the product. Support will be provided only as time allows and given our current release schedule it looks like there will be no time for supporting this package. We are however interested in feedback and comments. Especially regarding your requirements for multi-threaded support in the Visual Builder and the notification framework. Send comments to Steve Wasleski (WASLESKI at CARY). MOTIVATIONS ----------- During the development of a Conversational Communication Class Library for VisualAge C++ the following problems were encountered and had to be solved. The solution to these problems is contained in this package. 1) The VisualAge C++ Visual Builder and Open Class Library notification framework do not support multi-threaded parts. 2) Background (non-GUI) threads or applications (server applications in a client/server solution, for example) are also not supported. This is primarily due to the user interface classes being the only source of asynchronous events that start a chain of notifications. The Conversational Communication Class Library is not in this package. It may be available in a later version of VisualAge C++. Interested IBMers can get a driver. Contact Paul Morris (RPMORRIS at RTPNOTES). REQUIREMENTS ------------ The solution to our problem had to meet the following requirements. 1) Application developers that use multi-threaded parts created by others should not need to know or care that the part spawns other threads to do its work. 2) Notifications from the part must all be asynchronously processed on the same thread on which it was created even if the notifyObservers call takes place one of the part's spawned threads. 3) Event data must be supported even though the events are processed asynchronously rather than synchronously as in the standard notification mechanism. 4) This asynchronous notification enables other sources of asynchronous events other than the user via the user interface class library. This fact must be taken advantage of to support creating background threads and applications that can be driven by these new asynchronous event sources. These other requirements were identified during the creation of this prototype, but are not resolved by it. 1) Explicit IThread object support in the visual builder. 2) Asynchronous variable support. That is, a new kind of variable that will change any regular style part's notifications into asynchronous notifications. 3) Make a part's notification mechanism (list of observers) reentrant on multiple threads rather than a single thread. FILES ----- Needed to create and use asynchronous notifiers: THREADS.ZIP - Contains all these files except itself. Use PKUNZIP2 -d THREADS.ZIP to unpack into current directory. README.TXT - This file. iasyntfy.hpp - The header file for IAsyncNotifier. Note that IAsyncNotifier is an abstract base class and is a subclass of IStandardNotifier. asyncnot.dll - The DLL the contains the implementation of IAsyncNotifier. asyncnot.LIB - The import library for asyncnot.dll. asyncnot.vbb - Contains the IAsyncNotifier non-visual part. Note that IAsyncNotifier is an abstract base class. Needed to rebuild the asyncnot.dll (this is all the source code): asyncnot.def - The module definition file asyncnot.mak - Make file generated by WorkFrame/2 iasynbkg.cpp - Source for queuing to background threads iasynbkg.hpp iasyngui.cpp - Source for queuing to GUI threads iasyngui.hpp iasyntfy.cpp - Source code for IAsyncNotifier iasyntfy.hpp iasynthr.cpp - Abstract base class for queuing iasynthr.hpp ievntsem.cpp - A little event sem class (thanks to Rick Blevins) ievntsem.hpp In the SAMPLE subdirectory: counter.vbb - Contains the sample Counter non-visual part and two visual parts, CountMn and CountWnd, that use it. Counter is a concrete subclass of IAsyncNotifier. counter.hpv - Declarations of Counter features. counter.cpv - Definitions of Counter features. RUNNING THE SAMPLE ------------------ 1) Put the asyncnot.dll in a directory in your LIBPATH. The current directory when you run the sample is okay if you have .; in your LIBPATH. 2) Bring up the Visual Builder and load all the two VBB files (iasynfty.vbb and counter.vbb). I suggest starting the Visual Builder from a command line with the current directory being the SAMPLE subdirectory. The Visual Builder exe is ICSVB.EXE. 3) Generate part source for Counter. 4) Generate part source for CountMn and CountWnd. 5) Generate main for CountMn. 6) Make sure the location of iasyntfy.hpp is in the INCLUDE and asyncnot.lib is in the LIB environment variables. Run nmake on CountMn.mak. 7) Run CountMn.exe. 8) When the window comes up, press the 'Create new count window' button. A connection is fired that brings up a CountWnd window. The CountWnd part uses the Counter non-visual part that starts a thread that increments a counter every second. Notice that the counter advances, but does not hang the rest of the system since it is running on a separate thread. 9) You can create multiple instances of CountWnd by returning to the Counter Main Window and pressing the 'Create new count window' button again. 10) To exit the sample, close down all the windows. HOW TO CREATE MULTI-THREADED NON-VISUAL PARTS --------------------------------------------- 1) Make your parts subclasses of IAsyncNotifier rather than IStandardNotifier. 2) If any of your notifications will have event data, create the event data on the heap with the new operator. (The actual data, IString, etc., not the IEventData object. It can be allocated on the stack, since it will be copied INotificationEvent). Do not clean up the event data as you normally would after notifyObservers returns. You will get a chance to clean it up later as described in number 3. 3) If any of your notifications will have event data, as described in number 2, override IAsyncNotifier::notificationCleanUp. It will be called after each notification has really be processed. Your declaration will look like this: virtual const MyClass & notificationCleanUp ( const INotificationEvent & anEvent ) const; Your definition will look like this: const MyClass & MyClass :: notificationCleanUp ( const INotificationEvent & anEvent ) const { // A subclass of MyClass must call MyClass::notificationCleanUp first. IAsyncNotifier::notificationCleanUp ( anEvent ); // If this is an event that we have event data that we need to // clean up, clean it up. Note that data types that fit in 4 // or less bytes will not need cleanup (int, Boolean, unsigned long, // etc.). switch ( anEvent.notificationId() ) { case MyClass::event1Id : { MyEvent1Data * eventData = (MyEvent1Data *)(anEvent.eventData().asUnsignedLong()); delete eventData; break; } case MyClass::event2Id : { MyEvent2Data * eventData = (MyEvent2Data *)(anEvent.eventData().asUnsignedLong()); delete eventData; break; } } return *this; } HOW TO USE MULTI-THREADED NON-VISUAL PARTS ON GUI THREADS --------------------------------------------------------- Use them exactly the same way you use standard non-visual parts. You can create multiple async parts on one thread. Note that an async part does not have to have an action that explicitly starts its thread(s). They could be started in the constructor or as a by-product of normal interaction with the part. Therefore, the threading can be completely hidden even if it is not in our sample. HOW TO USE MULTI-THREADED NON-VISUAL PARTS ON BACKGROUND THREADS ---------------------------------------------------------------- Use them same way you would use standard non-visual parts on a background thread. You can create multiple async parts on one thread. That is, create a non-visual part (standard or async). Put other non-visual subparts (standard and/or async with at least one async part if the part itself is not async) on this new parts composition editor. Interconnect the part and subparts as needed. When you application sees fit, start a background thread (perhaps inside another async notifier) and create an instance of this new part on that thread. Its subparts will be created and you will have at least one (more depending on the subparts) async notifier created on this new thread. The rest of this can, probably should, be encapsulated within your new part. Do what ever is needed (if anything) at least one of the async notifiers into a state where it will produce async events (open a conversation with a remote process, for example). Finally call IAsyncNotifier::run on this new thread. Much like ICurrentApplication::run, it will not return until the thread is done with its work. In this case that is defined as all the IAsyncNotifier instances that were created on the thread have been deleted. When IAsyncNotifier::run returns, just run off the end of your thread and it will terminate. HOW TO USE MULTI-THREADED NON-VISUAL PARTS IN BACKGROUND APPLICATIONS --------------------------------------------------------------------- This is a special case of the last section. The difference is that thread 1 is already started for you. You just need to Generate Main for the new part you created as described in the last section and you will almost be there. In the generated .mak file change /pmtype:pm to /pmtype:vio. In the generated .app file: Add these lines: #ifndef _IASYNTFY_ #include #endif Remove these lines: #ifndef _IWINDOW_ #include #endif #ifndef _IMSGBOX_ #include #endif Change the main function to look like this (removed lines are commented and the added line is flagged): int main(int argc, char ** argv) { IApplication::current().setArgs(argc, argv); TestServer1 *iPart; // IMessageBox::Response resp = IMessageBox::unknown; // do { // try { iPart = new TestServer1(); iPart->initializePart(); // } // catch (IException& exc) { // resp = IMessageBox(IWindow::desktopWindow()).show(exc); // } // } // while (resp == IMessageBox::retry); // if (resp == IMessageBox::cancel) IApplication::current().exit(); // IApplication::current().run(); IAsyncNotifier::run(); // This line was added. return 0; } //end main You can, of course, create other background threads if you want in the manner described in the last section.