top header
apartmentsterm

 

home contents introduction com as a better C++ interfaces classes apartments labs lab setup quizzes
 
previousnext

 
Apartments : The Overview

We have just talked about the idea of an execution context as being a place where objects with similar characteristics can live. Remember that an object lives in one and only one context. This defines the only place where direct calls to the object can be made. Cross-context access is achieved with a shim that maps policies from one context to another.

What is an apartment?

At the time of writing, only one type of context currently exists: the apartment. Historically, apartments exist because some objects have thread affinity. Apartments are a way of dividing up a process and defining which thread, or group of threads, an object can be executed on. Some objects must execute their method code on the same thread every time. Some objects can execute their method code on any thread from a group of threads. Some objects do not care. In order to create the illusion that all objects have the same thread affinity characteristics, each object within a process lives in one of three apartment types.

At the moment, the apartment is the smallest context that defines where an object "lives.&squot; In future, under COM+, apartments will be subdivided into other meaningful contexts based on alternative runtime criteria such as transaction requirement, synchronization requirement, role-based security requirement, etc. In this case, such a context will be the smallest scope definition of where an object lives. Calling across context has to do with matching disparate object runtime policy requirements. Calling across apartments has to do with ensuring an object is called on a compatible thread. An apartment becomes a collection of contexts whose objects share the same thread affinity requirements. Calling across apartment requires a thread switch (heavyweight proxy/stub). Calling across context within apartment does not require a thread switch (lightweight proxy/stub).

For now, in the NT4 time frame, we must think of a process as being divided into apartments (which define object thread affinity) each of which contains one context (the default context), which has no special runtime policies. In this case, calling across apartments will always require a heavyweight proxy/stub, calling within apartment will always be calling within the single default context and will not require any proxy/stub.

Where possible, this section will focus on context and context mismatch. Where necessary, it will concentrate on apartments and apartment mismatch.

Different apartment types

Single Threaded Apartments

For whatever reason, some resources are tied to the thread that created them. That is, they can only be accessed from code executing on that thread. Any object that uses such a resource is also forced to execute its method code on that thread. This has the side effect of serializing access to all objects that execute on this thread. After all, the thread can only do one thing at a time. This means such objects do not need to be coded in a thread-safe manner. Such objects live in a single threaded apartment (STA).

A single thread assigns itself to a given STA to dispatch calls to objects in that STA. Only callers in a given STA can directly touch the objects that live in that STA. A caller in another apartment has to access objects in a given STA via a shim. Because of thread affinity, this shim must perform a thread switch. In this case, the mechanism used by the shim to pass control from the caller to the STA object is to translate the call to a windows message. This message is posted onto the message queue of the STA thread. Calls are then dispatched serially by the STA thread taking message requests off the queue and calling the object in question directly. his means an STA thread must have a message loop.

wpeC.gif (8519 bytes)

Here is a diagram showing how a thread switch occurs when calling into the STA. Calls can only be dispatched within this apartment by the single STA thread. Access to objects is serialized via messages posted to STA message queue.

A process can have more than one STA. If it could not, all objects with thread affinity would have to execute on a single thread, creating a bottleneck.

Even though STA objects are thread-safe by virtue of serialized access, STA objects must serialize access to any global state. This is because objects in different STAs that are sharing state can be called concurrently. If they do not protect that shared state, they run the risk of corrupting it. Even the module lock count needs to be protected.

The Main STA

Some legacy objects don't even understand the concept of a thread. They were written before multi-threaded code was commonplace. How would they know? We have seen that regular STA objects don't need to guard their own state against concurrent access, but they do at least recognize that a caller in another STA may be concurrently accessing global state and protect it accordingly. Legacy objects are certainly not going to do this because they don't understand that they have to. Such objects must all be put in the same STA.

The first STA created in a process is called the Main STA, and this is where all such thread-unaware legacy objects get put. Access to them and their shared state is serialized. The Main STA is special to the COM runtime for reasons that we will explore later.

The Multi Threaded Apartment

Some objects do not have thread affinity and are coded to be thread-safe.By an accident of history, such objects live in the multi-threaded apartment (MTA).

Threads that aren't tied to an STA can assign themselves to this apartment to dispatch calls to objects in the MTA. So in reality, even though the MTA objects have no thread affinity, they are tied to a group of threads. Only callers in the MTA can directly touch the objects that live in the MTA. A caller in another apartment has to access objects in the MTA via a shim. Because of thread affinity (less than an STA, but it still exists), this shim will again perform a thread switch. Calls from outside of the MTA are thread-switched to be dispatched by RPC threads that live in the MTA. Such threads do not need a message loop.

wpeD.gif (4773 bytes)

Here is a diagram showing how a thread switch occurs when calling into the MTA. Calls can be dispatched within this apartment by any waiting MTA thread. Access to objects is concurrent.

A process has a single MTA, since any thread in the MTA can dispatch calls to any object in the MTA.

The Neutral Threaded Apartment or Threading Neutral Apartment (TNA)

Both an STA and the MTA mandate that a thread switch is necessary to call between them (STA<->STA or MTA<->STA). This is expensive. From Windows 2000 (W2K) onwards, objects can mark themselves as living in the neutral threaded apartment (TNA). Objects that live in this apartment truly have no thread affinity. No threads assign themselves to the TNA. Just like the other apartment types, only callers in the TNA can directly touch objects that live in the TNA. A caller in another apartment has to access objects in the TNA via a shim. Because of the lack of thread affinity, this shim will not have to perform a thread switch.

This means callers currently executing in the MTA or an STA can call to objects living in the TNA through a shim that uses the same thread. The converse is not necessarily true. We can only reuse the same thread when calling from the TNA to an object in another apartment if the target object lives in the same apartment as the calling thread is assigned to.

wpeE.gif (8210 bytes)

Here is a diagram showing how no thread switch occurs when calling into the TNA.Calls can be dispatched within this apartment by the calling thread via a lightweight shim. Access to objects is concurrent.

A process has a single TNA since any thread that transitions into the TNA can dispatch calls to any object in the TNA.

Given that the TNA will not be available until W2K is released, we will not say much more about it.

Rules of Apartments

Apartments are collections of contexts whose objects share common thread affinity. The rules of apartments are therefore consistent with the rules of context; formerly defined.

  • Every object lives in one, and only one context, for its whole lifetime. Every context lives in one, and only one apartment for the whole of its lifetime.
    • Either an STA, the MTA, or the TNA.
    • An object's apartment defines the threads that are able to touch the object
      • NT4 - Because each apartment only contains one default context at present, an object's apartment defines the only place that direct calls can be made to it
  • Callers in an object's apartment can touch it without switching threads.
    • NT4 - Because each apartment only contains one default context at present, callers in an object's apartment can touch it directly
  • Callers outside an object's apartment must access it through a proxy/stub shim.
    • Any object reference passed out of the object's apartment must be exported.
    • Any exported object reference to be used outside of the exporting objects apartment must be imported into the using apartment.
    • This may or may not involve a thread switch.
  • In order to use COM, a thread must assign itself to an STA or the MTA, where it stays assigned for the whole of its lifetime.
    • A thread cannot reassign from one STA to another.
    • A thread cannot reassign from an STA to the MTA.
    • A thread cannot reassign from the MTA to an STA.
    • A thread in an STA or threads in the MTA can temporarily transition through a proxy/stub shim to execute an object in the TNA  .

Let's go on to look at the basic details of apartments.


Top of document