views:

342

answers:

4

I have an STA COM component which is put into a COM+ application. The client creates several instances of the class in that component and calls their methods in parallel. The class is registered properly - the "ThreadingModel" for the corresponding class id is "Apartment".

I see several calls of the same method of the same class being executed in parallel inside the component - in actual component code. They are executed in the same process but in different threads.

What's happening? Is COM+ ignoring the threading model? Shouldn't STA model only allow one call at a time to be executed?

+1  A: 

No, not really. STA literally means 'Single Threaded Apartment' which further means that only a single thread can run in an apartment. Now the question is that what is an apartment. Apartment is a logical space within a process and its implementation can vary from framework to framework. Microsoft implements apartments as Threads because of which an STA (in Microsoft's COM Context) translates into Single Threaded Thread i.e., there can be multiple apartments/threads but every apartment/thread will be single threaded in case of STA.

You can generalize this thing to MTA yourself. From what I said above, an MTA is a Multi-Threaded thread in COM Context.

Aamir
Does this mean that I still need synchronization when accessing all the shared variables? What's the point of STA if it doesn't protect against shared variables concurrent access?
sharptooth
I think that this answer by Aamir is either wrong, or relying on som unstated detail of COM threading model which is not obvious. By the rules of COM, an object in an STA can only receive messages from the thread that is in that apartment. I don't think that there is a lot of leeway there
1800 INFORMATION
+1  A: 

STA guarantees that your object is only accessed from a single, specific thread -- no protection against shared variable is required.

I remember that for VB6, there was a special mode (I do not recall how it was named): You could allow COM+ to spawn up multiple STAs, each using a dedicated object. The variables of these objects, however, were treated as thread-local storage -- so although there are multiple instances of your COM class being accessed from multiple threads, no sharing of variables is taking place. Is it possible that you are using this feature?

Johannes Passing
I haven't enabled anything special - just created an empty COM+ server application and added my component into it.
sharptooth
Are you using VB6?
Johannes Passing
Both the component and the client are 32-bit C++ with ATL.
sharptooth
Ok. Are you using in-process COM+? If so, are the callers of the respective objects running in STAs, too? If 2 callers running in 2 separate STAs both create an instance of the respective objects and call a method on it, then these calls will run in parallel. Could that be the case?
Johannes Passing
I use out-proc COM+. I tried two different processes calling CoCreateInstance() - calls from these processes do overlap.
sharptooth
See http://www.tech-archive.net/Archive/VC/microsoft.public.vc.atl/2009-03/msg00056.html.
Johannes Passing
+1  A: 

Have you passed the object to objects that live in another apartment? If so, did you need to marshal the interface before you did it? Did you happen to aggregate the free threaded marshaller?

Roughly speaking, if you pass an interface to your object to an object in another apartment (thread), then you must make sure to marshal the interface. If you do not, then you may find that your object can be called freely from the objects in the other apartment, since they are not calling through a proxy which handles the call correctly.

All calls to an object must be made on its thread (within its apartment). It is forbidden to call an object directly from another thread; using objects in this free-threaded manner could cause problems for applications. The implication of this rule is that all pointers to objects must be marshaled when passed between apartments. COM provides the following two functions for this purpose:

* CoMarshalInterThreadInterfaceInStream marshals an interface into a stream object that is returned to the caller.
* CoGetInterfaceAndReleaseStream unmarshals an interface pointer from a stream object and releases it.

These functions wrap calls to CoMarshalInterface and CoUnmarshalInterface functions, which require the use of the MSHCTX_INPROC flag.

1800 INFORMATION
Interface pointers are not passed between objects. Each instance of a client has its own interface pointer which it never exposes to anyone.
sharptooth
+1  A: 

To avoid confusion, I won't use the term "object" in this answer. Instead let's use "class" and "instance". I'm confident we all understand the difference between them.

Marking your COM class with a ThreadingModel of "Apartment" means that instances of it will be loaded into an STA. The process creating those instances will determine whether they all go into the same STA, or into separate STAs.

As you've discovered, COM+ has loaded several instances into separate STAs.

The guarantee you get with an STA is that a single instance will never be accessed by multiple threads at the same time. Separate instances of the same class, if they are loaded into separate STAs, could certainly be accessed by different threads at the same time.

So the STA is really a way of protecting your instance data. Not your class data. Any "shared" or "static" data in your COM code will have to be protected by you.

Martin