Advanced COM COM apartments and threads

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 objects

Now 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:
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
By application we mean not the entire application but only the code that creates the thread.

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).

newObjects Copyright 2001-2006 newObjects [ ]