Understanding COM Apartments, Part I

Let me begin my inaugural column for CodeGuru by stating that I'm on a crusade-a crusade to stamp out bugs related to COM concurrency. COM features a concurrency mechanism that's capable of intercepting and serializing concurrent method calls to objects that were designed to process only one method call at a time. That mechanism centers around the notion of abstract boundaries called apartments. When I troubleshoot COM systems that don't work, probably 40% of the bugs that I find result from a lack of understanding of apartments. This deficiency of knowledge shouldn't be surprising, because apartments are at once one of the most complex areas of COM and also the least well documented. Microsoft's intentions were good, but when they introduced apartments to COM in Windows NT 3.51, they laid a mine field for unwary developers. Play by the rules and you can avoid stepping on mines. But it's hard to obey the rules when you don't know what the rules are.

This article is the first in a two-part series that describes what apartments are, why they exist, and how to avoid the problems that they introduce. In Part 1, I'll describe COM's apartment-based concurrency mechanism. In Part 2, I'll provide a set of rules that you can follow to avoid some of the nastiest and most insidious bugs that afflict COM programmers.

Apartment Basics

An apartment is a concurrency boundary; it's an imaginary box drawn around objects and client threads that separates COM clients and COM objects that have incompatible threading characteristics. The primary reason that apartments exist is to enable COM to serialize method calls to objects that aren't thread-safe. If you don't tell COM that an object is thread-safe, COM won't allow more than one call at a time to reach the object. Tell COM that the object is thread-safe, however, and it will happily allow the object to field concurrent method calls on multiple threads.

Every thread that uses COM, and every object that those threads create, is assigned to an apartment. Apartments never span process boundaries, so if an object and its client reside in two different processes, then they reside in different apartments, too. When a client creates an in-proc object, COM must decide whether to place the object in its creator's apartment or in another apartment in the client process. If COM assigns the object and the thread that created it to the same apartment, then the client has direct, unimpeded access to the object. But if COM places the object in another apartment, calls to the object from the thread that created it are marshaled.

Figure 1 depicts the relationship between threads and objects that share an apartment, and threads and objects that are assigned to different apartments. Calls from thread 1 travel directly to the object that it created. Calls from thread 2 go through a proxy and a stub. COM creates the proxy/stub pair when it marshals the interface pointer to thread 2's apartment. As a rule, an interface pointer must be marshaled when it's passed across apartment boundaries. This means that when custom interfaces are involved, the same proxy/stub DLL (or type library if you prefer typelib marshaling) you use to provide marshaling support for cross-process and cross-machine method calls is needed even for in-proc objects if those objects will be communicating with clients in other apartments.


Click here for larger image

Figure 1: Calls to objects in other apartments are marshaled, even if the object and its caller belong to the same process.

Windows NT 4.0 supports two different types of apartments; Windows 2000 supports three. The three types of apartments are:

  • Single-threaded apartments, or STAs (Windows NT 4.0 and Windows 2000)
  • Multithreaded apartments, or MTAs (Windows NT 4.0 and Windows 2000)
  • Neutral-threaded apartments, or NTAs (Windows 2000 only)

Single-threaded apartments are limited to one thread each, but can host an unlimited number of objects. Additionally, COM places no limit on the number of STAs in a given process. The very first STA created in a process is referred to as the process's main STA. What's important about STAs is that every call destined for an object in an STA is transferred to the STA's thread before being delivered. Since all of the object's calls execute on the same thread, it's impossible for an STA-based object to execute more than one call at a time. COM uses STAs to serialize incoming calls to non-thread-safe objects. If you don't explicitly tell COM that an object is thread-safe, it will place instances of that object in an STA so the object won't suffer concurrent thread accesses.

One of the more interesting aspects of an STA's operation is how COM transfers calls destined for an STA-based object to the STA's thread. When it creates an STA, COM creates a hidden window to go with it. The window is accompanied by a window procedure that knows how to handle private messages representing method calls. When a method call destined for an STA comes out of COM's RPC channel, COM posts a message representing that call to the STA's window. When the thread in the STA retrieves the message, it dispatches it to the hidden window, and the window's window procedure delivers the call to the stub. The stub, in turn, executes the call to the object. Because a thread retrieves, dispatches, and processes just one message at a time, putting an object in an STA enacts a crude (but effective) call serialization mechanism. As shown in Figure 2, if n method calls are placed to an STA-based object at exactly the same time, the calls are queued and delivered to the object one at a time.


Click here for larger image

Figure 2: Calls entering an STA are converted into messages and posted to a message queue. Messages are retrieved from the message queue and converted back into method calls one at a time by the thread running in the STA.

Something equally important happens when a call leaves an STA. COM can't simply allow the thread to block inside the RPC channel, because a callback would induce deadlock. (Imagine what would happen if an STA thread called an object in another apartment, and that object, in turn, called an object in the calling thread's apartment. If the thread were blocked, the call would never return because the one and only thread that can process the callback is waiting in the RPC channel for the original call to return.) Therefore, when a call leaves an STA, COM blocks the calling thread is such a way that the thread can be awakened to process callbacks. To enable such callbacks to occur, COM tracks the causality of each and every method call so it can recognize when an STA thread that's waiting in the RPC channel for a call to return should be released to process another incoming call. By default, a call that arrives at the entrance to an STA blocks if the STA's thread is currently waiting for an outbound call to return and the inbound and outbound calls are not part of the same causality chain. You can change this default behavior by writing a message filter, but that's a topic for another day.

Multithreaded apartments are different animals altogether. COM limits each process to one MTA, but it places no limit on the number of threads or objects in an MTA. If you look inside a process, you might find several STAs containing one thread each, but you'll never see more than on MTA. However, that one MTA, if it exists, can host any number of threads.

How do MTAs differ from STAs? Besides the fact that each process is limited to one MTA and that a given MTA can host any number of threads, an MTA has no hidden window and no message queue. Calls inbound to an object in an MTA are transferred to threads randomly selected from an RPC thread pool and are not serialized (see Figure 3). This means that objects placed in an MTA better be thread-safe, because in the absence of an external synchronization mechanism guaranteeing that an MTA-based object will only receive one call at a time, that object is likely to see calls execute concurrently on different RPC threads.


Click here for larger image

Figure 3: Calls entering an MTA are transferred to RPC threads but are not serialized.

When a call leaves an MTA, COM does nothing special. The calling thread is simply allowed to block inside the RPC channel, and if a callback occurs, no deadlock will occur because the callback will be transferred to another RPC thread.

Windows 2000 introduced a third apartment type: the neutral-threaded apartment, or NTA. COM limits processes to a maximum of one NTA each. Threads are never assigned to the NTA; the NTA hosts objects only. What's important about the NTA is the fact that calls to NTA-based objects do not incur thread switches as they enter the NTA. In other words, when a call emanates from an STA or MTA to an NTA in the same process, the calling thread temporarily leaves the apartment it's in and executes code directly in the NTA. Contrast this to STA- and MTA-based objects, which always incur thread switches when called from other apartments. This thread switching accounts for the bulk of the overhead incurred when a call is marshaled between apartments. Eliminating these thread switches improves performance. Therefore, you can think of the NTA as an optimization that allows interapartment method calls to execute more efficiently. In addition, Windows 2000 supports an external synchronization mechanism based on activities that lets you specify separately whether calls to NTA-based objects should be serialized. Activity-based call serialization is more efficient than message-based serialization and can be enacted (or not enacted) on an object-by-object basis.

How Threads are Assigned Apartments

One of the cardinal rules of COM programming is that every thread that uses COM in any way must first initialize COM by calling either CoInitialize or CoInitializeEx. When a thread calls either of these functions, it is placed in an apartment. What type of apartment it's placed in depends on which function the thread called and how it called it. If a thread calls CoInitialize, COM creates a new STA and places the thread inside it:
CoInitialize (NULL); // STA

If the thread calls CoInitializeEx and passes in the parameter COINIT_APARTMENTTHREADED, it, too, is placed in an STA:

CoInitializeEx (NULL, COINIT_APARTMENTTHREADED); // STA

Calling CoInitializeEx with a COINIT_MULTITHREADED parameter places the thread inside the process's one and only MTA:

CoInitializeEx (NULL, COINIT_MULTITHREADED); // MTA

To a very large extent, a process's apartment configuration is driven by how the threads in that process call CoInitialize[Ex]. There are instances in which COM will create a new apartment outside of a call to CoInitialize[Ex], but for now we won't muddy the water by considering such circumstances.

For the sake of example, suppose that a new process is begun and that a thread in that process (thread 1) calls CoInitialize:

CoInitialize (NULL); // Thread 1

Furthermore, suppose that thread 1 starts threads 2, 3, 4, and 5, and that these threads initialize COM with the following statements:

CoInitializeEx (NULL, COINIT_APARTMENTTHREADED); // Thread 2
CoInitializeEx (NULL, COINIT_MULTITHREADED);     // Thread 3
CoInitializeEx (NULL, COINIT_MULTITHREADED);     // Thread 4
CoInitialize (NULL);                             // Thread 5

Figure 4 shows the resulting apartment configuration. Threads 1, 2, and 5 are assigned to STAs because of how they called CoInitialize and CoInitializeEx. They're placed in separate STAs because STAs are limited to one thread each. Threads 3 and 4, on the other hand, go in the process's MTA. Remember, COM never creates more than one MTA in a given process, but it's willing to place any number of threads in that MTA.


Click here for larger image

Figure 4: A process with five threads distributed among three STAs and one MTA.

If you're a nuts and bolts person, you might be curious to know more about the physical nature of an apartment-that is, how COM represents apartments internally. Whenever it creates a new apartment, COM allocates an apartment object on the heap and initializes it with important information such as the apartment ID and apartment type. When it assigns a thread to an apartment, COM records the address of the corresponding apartment object in thread-local storage (TLS). Thus, if COM is executing on a thread and it wants to know which, if any, apartment the thread belongs to, all it has to do is reach into thread-local storage and look for the address of an apartment object.

How In-Proc Objects are Assigned Apartments

Now that we know how threads are assigned to apartments, we should consider the other half of the equation-that is, how objects are assigned apartments. The algorithm that COM uses to decide which apartment to create an object in differs depending on whether the object is an in-proc object or out-of-proc object. The in-proc case tends to be the most interesting, because only in-proc objects can be created in their creator's apartment. We'll discuss the in-proc case first, and then double back to discuss apartment considerations for out-of-proc objects.

COM determines which apartment an in-proc object will be created in by reading the object's ThreadingModel value from the registry. ThreadingModel is a named value assigned to the InprocServer32 key that identifies the object's DLL. The following registry entries, shown here in REGEDIT format, identify an object whose CLSID is 99999999-0000-0000-0000-111111111111, whose DLL is MyServer.dll, and whose ThreadingModel is Apartment:

[HKEY_CLASSES_ROOT\CLSID\{99999999-0000-0000-0000-111111111111}]
@="My Object"
[HKEY_CLASSES_ROOT\CLSID\{99999999-0000-0000-0000-111111111111}
\InprocServer32]
@="C:\\COM Servers\\MyServer.dll"
"ThreadingModel"="Apartment"

Apartment is one of four threading models supported by Windows NT 4.0, and one of five supported by Windows 2000. The five threading models-and the operating systems in which they're supported-are:

ThreadingModel Apartment Type NT 4.0 Windows 2000
None Main STA X X
Apartment Any STA X X
Free MTA X X
Both STA or MTA X X
Neutral NTA   X

The column labeled "Apartment Type" indicates how COM treats an object with the designated ThreadingModel value. For example, COM restricts an object that has no ThreadingModel value ("ThreadingModel=None") to the process's main STA. ThreadingModel=Apartment allows the object to be created in any STA (not just the main STA), while ThreadingModel=Free restricts the object to the MTA and ThreadingModel=Neutral restricts it to the NTA. Only ThreadingModel=Both offers COM any real choice in the matter by giving it permission to create an object in either an STA or MTA.

COM tries its best to place in-proc objects in the same apartments as the threads that create them. For example, if an STA thread creates an object marked ThreadingModel=Apartment, then COM will create the object in the creating thread's STA. If an MTA thread creates a ThreadingModel=Free object, COM will place the object in the MTA alongside the creating thread. Sometimes, however, COM can't put an object in its creator's apartment. If an STA thread, for example, creates an object marked ThreadingModel=Free, then the object will be created in the process's MTA and the creating thread will access the object through a proxy and stub. Similarly, if an MTA thread creates a ThreadingModel=None or ThreadingModel=Apartment object, calls from that thread will be marshaled from the MTA to the object's STA. The following table documents what happens when a thread in either an STA or MTA creates an object marked with any valid ThreadingModel value (or no ThreadingModel value):

  None Apartment Free Both Neutral
STA Main STA Creator's STA MTA Creator's STA NTA
MTA Main STA STA MTA MTA NTA

Why does ThreadingModel=None restrict an object to a process's main STA? Because only then can COM ensure that multiple instances of an object that knows nothing about thread safety can execute safely. Suppose that two ThreadingModel=None objects are created from the same DLL. If the objects access any global variables in that DLL (and they almost certainly will), COM must execute all calls to both objects on the same thread, or else the objects might attempt to read or write the same global variable at once. Restricting object instances to the main STA is COM's way of getting the objects on the same thread.

Although it might not be obvious at first, the threading model that you choose has important implications for the code that you write. For example, an object marked ThreadingModel=Free or ThreadingModel=Both should be completely thread-safe since calls to MTA-based objects aren't serialized. Even a ThreadingModel=Apartment object should be partially thread-safe, because ThreadingModel=Apartment doesn't prevent multiple objects created from the same DLL from colliding over shared data. We'll explore this subject in my next column.

How Out-of-Proc Objects are Assigned Apartments

Out-of-process objects don't have ThreadingModel values because COM uses a completely different algorithm to assign out-of-proc objects to apartments. To make a long story short, COM places an out-of-proc object in the same apartment as the thread in the server process that creates the object. Most out-of-proc (EXE) COM servers begin by calling either CoInitialize or CoInitializeEx to place their primary thread in an STA. They then create class objects for the object types that they're capable of creating and register them with CoRegisterClassObject. When an activation request reaches a server that's initialized this way, the request is processed in the process's STA. As a result, objects created in the server process are placed in the process's STA, too.

You can move out-of-proc objects to the MTA by placing the thread that registers the objects' class objects in the MTA. Incoming activation requests will then arrive on RPC threads that execute in the server process's MTA. Objects created in response to these activation requests will reside in the MTA as well.

The upshot is that in most cases involving EXE COM servers, the apartment that hosts the thread that calls CoRegisterClassObject is also the apartment that hosts the objects that the server creates. Exceptions do exist; EXE COM servers written with ATL's CComAutoThreadModule and CComClassFactoryAutoThread classes, which create multiple STAs in the server process and divide objects evenly among those STAs, are one example. These, however, account for a tiny fraction of the EXE COM servers that exist today, and can very much be considered the exception rather than the rule.

Coming Up Next

So what does it all mean? Much of the detail presented in this article may seem too arcane to have any practical value. The reality, however, is that understanding COM apartments is absolutely essential if you want to avoid some of the most common-and potentially most dangerous-pitfalls that afflict COM programmers. You'll see what I mean in my next column.


Comments

  • Pas cher rose ghd en vente

    Posted by gciddm350 on 07/16/2013 10:00am

    La meilleure chose à faire lorsque vous essayez de trouver défrisants bon marché est d'examiner la véritable valeur de n'importe quelle marque. Si vous allez à débourser plus de 100 £ pour quelqu'un, assurez-vous que vous obtenez votre argent vaut. Lire les commentaires des consommateurs, car ce sont les commentaires les plus honnêtes que vous obtiendrez dans l'industrie. Cela vous aidera à prendre la bonne décision lorsque vous êtes à la recherche pour les cheveux rettetang.Selvfølgelig pas cher est rose ghd n'est pas seulement une norme de tourbière styler ghd. C'est un ghd qui a été conçu pour recueillir des fonds pour des organisations caritatives du cancer du sein. En effet, un tel succès a été redresseurs rose que selon GHD il a abouti à 1 million de livres étant donné. Il ya aussi un grand succès auprès des célébrités comme Jennifer Aniston et Victoria Beckham. [url=http://ghdpascherferfr.blinkweb.com/]ghd pas cher lisseur[/url] Lisseur GHD est un outil de style professionnel avec revêtement avancé de perles en céramique pour une distribution uniforme de la chaleur et de douceur glissent à revêtement céramique normal. Il ne sera pas coincé dans les cheveux et le rendre brillant et droite à zéro tid.Høytpresterende redresseur avec cordon de revêtement en céramique et chauffage rapide à 10 sekunder.Den fonctionnalités réglage automatique de la tension pour le bon fonctionnement dans le monde, il s'éteint automatiquement après 60 minutes et il a un fil à charnière de 3 mètres. Une pochette de rangement résistant luxueux et chauffage inclus. [url=http://ghdpascherferfr.blinkweb.com/]ghd pas cher lisseur[/url] Tout d'abord le GHD défrisant pour cheveux IV a peu de concurrence très réaliste, GHD considéré comme un niveau de consommation et professionnels (coiffeurs) comme le numéro un des outils de coiffure. Deuxièmement, le développement de technologies qui réduisent les coûts de production - et ma réponse est que GHD n'est pas allé sur les coûts de production d'ions de réduction au détaillant (propriétaires de salon) et les prix réels des coûts ont augmenté en raison de l'augmentation du prix indicatif. Retours à vide MK1 à Mk3 ont été en vente qu'ils avaient un prix public conseillé de 99 EUR, maintenant GHD MK4 styler a un RRP £ 119

    Reply
  • Precio de oro series GHD, comentarios ghd tienda online

    Posted by hanmeihm on 05/30/2013 12:44pm

    [url=http://www.ghdplanchasonline.manifo.com/]planchas ghd baratas[/url] Un nmero de consumidores es elementos altamente alrgicas en tinte de cabello y el proceso podra ser peligroso Hacer en la consulta de su mdico y no compro nada por su cuenta, ghd outlet , pero hay que recordar que tales mercancas normales utilizando para cuidado del cabello por s mismo no es suficiente. [url=http://www.planchas-ghd.manifo.com/]Planchas Ghd[/url] Pole GHD puede ser capaz de utilizar un pao o una esponja sonoridad GHD Australia y / o hacer rizos de su cabello Offa Qian protegido durante el da hasta que finalmente el hombre le encantara niveles de sonoridad, result debido a la vanguardia Cierra los p¢rpados efectos negativos expanden humedecer vaporisateur lejos del agua. Mientras que la cola de caballo mnima pelo, tensos sus caractersticas especficas min²scula tu cabello directamente a tipos petite seguido dispersa vaporisateur inmediatamente despus de rociar en sus odos y por lo tanto la eliminacin equivalente a su mtodo de tonos del cabello ofrece forme.A Belleza Nivel crculos Jiao sobre la parte sucesiva, la mayora en particular, y tambin el apoyo. [url=http://www.ghdplanchasonline.manifo.com/]planchas ghd baratas[/url] Estas planchas rojas tienen un significado doble voltaje se puede obtener un rendimiento uniforme en todo el mundo ideal para cuando vas en sus viajes. Tambin con un sistema incorporado en el modo de suspensin tus alisadores poco a poco se empiezan a cerrar despus de 30 minutos de haber sido utilizado C que le ahorra la preocupacin de si est¢ o no les apag!

    Reply
  • Jordan shoes mentioned Gene to buy the brand, a margin of Nike

    Posted by TaddyGaffic on 04/19/2013 09:24pm

    Where did that get us? A bunch of banks writing loans that they didnt care if poeple would be able to pay for because they were conforming [url=http://markwarren.org.uk/goodbuy.cfm]nike free run uk[/url] loans and Fannie and Freddie would back them. And their $150+ billion losses show that they are just as unable to predict or control the market as the rest of us. It won't work because it doesn't reward investors for taking the risks involved. In order to set a good example of following your dreams, you may wish to consider strictly limiting, or eliminating TV from your life. When people are involved in pursuing their dreams they often find that they do not have the time to watch TV. TV just gets in the way of pursuing other dreams.. Take a limousine ride with Aerosmith on one of the fastest rollercoaster you have to face. Live shows throughout the day from Beauty and the Beast [url=http://northernroofing.co.uk/roofins.cfm]nike free run uk[/url] will bring memories flooding back for young and old. You can get closer to the action and feel that he wanted to be in the spotlight. Other technology advancements are the midsole. It has a compression molded EVA for lasting impact protection. A Vibrakill shock-absorber in the heel provides a lot of [url=http://turbo-vac.co.uk/components_13.cfm]nike free womens[/url] comfort, and the Exact Pro technology combines a pebax plate and a Dynamic camflex in the forefoot for improved energy return on every step. Meindl Borneo Lady Pro - This shoe is just one of my wifes most popular hiking boots. It is appropriate for lengthy outdoor hikes and you can actually do a tiny stretch of hill hiking whilst sporting them. This product also includes memory foam

    Reply
  • Staff Engineer

    Posted by SOFSPEEL on 03/17/2010 06:44pm

    Dude this is absolutely the best article EVER on COM threading

    Reply
  • Still Relevant in 2009!!

    Posted by kalukaley on 01/30/2009 06:09pm

    Excellent job. I've been reading Prosise's articles since the mid '90s, and he still kicks butt. Jeff Prosise, Peter Norton (from the MS DOS days), Paul DiLascia, Matt Pietrek, Mark Russinovich, Jeffrey Richter, and of course Charles Petzold are the required reading for Windows programmers ... PERIOD

    Reply
  • Old article, excellent content

    Posted by danielsson on 03/15/2006 08:19am

    Very enlightening for a C++ programmer trying ta grasp COM programming.

    Reply
  • This is what I call good article

    Posted by Legacy on 12/16/2003 12:00am

    Originally posted by: Gavriloaie Andrei

    I must digest your article for the next two weeks!!!
    Your article is my bible in COM programming!!!

    Reply
  • Splendid Article!

    Posted by Legacy on 12/05/2003 12:00am

    Originally posted by: SAS

    Best explanation of COM Threading Models I have seen so far.

    Thanks Jeff!

    Reply
  • Comment

    Posted by Legacy on 10/09/2003 12:00am

    Originally posted by: Naveen Mankotia

    NTA still needs explanation

    Reply
  • a masterpiece

    Posted by Legacy on 09/24/2003 12:00am

    Originally posted by: liarma

    oh!!! a piece of master's and a guideline for novinces

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds