INFO: Descriptions and Workings of OLE Threading ModelsLast reviewed: August 21, 1997Article ID: Q150777 |
The information in this article applies to:
SUMMARYCOM objects can be used in multiple threads of a process. The terms "Single- threaded Apartment" (STA) and "Multi-threaded Apartment" (MTA) are used to create a conceptual framework for describing the relationship between objects and threads, the concurrency relationships among objects, the means by which method calls are delivered to an object, and the rules for passing interface pointers among threads. Components and their clients choose between the following two apartment models presently supported by COM:
MORE INFORMATION
OverviewThe threading models in COM provide the mechanism for components that use different threading architectures to work together. They also provide synchronization services to components that require them. For example, a particular object may be designed to be called only by a single thread and may not synchronize concurrent calls from clients. If such an object is called concurrently by multiple threads, it crashes or causes errors. COM provides the mechanisms for dealing with this interoperability of threading architectures. Even thread-aware components often need synchronization services. For example, components that have a graphical user interface (GUI), such as OLE/ActiveX controls, in-place active embeddings, and ActiveX documents, require synchronization and serialization of COM calls and window messages. COM provides these synchronization services so these components can be written without complex synchronization code. An "apartment" has several inter-related aspects. First, it is a logical construct for thinking about concurrency, such as how threads relate to a set of COM objects. Second, it is a set of rules programmers must obey to receive the concurrency behavior they expect from COM environment. Finally, it is system-supplied code that helps programmers manage thread concurrency with respect to COM objects. The term "apartment" comes from a metaphor in which a process is conceived as a totally discrete entity, such as a "building" that is subdivided into a set of related but different "locales" called "apartments." An apartment is a "logical container" that creates an association between objects and, in some cases, threads. Threads are not apartments, although there may be a single thread logically associated with an apartment in the STA model. Objects are not apartments, although every object is associated with one and only one apartment. But apartments are more than just a logical construct; their rules describe the behavior of the COM system. If the rules of the apartment models are not followed, COM objects will not function properly.
More DetailsA Single-threaded apartment (STA) is a set of COM objects associated with a particular thread. These objects are associated with the apartment by being created by the thread or, more precisely, being first exposed to the COM system (typically by marshaling) on the thread. AN STA is considered a place where an object or a proxy "lives." If the object or proxy needs to be accessed by another apartment (in the same or a different process), its interface pointer must be marshaled to that apartment where a new proxy is created. If the rules of the apartment model are followed, no direct calls from other threads in the same process are allowed on that object; that would violate the rule that all objects within a given apartment run on a single thread. The rule exists because most code running in an STA will fail to function properly if run on additional threads. The thread associated with an STA must call CoInitialize or CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) and must retrieve and dispatch window messages for the associated objects to receive incoming calls. COM dispatches and synchronizes calls to objects in an STA using window messages, as described later in this article. The "main STA" is the thread that calls CoInitialize or CoInitializeEx(NULL,COINIT_APARTMENTTHREADED) first within a given process. The main STA of a process must remain alive until all COM work is completed because some in-proc objects are always loaded in the main STA, as described later in this article. Windows NT 4.0 and DCOM95 introduce a new type of apartment called the multi-threaded apartment (MTA). An MTA is a set of COM objects associated with a set of threads in the process such that any thread can call any object implementation directly without the interposition of system code. Interface pointers to any object in the MTA may be passed among the threads associated with the MTA without having to be marshaled. All threads in the process that call CoInitializeEx(NULL, COINIT_MULTITHREADED) are associated with the MTA. Unlike the STA described above, the threads in an MTA do not need to retrieve and dispatch window messages for the associated objects to receive incoming calls. COM does not synchronize calls to objects in an MTA. Objects in an MTA must protect their internal state from corruption by the interaction of multiple simultaneous threads and they cannot make any assumptions about the content of Thread-Local Storage remaining constant between different method invocations. A process can have any number of STAs, but, at most, can have one MTA. The MTA consists of one or more threads. STAs have one thread each. A thread belongs, at most, to one apartment. Objects belong to one and only one apartment. Interface pointers should always be marshaled between apartments (although the result of marshaling may be a direct pointer rather than a proxy). See information below on CoCreateFreeThreadedMarshaler. A process chooses one of the threading models provided by COM. An STA model process has one or more STAs and does not have an MTA. An MTA model process has one MTA with one or more threads and does not have any STAs. A mixed- model process has one MTA and any number of STAs.
Single-threaded Apartment ModelThe thread of an STA must call CoInitialize or CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) and must retrieve and dispatch window messages because COM uses window messages to synchronize and dispatch the delivery of calls to an object in this model. See the REFERENCES section below for more information. Server that Supports STA Model: In the STA model, calls to an object are synchronized by COM in the same manner as window messages posted to a window are synchronized. Calls are delivered using window messages to the thread that created the object. Consequently, the object's thread must call Get/PeekMessage and DispatchMessage to receive calls. COM creates a hidden window associated with each STA. A call to an object from outside the STA is transferred by COM runtime to the object's thread using a window message posted to this hidden window. When the thread associated with the object's STA retrieves and dispatches the message, the window procedure for the hidden window, also implemented by COM, receives it. The window procedure is used by the COM runtime to "hook" the thread associated with the STA because the COM runtime is on both sides of the call from the COM-owned thread to the STA's thread. The COM runtime (now running in the STA's thread) calls "up" via a COM-supplied stub into the corresponding interface method of the object. The execution path returning from the method call reverses the "up" call; the call returns down to the stub and the COM runtime, which passes control back to the COM runtime thread via a window message, which then returns through the COM channel to the original caller. When multiple clients call an STA object, the calls are automatically queued in the message queue by the transfer of control mechanism used in the STA. The object receives a call each time its STA retrieves and dispatches messages. Because the calls are synchronized by COM in this manner, and because the calls are always delivered on the single thread associated with the object's STA, the object's interface implementations do not need to provide synchronization. Note that the object can be re-entered if an interface method implementation retrieves and dispatches messages while processing a method call, causing another call to be delivered to the object by the same STA. A common way in which this occurs is if an STA object makes an out-going (cross-apartment/cross-process) call using COM. This is identical to the manner in which a window procedure can be re-entered if it retrieves and dispatches messages while processing a message. COM does not prevent re- entrance on the same thread but does prevent concurrent execution. It also provides a means by which COM-related reentrancy can be managed. See the REFERENCES section below for more information. The object is not re- entered if method implementations do not call out of its apartment or otherwise retrieve and dispatch messages. Client Responsibilities in the STA Model: Client code running in a process and/or thread that uses the STA model must marshal interfaces of an object between apartments by using CoMarshalInterThreadInterfaceInStream and CoGetInterfaceAndReleaseStream. For example, if Apartment 1 in the client has an interface pointer, and Apartment 2 requires use of it, Apartment 1 must marshal the interface using CoMarshalInterThreadInterfaceInStream. The stream object returned by this function is thread-safe and its interface pointer should be stored in a direct memory variable accessible by Apartment 2. Apartment 2 must pass this stream interface to CoGetInterfaceAndReleaseStream to unmarshal the interface on the underlying object and get back a pointer to a proxy through which it can access the object. The main apartment of a given process should remain alive until the client has completed all COM work because some in-proc objects are loaded in the main-apartment. (More information is detailed below). Multi-threaded Apartment Model An MTA is the collection of objects created or exposed by all the threads in the process that have called CoInitializeEx(NULL, COINIT_MULTITHREADED). NOTE: Current implementations of COM allow a thread that does not explicitly initialize COM to be a part of the MTA. A thread that does not initialize COM is part of the MTA only if it starts using COM after at least one other thread in the process has previously called CoInitializeEx(NULL, COINIT_MULTITHREADED). (It is even possible that COM itself may have initialized the MTA when no client thread has explicitly done so; for example, a thread associated with an STA calls CoGetClassObject/CoCreateInstance[Ex] on a CLSID that is marked "ThreadingModel=Free" and COM implicitly creates an MTA into which the class object is loaded.) See the information on threading model interoperability below. However, this is a configuration that might cause problems, such as access violations, under certain circumstances. Therefore, it is strongly recommended that each thread that needs to do COM work initialize COM by calling CoInitializeEx and then, on completion of COM work, call CoUninitialize. The cost of "unnecessarily" initializing an MTA is minimal. MTA threads do not need to retrieve and dispatch messages because COM does not use window messages in this model to deliver calls to an object. Server that Supports the MTA Model: In the MTA model, calls to an object are not synchronized by COM. Multiple clients can concurrently call an object that supports this model on different threads, and the object must provide synchronization in its interface/method implementations using synchronization objects such as events, mutexes, semaphores, etc. MTA objects can receive concurrent calls from multiple out-of-process clients through a pool of COM-created threads belonging to the object's process. MTA objects can receive concurrent calls from multiple in-process clients on multiple threads associated with the MTA. Client Responsibilities in the MTA Model: Client code running in a process and/or thread that uses the MTA model does not have to marshal interface pointers of an object between itself and other MTA threads. Rather, one MTA thread can use an interface pointer obtained from another MTA thread as a direct memory pointer. When a client thread makes a call to an out-of-process object, it suspends until the call has completed. Calls may arrive on objects associated with the MTA while all application-created threads associated with the MTA are blocked on out- going calls. In this case and in general, incoming calls are delivered on threads provided by the COM runtime. Message filters (IMessageFilter) are not available for use in the MTA model.
Mixed-Threading ModelA process that supports the mixed-threading model will use one MTA and one or more STAs. Interface pointers must be marshaled between all apartments but can be used without marshaling within the MTA. Calls to objects in an STA are synchronized by COM to run on only one thread while calls to objects in the MTA are not. However, calls from an STA to an MTA normally go through system-provided code and switch from the STA thread to an MTA thread before being delivered to the object. NOTE: See the SDK documentation on CoCreateFreeThreadedMarshaler() and the discussion of that API below for information on cases where direct pointers can be used, and how an STA thread can call directly into an object first associated with the MTA and, vice versa, from multiple apartments.
Choosing the Threading ModelA component can choose to support the STA model, the MTA model, or a combination of the two by using the mixed-threading model. For example, an object that does extensive I/O can choose to support MTA to provide maximum response to clients by allowing interface calls to be made during I/O latency. Alternatively, an object that interacts with the user almost always chooses to support STA to synchronize incoming COM calls with its GUI operations. Supporting the STA model is easier because COM provides synchronization. Supporting the MTA model is more difficult because the object must implement synchronization, but response to clients is better because synchronization is used for smaller sections of code, rather than for the whole interface call as provided by COM. The STA model is also used by Microsoft Transaction Server (MTS, previously code-named "Viper"), and thus DLL-based objects planning to run within the MTS environment should use the STA model. Objects implemented for the MTA model will normally work fine in an MTS environment. However, they will run less efficiently because they will be using unnecessary thread synchronization primitives.
Marking the Supported-threading Model of In-Proc ServersA thread uses the MTA model if it calls CoInitializeEx(NULL, COINIT_MULTITHREADED) or uses COM without initializing it. A thread uses the STA model if it calls CoInitialize or CoInitializeEx(NULL, COINIT_APARTMENTTHREADED). The CoInitialize APIs provide apartment control for client code and for objects that are packaged in .EXEs, because the COM runtime's startup code can initialize COM in the desired fashion. However, an in-proc (DLL-based) COM server does not call CoInitialize/CoInitializeEx because those APIs will have been called by the time the DLL server is loaded. Therefore, a DLL server must use the registry to inform COM of the threading model it supports so that COM can ensure that the system operates in a way that is compatible with it. A named value of the component's CLSID\InprocServer32 key called ThreadingModel is used for this purpose as follows:
Threading models of in-proc servers are discussed later in this article. If an in-proc server provides many types of objects (each with its own unique CLSID), each type can have a different ThreadingModel value. In other words, the threading model is per CLSID, not per code package/DLL. However, the API entry points necessary to "bootstrap" and query all in-proc servers (DLLGetClassObject(), DLLCanUnloadNow()) must be thread-safe for any in-proc server that supports multiple threads (meaning a ThreadingModel value of Apartment, Both, or Free). As previously stated, out-of-process servers do not mark themselves using the ThreadingModel value. Rather, they use CoInitialize or CoInitializeEx. DLL-based servers that expect to run out-of-process using the "surrogate" functionality of COM (such as the system-supplied surrogate DLLHOST.EXE) simply follow the rules for DLL-based servers; there are no special considerations in that case.
When the Client and Object Use Different Threading ModelsInteraction between a client and an out-of-process object is straight forward even when different threading models are used because the client and object are in different processes and COM is involved in passing calls from the client to the object. Because COM is interposed between the client and the server, it provides the code for interoperation of the threading models. For example, if an STA object is called concurrently by multiple STA or MTA clients, COM synchronizes the calls by placing corresponding window messages in the server's message queue. The object's STA receives one call each time it retrieves and dispatches messages. All combinations of threading-model interoperability are allowed and fully supported between clients and out-of-process objects. Interaction between a client and an in-proc object that uses different threading models is more complicated. Although the server is in-proc, COM must interpose itself between the client and the object in some cases. For example, an in-proc object designed to support the STA model may be called concurrently by multiple threads of a client. COM cannot allow the client threads to directly access the object's interface because the object is not designed for such concurrent access. Instead, COM must ensure that calls are synchronized and made only by the thread associated with the STA that "contains" the object. Despite the added complexity, all combinations of threading-model interoperability are allowed between clients and in-proc objects.
Threading Models in Out-of-Process (EXE-based) ServersFollowing are three categories of out-of-process servers, each of which can be used by any COM client regardless of the threading model used by that client:
The server optionally can implement IMessageFilter in each STA to control aspects of call delivery by COM. A degenerate case is the single-threading model server that does COM work in one STA. The server does COM work in one or more threads, all of which belong to the MTA. Calls are not synchronized by COM. COM creates a pool of threads in the server process, and a client call is delivered by any of these threads. Threads do not need to retrieve and dispatch messages. The object and class factory must implement synchronization. The server does not need to marshal interface pointers between threads. See the section in this article titled "Mixed-threading model" for more information.
Threading Models in ClientsThere are three categories of clients:
There are four categories of in-proc servers, each of which can be used by any COM client regardless of the threading model used by that client. However, in-proc servers must provide marshaling code for any custom (non- system-defined) interface they implement if they are to support threading model interoperability because that typically requires that COM marshal the interface between client apartments. The four categories are:
Threading Model Interoperability Between Clients and In-process ObjectsAll combinations of threading-model interoperability are allowed between clients and in-process objects. COM allows all STA model clients to inter-operate with single-threading in-proc objects by creating and accessing the object in the client's main STA and marshaling it to the client STA that called CoCreateInstance[Ex]. If an MTA in a client creates an STA model in-proc server, COM spins up a "host" STA in the client. This host STA creates the object and the interface pointer is marshaled back to the MTA. Similarly, when an STA creates an MTA in-proc server, COM spins up a host MTA in which the object is created and marshaled back to the STA. Interoperability between the single-threading model and MTA model is handled similarly because the single-threading model is just a degenerate case of the STA model. Marshaling code must be provided for any custom interface that an in-proc server implements if it wants to support interoperability that requires COM to marshal the interface between client apartments. See the REFERENCES section below for more information.
Relationship Between Threading Model and Class Factory Object ReturnedA precise definition of in-proc servers being "loaded into" apartments is explained in the two following steps:
Summary of Client and Object Threading Models for In-Proc ServersThe following table summarizes the interaction between different threading models when a client thread first calls CoGetClassObject on a class that is implemented as an in-proc server. Kinds of clients/threads:
Client Server Result ------ ------ ----------------------------------------- STA0 None Direct access; server loaded into STA0 STA* None Proxy access; server loaded into STA0. MTA None Proxy access; server loaded into STA0; STA0 created automatically by COM if necessary; STA0 Apt Direct access; server loaded into STA0 STA* Apt Direct access; server loaded into STA* MTA Apt Proxy access; server loaded into anSTA created automatically by COM. STA0 Free Proxy access; server is loaded into MTAMTA created automatically by COM if necessary. STA* Free Same as STA0->Free MTA Free Direct access STA0 Both Direct access; server loaded into STA0 STA* Both Direct access; server loaded into STA* MTA Both Direct access; server loaded into the MTA REFERENCESSDK documentation on the CoRegisterMessageFilter() and IMessageFilter interface. For additional information, please see the following articles in the Microsoft Knowledge Base:
ARTICLE-ID: Q136885 TITLE : OLE Threads Must Dispatch Messages ARTICLE-ID: Q137629 TITLE : In-proc Object Custom Interface in Apartment Model Client Keywords : LeTwoArc LeTwoCom kbinfo Technology : kbole Version : 3.51 4.0 Platform : NT WINDOWS Issue type : kbinfo |
================================================================================
© 1998 Microsoft Corporation. All rights reserved. Terms of Use. |