| To understand this page you need basic understanding of the
              threads and some basic knowledge about COM (Component Object
              Model). If you have some skills in COM programming in C++ it will
              only help but is not a requirement. What are the threads?
                Most modern Operating Systems (OS) support this feature - a
                parallel execution of part of the program in the same memory
                space. In other words the program is able to do its work in
                several parallel logical threads. The OS is responsible to hide
                the actual hardware abilities (number of processors etc.). Apparently
                there is a problem - the synchronization. All the
                threads use the same memory and even if they are planned to use
                different variables at some point they must cooperate by passing
                data to each other. As they are parallel any data accessed by
                more than one of them is in danger - the thread can be
                interrupted by the OS at any point and there is no guarantee the
                data will be consistent when two or more threads write at the
                same place in memory. For the purpose the OS supplies
                synchronization objects and the threads can synchronize between
                each other and access the critical data sequentially.
               The COM objects and the threads
                COM objects are code/data integration that follows a binary
                standard which allows these objects to communicate to each
                other. But they are created by applications - which in turn have
                one or more threads. So certain COM object is always created in
                certain thread and when it calls another object the other object
                can be in the same or in another thread (or in another process
                which is out of our interest here). So the thread
                synchronization problems are COM synchronization problems as
                well. As the COM objects can be created by the application in
                various places (in different threads and under different circumstances) there is
                no simple solution for the synchronization problems. As there
                are other considerations related to another OS features the
                problem becomes even harder. Still there are simple solutions
                built-in - supplied by the COM libraries of the OS. To handle
                the different situations COM separates the objects in two major categories - Apartment
                threaded and Free threaded. Here comes
                the term COM apartment. COM Apartment may contain one or
                many threads and there are two apartment types Single
                threaded and Multi-threaded. The apartments are
                created by the application that uses the COM objects. The system
                COM library is initialized in each thread created by the
                application. Initializing it as single-threaded or
                multi-threaded tells the COM how to deal with the objects
                created further by the thread. All the threads with
                multi-threaded initialization form together one and only
                multi-threaded COM apartment, while each thread with
                single-threaded COM initialized form a separate single threaded COM apartment.
                Further if a thread creates new COM object it will be created by
                default in the apartment the thread belongs to and all the calls
                between objects in the same apartment are direct - i.e. COM does
                nothing to synchronize or translate them. On contrary if the
                objects that call each other are in separate apartments (two
                single-threaded or one multi-threaded and one single-threaded)
                COM handles the calls through internally created pair of
                proxy-stub objects which are responsible for the
                synchronization. How the synchronization works? COM
                proxy-stubs take the full responsibility for the synchronization
                for the objects in the single-threaded apartments. To do so the
                system COM library uses a message loop in each of the single-threaded apartments
                (remember they contain only one thread each). And all the calls
                to or from that apartment are serialized - so only one call can
                be in progress. Furthermore the call is always processed in the
                same thread - the thread of the single-threaded apartment where
                the object resides. So internally the call is just queued and
                the apartment (the thread) picks it from the queue and executes
                it.  Therefore no matter how many threads with single-threaded
                COM apartments you have your application will most likely work
                as like it has only a single thread if there is a chain of
                objects calling each other in the separate threads. E.g. if an
                object in the first apartment calls an object from the second
                apartment the first will wait the call to complete before any
                other call from any apartment can proceed and so on - the entire
                chain of calls must complete before any other call would be
                allowed. Thus the price for this "automatic"
                synchronization is the reduction of the actual number of the
                concurrent logical threads in the application. To avoid this
                issue the applications that need to support parallel processing
                and wide variety of components in the same time most often rely
                on free-threaded COM objects. The free threaded COM objects are
                characterized by the fact that they are designed to process
                calls from different threads without help from the COM library.
                So, they take the responsibility for the synchronization on
                themselves. Usually this is done with the so called critical
                sections which allow the object to lock/unlock the access to
                certain member variables. Unfortunately this technique is based
                on the execution flow, so the actual effect is that the
                execution of certain methods (or part of them) will wait until
                another one finishes its "critical section" - i.e.
                unlocks the code flow. This technique works perfectly if
                considered for outside calls only - in other words object
                processes calls from other objects/parts of the application.
                However there is set back if a callback technique must be
                implemented - a scenario in which the object calls member of
                another object and then this second object needs to call back
                one or more methods on the sourcing object during the process of
                the initial call. Apparently it is very important to place the
                lock/unlock points in the COM objects involved very carefully in
                order to avoid dead locks. This is quite difficult and sometimes
                even impossible. The above issue means that very often
                free-threaded objects not designed to work in scenarios that
                include call back techniques may cause troubles if forced to
                participate in such applications.
               Using threads with COM objectsNow we can try to illustrate the above. You will see that the
                ActiveX Pack1 library contains two thread classes COMThread
                and COMScriptThread. The
                first class controls a thread and when it is started it
                initializes COM in it. Then the application uses this thread
                through the COMThread object. So, suppose we have created the
                object and activated (Activate
                method) it, after that we have the Execute
                method which actually posts a request for a call. As seen from
                its parameters you specify an object and a method to be called
                on it. 
               
 What happens then? The COMThread object posts a
                message in the message loop of the thread it manages. The thread
                retrieves it and performs the call. Therefore the call occurs in
                the thread and not in the thread of the application that created
                the COMThread object. Up to this point everything is simple but
                lets consider the object on which the specified method is
                called. The most important question is "where is this
                object". There are several possibilities:
                 It can be in the application's COM apartment - i.e. in the
                    same apartment where is the code that has created the
                    COMThread object. Depending on the threading model we may
                    have:
                     
                      By application we mean not the entire application but only the
                code that creates the thread.
                        | Application threading model | Object's threading model (as registered) | Location of the object |  
                        | Single threaded | apartment or both | The same apartment as the application |  
                        | Single threaded | Free threaded | In a separate free threaded apartment |  
                        | Free threaded | both or free threaded | In the same multi threaded apartment as
                          the application. |  
                        | Free threaded | apartment | In a separate (new) single threaded
                          apartment |  On the other hand we are able to instruct the COMThread
                object to initialize the thread it manages as multi-threaded or
                as single threaded COM.  Now our application's code (which created the thread) is
                running in a COM apartment. If it is a single threaded
                apartment then this apartment is not able to process any
                external calls because our code executes in it and any call
                placed for it will wait until our code finishes. This means that
                if we create an apartment threaded object and want to call
                asynchronously method on it nothing good will happen. The object
                we create will be in our (single threaded) apartment and the
                COMThread object will actually post an external call to an
                object in the apartment of our application. Thus the call will
                have a chance to proceed only after we finish everything in our
                thread. In most cases this is of no use as no parallel action
                will occur, but using thread means that we want to achieve
                something like that. It can be even worse our application may
                exit before the thread call has chance to proceed. If the situation is the same as above but we create a free
                threaded object everything will be ok as the call will not be
                sequenced through a message pump of its apartment thread. The
                call will actually occur in the thread managed by the COMThread
                object. If the application runs in free threaded apartment the
                situation will be different. The apartment threaded objects will
                be created in separate single threaded apartments and a call
                through the COMThread's thread will occur successfully because
                it involves another apartment that has nothing in common with
                our application's apartment. If the object created is free/both threaded it will be in our
                application's apartment. but it is a free threaded apartment and
                no sequencing will be performed and again the call will be
                executed asynchronously. What else we must be concerned about? If you use these
                techniques in ASP application (for example) then be aware that
                different ASP implementations may run your pages in different
                ways and you should check the documentation to see what kind of
                COM apartments are used. Also note that the script languages are
                able to run in multithreaded apartments but they perform on
                their own some of the operations typical for the single threaded
                COM apartments. So, the effect could be quite confusing and
                undeterminable. To avoid the confusion in more universal way you
                have another opportunity to ensure that the object on which
                asynchronous calls will be performed (calls from separate
                thread)  lives in apartment you control. So, by using the COMApartment
                object you can create a single threaded apartment or get access
                to the free threaded apartment in the running process (or
                implicitly create such if none exist at this moment). Then you
                will be sure about everything you create there (through the
                methods of the Pack1Creator
                object accessible through the COMApartment's Creator
                property). For example if you create a single threaded apartment
                any apartment/both threaded object created in it will be in this
                apartment and also every object directly created (using the
                special syntax allowed in the Pack1Creator.CreateObject
                you can create objects directly from the ActiveX pack1 DLL or
                from another DLL or composite definition bypassing the COM
                system). Then you can safely call it from the thread of your
                COMThread object as you will know that the call will involve
                only apartments outside your own. OS notes
                With the techniques mentioned above you can achieve almost
                everything on the desktop Windows versions (Windows
                95/98/ME/NT/XP/2k/2k3 and later), but on Windows CE based
                machines you should know that there is no support for single
                threaded apartments provided by the COM library. This means that
                everything will run in free threaded apartments.  Now remember that the script languages behave a bit strange -
                in general they are not able to process correctly calls to them
                from thread other than the thread in which they were originally
                created. So, a call through COMThread object will be impossible
                - will cause fatal error. The desktop versions of the script
                engines are more advanced but you should not rely on this even
                if it seems that everything works correctly (it is most likely
                just a coincedence!). On desktop Windows OS you can guarantee
                that any call to your script will occur in the thread where it
                is created by creating a host in a single threaded COMApartment
                (see ScriptManager2).
                Then each call from the COMThread's thread will be routed to the
                message loop of the COMApartment's single threaded apartment and
                the call will occur in its thread. But if you try to do this
                with free threaded apartment it will mean that the call from the
                COMThread's thread will be foreign for the script (from another
                thread) and the operation will fail. As mentioned on Windows CE
                (including Pocket PC, Smartphone etc.) you cannot run/call a
                script in separate thread using this technique. A solution for the scripts is COMScriptThread
                which makes its own routing of the calls to the script and
                guarantees that the script is created and used in only one
                thread. This object saves all the efforts to prepare suitable
                environment for the script (see the need of COMApartment and
                COMthread object above) and also will run perfectly if
                initialized free threaded. Which means that it also works
                correctly on Windows CE. You will see that this class (COMScriptThread)
                can also be instructed to initialize the thread it creates and
                manages as single or multi threaded COM. But the object
                sequences the calls through it on its own so the actual
                initialization will not be vital. It will have impact only on
                the performance - i.e. depending on what kind of other COM
                objects are created for internal usage by the script in the
                thread you can find that one of the both threading models will
                lead to better results (the model that fits the most of the
                objects created - e.g. if more of them live in the same
                apartment the performance will be better). Conclusion
                (You can also read the other overview of the COM
                and threads which is oriented more practically to the usage
                of the ActiveX Pack1 objects.) The developers who don't want or don't know enough about COM
                can just use the COMScriptThread object to run scripts in
                separate thread(s) asynchronously. This requires no advanced COM
                knowledge not it requires you to know details about each object
                used by your script running in this separate thread. If you have
                stronger COM understanding and enough information about the
                objects you use (suppose you want to call not scripts, but some
                other code in separate thread - for instance you may have your
                own modules written in VB or C++) you can use COMThread (and
                COMApartment if needed) to construct whatever COM environment
                they need. When using COMApartment object note that you must keep it
                alive until all the objects in it are meant to be alive. Failing
                to do so will destroy them together with the COMApartment! For
                example in ASP applications you should keep the COMApartment in
                the Application or Session until the thread or threads that use
                them are alive. Even if you use COMApartment without COMThread
                (just to make sure that certain object is created in COM
                apartment model of your choice) you must keep the apartment's
                object. The best practice is to work with well managed/well
                known number of COMApartment objects and  keep them until
                the application exits (in ASP case this means that once created
                you save them in the Application and never set them to Nothing). |