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

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.

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.

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

|