top header
apartmentsterm

 

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

 
COM Context, Remoting and Marshalling

Overview

We start this section with a discussion about the abstract notion of an 'execution context'.

Then we provide an overview of the primary execution context we have in COM today, namely Apartments.

The crossing of execution context boundaries is tied to the COM marshalling architecture in general and COM "standard marshalling" in particular, so we look at that too. It must be stressed here, though, that the marshalling trail is optional and is there purely as background information for the interested student.

Next, we will look at some of the basic details of apartments.  

Finally, we study how calls are dispatched within an apartment.

Execution Context

This section aims to introduce the abstract concept of an execution context and why it might be needed. The terms execution context and context will be used synonymously.

What is context?

Objects often have characteristics that define certain runtime requirements in order for them to function correctly. They have certain expectations about the ambient environment in which they will execute. If such an object is executed in a different environment, it will not work as expected. Examples might be as follows.

  • An object requires a transaction under which to perform its work. If it doesn't have one, the integrity of the data it manipulates may be compromised.
  • An object has affinity to a thread and requires that its methods always execute on that thread. This might be because it makes use of thread local storage or window handles, each of which has thread affinity.
  • An object requires that it cannot ever be accessed re-entrantly, else it will melt into a pool of object goo. Such an object does not expect to have to lock down its state while it is use.

To avoid complete anarchy we define the notion of an 'execution context,' which allows us to group together objects with the same characteristics to execute in the same controlled environment. An object lives in exactly one context and there may be many like-minded objects in the same context. We must also ensure that this segregation is as transparent as possible, so that objects with different characteristics can assume that all other objects are just like themselves.

A caller has direct access to those objects in the same context as itself. Either a caller always executes in that context or is temporarily in that context for a short period of time. Either way, its policies currently match those of the context it is in.

In order for a caller to access objects in a different context, the caller must enter the target context via some controlled mechanism that can alter the caller's environment to match those of the target context. Once this context switch occurs, the objects in the target context can be accessed directly. Emerging from the target context back to the original context causes the callers policies to be reset.

A context is necessarily a sub-division of a process, since we can never obtain a direct pointer to an object in another process.

Calling across context

A 'shim' object is used to call from one context to another. This shim object maps between the different policies of the caller's context and the target object's context. The shim is a lightweight replacement for the target object in the caller's context - it must look, feel, taste, and smell like the real target object but behave according to the policies of the caller's context. When called, the shim object transforms the caller's environment to use the policies of the called object's context for the duration of the call. Finally the shim restores the policies of the caller's context on return from the call.

The shim object must be able to perform any necessary mapping of parameters and return results as necessary between contexts. If the target object's context cannot share the stack of the caller, this may involve a technique called marshalling - serializing the call context into a buffer to be transmitted between contexts. The message-based protocol COM uses to transmit the call context (the 'remote' method call) is well defined. The mechanism used to exchange messages will differ according to the exact differences between contexts.

The process by which these shims are dynamically created when required must be seamless enough to maintain the illusion that all objects have the  same characteristics. That is, the caller and the called object must be unaware of the shim object between them. This provides us with context transparency.

It might be worth mentioning here that the details of whether the shim object requires a thread switch to facilitate the context mapping is immaterial. Typically, this will depend on the policy requirements of the disparate contexts. Some threads will execute in the same context for the whole of their lifetime. Some will jump between contexts as needed. The fact that the logical thread of execution may need to switch physical OS threads is a detail that doesnĄ¯t affect the programming model.

context

Diagram showing a caller and a called object living in different execution contexts (with and without incurring a thread switch).

How does an object specify the context it lives in?

A class specifies declarative attributes that can be picked up by the runtime environment such that the following can be enforced.

  • An object of this class must be placed in an appropriate context. If an appropriate context does not exist, one is created.
  • A shim can be dynamically created when needed to interface between an object of this class and a caller when they are in different contexts.
Examples of context (mismatch)

Let's look at some examples of different contexts where direct access would be impossible, but would have to occur through a shim.

  • The caller and target object are in different processes

    This is the most obvious example. The target object lives in a different address space than the caller. They cannot easily share any resources. Certainly they do not share the same stack. The call stack must be marshalled between one address space and the other (possibly even across the network) by the shim.

  • The caller and target object have different concurrency models

    Some developers write thread-safe object code. They spend man-months tuning multi-threaded access to their objects in order to make them thread-safe and expect multiple threads to access them concurrently in order to increase throughput to their object. In turn, some threads expect everything they touch to be thread-safe. Some developers do not write thread-safe code and rely on the caller to sort out synchronization.The caller may not want to; the shim must deal with the differences in concurrency expectations.

  • The object has affinity to a particular thread

    Some objects maintain state in thread-local storage, often to propagate information across function calls. This means the object can only be accessed directly from one thread and it must be the same thread every time. To ensure that this is the case, a thread-switch may need to occur between the calling thread and the thread used to directly access the object. The shim must sort this out.

  • The object and thread have different security characteristics

    A caller and a target object may need to represent two different security principals. The shim between them may need to perform access checks to ensure that the caller has the right to execute a particular method. Also, the target object may want to perform access checks on behalf of the caller to ensure that the caller is in a role that is allowed access to the object's resources. It may even want to impersonate the caller when accessing those resources.

  • Object and thread have different transaction requirements

    An object may have a requirement to perform its work as part of a transaction when the caller does not. A shim between them may be able to start a new transaction and enlist into the transaction any resources required by the object.

The rules of context

The rules of context are fairly simple.

  • Every object lives in one, and only one context, for its entire lifetime
    • This defines the only place that direct calls to the object can be dispatched
  • Callers inside an object's context can access it directly
  • Callers outside an object's context must access it through a shim
    • Any object reference passed out of the object's context must be exported.
    • Any exported object reference to be used outside of the exporting objects context must be imported into the using context
    • Calling through a shim may or may not involve a thread switch
Exporting and importing

To pass interface references between contexts, they must be exported and imported. This is essentially the practice of remoting an interface on an object by creating a proxy in one context which talks to a stub in another in order to provide indirect access to the object. Together, the proxy and stub represent the shim referred to formerly.

It turns out that COM has a flexible protocol (ORPC) for invoking functionality on remote objects and a well thought-out remoting architecture with extensibility points for providing your own interface remoting mechanisms. However, if you don't care to take advantage of this, COM provides you with a default remoting architecture that is called standard marshalling. We will spend most of our time looking at this because it is simple and because its implementation is tightly coupled to the idea of context.

If you want to know more about marshalling and interface remoting, have a look at the marshalling and interface remoting trail now. Note that this trail is optional and goes into more detail than is strictly necessary in order to understand context and apartments.

If you would simply like to go on and learn about apartments, click here.


Top of document