Re: Threading Model = "Both" query
Dinesh Venugopalan <dinesh@epiance.com> wrote:
The point of this exercise was to get a bit of knowledge about how COM
actually works. I know most of the calls I made are illegal and I
expected the program to crash. But that's not happening here.
This C++ program also doesn't crash, despite being blatantly illegal:
class C {
public:
void f() { cout << "In C::f : this=" << this << endl; }
};
int main() {
C* p = NULL;
p->f();
p = new C;
delete p;
p->f();
return 0;
}
Your application doesn't crash for roughly the same reasons this one
doesn't.
I tried experimenting with threading model set to "Free" or
"Apartment" the behavior is as expected. As you rightly pointed out
that I am getting away by violating COM rules. What has surprised me
is why is COM so lenient when the threading model is "Both".
Try a combination where the threading model of the object (Free or
Apartment) matches the apartment that the thread creating the object
joins (MTA or STA, correspondingly). The crux of "getting away with it"
is getting a raw interface pointer, with no marshalling involved. With
Both, you get that every time. With Free or Apartment, creating thread's
model has to match.
I also wanted to know the lifetime of a COM object. Till I tried this
experiment I was under the impression that once the thread which
created the object died, the object's pointer will become a dangling
pointer and any calls made will make the program crash.
Dangling pointer, yes. Crash - not necessarily. The example I showed at
the beginning calls through a dangling pointer, and doesn't crash. Which
is not to say that you _should_ ever call through dangling pointers (you
shouldn't).
I guess this
supposition must be true only for STA based object. As there is only
one MTA in the process the object can still live.
MTA object lives as long as there's at least one MTA thread (and the
object's reference counter is greater than zero). It's not tied to the
creating thread.
I would be very grateful to you if you can explain me this particular
behavior.
My main thread as well as my other two threads are all STA. The
threading model is still "Both". The thread id of the main thread is
3168, Thread1 is 1072 and Thread2 is 932.I create the object in
Thread1. The constructor as well as the function displays the thread
id as 1072. In the thread2 I expected the spBoth->Test() to display
me 1072 but it actually displays me 932.
You don't marshal the pointer, and instead make a direct call from the
wrong thread. This is in violation of COM rules, but you happen to get
away with it. The call executes on the calling thread, in this case 932.
Finally when I release the
pointer in the main thread, the destructor of the object is
displaying the thread id as 3168.
I'm surprised the object isn't released, and its destructor called, at
the time thread 1 calls CoUninitialize(). I expected it to. I'm not sure
why it doesn't.
But to be honest, I don't find the question of why the program breaks
one way and not another particularly interesting. I'm more interested in
writing programs that don't violate rules, and hence don't break.
--
With best wishes,
Igor Tandetnik
With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925