Re: STA cannot prevent multiple client calls accessing at the same time??

From: Alexander Nickolov (agnickolov_at_mvps.org)
Date: 02/25/05


Date: Fri, 25 Feb 2005 09:54:38 -0800

When you call a method on the out-of-proc singleton you are
making a cross-apartment call from an STA. At this point COM
dispatches a message via some communication channel to your
EXE server and spins a message loop waiting for the response.
This is one form of yielding I mentioned earlier (by far the most
common cause of reentrancy in STA COM). Within this message
loop another COM request may come and be serviced - these
come as messages posted to a hidden window created by COM
as part of CoInitialize. Note there's only ever a single thread your
DLL object (and most likely your client) runs on.

In short, the behavior you see is perfectly normal. Again, it's called
reentrancy. And again, synchronization primitives like a mutex are
useless since they are designed to be reentrant on a single thread.
Use boolean flags. I believe I discussed this already in one of my
earlier replies...

A fact you seem not to be aware of is that a window is firmly
bound to the thread that created it - all messages for that window
are always delivered on that single thread. It's said that the window
has thread affinity.

As for the suddenly changing call stack - that's a debugger quirk -
you don't always see the entire call stack because not all funcions
create stack frames. When there's a function without a stack frame
on the call stack the debugger cannot parse the stack further up
the call chain.

-- 
=====================================
Alexander Nickolov
Microsoft MVP [VC], MCSD
email: agnickolov@mvps.org
MVP VC FAQ: http://www.mvps.org/vcfaq
=====================================
"Angela Yan" <yanyan9@hotmail.com> wrote in message 
news:%23AI125wGFHA.2360@TK2MSFTNGP12.phx.gbl...
> Hi,
>
> I've been searching for the STA info web sites these days, but it seems 
> that they don't really help in my scenerio. And maybe I am not an Appz 
> developer, while most of the web sites try to explain STA from 
> Appz's/client's point of view, which makes me a bit confused.
>
> Let me revise my problem. I have an ATL com in-proc dll project. Appz will 
> call AfxOleInit() before creating the dll object and start using the dll 
> methods. And Appz use PostMessage() to windows message queue after 
> receiving a call back. The receiving part of the message queue will also 
> use the same dll interface pointer(ie. the interface pointer of the dll is 
> globally shared). But I am not sure whether the "receiving part of message 
> queue" is residing in the owning thread of my dll or not.
>
> There is another singleton EXE com server in the scene. Both the dll and 
> Appz will hold the interface pointers of the EXE server. There is a 
> callback machanism implemented between Appz and EXE. (The dll doesn't 
> listen to callback from EXE)
>
> dll code:
>
> A()
> {
>    //Init and process some local variables and some class private member
>    ...
>    ...                                               <-----------  
> Location 1
>
>    IEXEPointer->DoSomethingSimple1();                <-----------  
> Location 2
>    IEXEPointer->DoSomethingSimple2();
>
>    ...
>    ...
> }
>
>
> The scenerio is when Appz calls dll method A(), I breakpoint at the first 
> line of A(), and then triger the EXE server to call back to the Appz using 
> another Appz which does not involve the dll. When Appz receives the call 
> back, it will try to call method B() of the dll. The dll will get method 
> B() call in ALWAYS (seems like always) after Location 2 
> (IEXEPointer->DoSomethingSimple1() call). The call stack in .Net debug 
> window when B() is called shows:
>
> B() in
> ...            //some Appz calls
> ...
> ...
> A() in
> ...            //some Appz calls
> ...
>
> Is this a correct behaviour?
> Why the reentrancy always occurs after the call to the EXE server?
> How can I solve the reentrancy issue? (mutex... or... ?)
>
> ===========================================================================
>
> After trigering the call back from EXE to the Appz, before Location 2 
> call, I use "sleep(10000)" at location 1. During this period, no B() is 
> received. And after location 2, when B() call is just received, the call 
> stack shows:
>
> B() in
> ....        //some Appz calls
> ....
>
> It does not have any information regarding A(). Until B() is completed, 
> the call stack suddently becomes:
>
> A() in
> ...        //some Appz calls
> ...
>
> Any idea what is going on?
>
> Some more facts on the dll code:
> It doesn't have any windows message loop related stuff such as a message 
> window or AtlWaitWithMessageLoop and CoWaitForMultipleHandles for example.
> The GetCurrectThreadID() call in both method A() and B() return the same 
> value.
> The location 2 call in debug mode always makes a blink of the .Net frame. 
> Possiblely a context (thread/stack...?) switch?
>
>
> Thank you very much.
>
> Regards,
> Angela
>
>
>
>
>
> "Alexander Nickolov" <agnickolov@mvps.org> wrote in message 
> news:ewQzUqRGFHA.3120@TK2MSFTNGP12.phx.gbl...
>> Yielding occurs when a message loop is spun within an STA
>> for any reason. Common causes are: calling a COM object
>> across apartment boundaries (including in another process),
>> displaying a dialog box or message box, calling SendMessage
>> to a window on another thread, and explicitly spinning a
>> message loop within your code (AtlWaitWithMessageLoop
>> and CoWaitForMultipleHandles for example). Note this all
>> is only of importance to objects running in an STA. It doesn't
>> matter what the threading model of the client is, except for
>> Both-threaded objects where this applies only for STA clients
>> (since then the object runs in an STA).
>>
>> Note that yielding does not break thread serialization for STA
>> objects. Your second request is delivered on the same thread,
>> so you don't need to do explicit synchronization (and it would
>> be useless anyway, since all kernel primitives are reentrant on
>> a single thread by design!). However, your code must be
>> desined to be reentrant, which is somewhat harder than making
>> it thread-safe...
>>
>> -- 
>> =====================================
>> Alexander Nickolov
>> Microsoft MVP [VC], MCSD
>> email: agnickolov@mvps.org
>> MVP VC FAQ: http://www.mvps.org/vcfaq
>> =====================================
>>
>> "Angela Yan" <yanyan9@hotmail.com> wrote in message 
>> news:%23Cru5u$FFHA.1476@TK2MSFTNGP09.phx.gbl...
>>> Hi,
>>>
>>> I just read your first reply again. This is my literal understanding of 
>>> the word "yield":
>>>
>>> Client calls my method A(), in A() there are calls to another COM object 
>>> which is an out-of-process exe. Although the calls to the other COM 
>>> object are not to display a dialog or message box etc, once the calls 
>>> are made, serialization is not enfored any more and my dll can receive 
>>> any interface method call again from client before A() finsihes. (In 
>>> this case method B() call is received.)
>>>
>>> Is this the correct understanding of the word "yield"?
>>>
>>> Does it matter if the client is single thread or multi thread in this 
>>> case?
>>>
>>> So does it mean if the dll calls to any other out-of-process COM object, 
>>> there is no more serilization? and what can I do about it?
>>>
>>> Thanks.
>>>
>>> Regards,
>>> Angela
>>>
>>>
>>> "Alexander Nickolov" <agnickolov@mvps.org> wrote in message 
>>> news:%236azMD5EFHA.3200@TK2MSFTNGP10.phx.gbl...
>>>> Your call stack clearly shows that C yields. Check what statement
>>>> yields. Once that sinks in, you can use your choice of boolean
>>>> flags appropriate for your code. Another approach would be
>>>> to flag the deletion request and do nothing further if you are
>>>> called reentrantly (using another flag set at the beginning of A
>>>> and cleared at its end). You can check the deletion flag at
>>>> each iteration within C and abort it when set, then carry out
>>>> the deletion at the end of A. Just another idea. With some
>>>> experience with reentrancy you should be able to generate
>>>> these yourself...
>>>>
>>>> -- 
>>>> =====================================
>>>> Alexander Nickolov
>>>> Microsoft MVP [VC], MCSD
>>>> email: agnickolov@mvps.org
>>>> MVP VC FAQ: http://www.mvps.org/vcfaq
>>>> =====================================
>>>>
>>>> "Angela Yan" <yanyan9@hotmail.com> wrote in message 
>>>> news:O05cUnwEFHA.3276@TK2MSFTNGP10.phx.gbl...
>>>>> Hi,
>>>>>
>>>>> The possible solution, is it the 5th Feb reply? I've used 
>>>>> "GetCurrentThreadID()" to print out the Thread ID while A() or B() is 
>>>>> being called. And they turn out to be the same. For the time stamp, it 
>>>>> has been confirmed that A() is called first, then followed by B() when 
>>>>> A() has not finished its execution.
>>>>>
>>>>> For 4th Feb reply, the C method does not yield. It is just a simple 
>>>>> loop to compare the link list elements. And I can't check for NULL 
>>>>> condition of the linkList, because after I 'delete' the LinkList in 
>>>>> B(), all the contents in the link list will be 0xcdcdcdcd or 
>>>>> 0xdddddddd. Currently I can even repro the whole thing such that B( ) 
>>>>> comes in before C( ) gets call.
>>>>>
>>>>>
>>>>> Call Stack:
>>>>>
>>>>> C()in
>>>>> B()out
>>>>> B()in
>>>>> A()in
>>>>>
>>>>> So I assume it has nothing to do with the linklist. In this case the 
>>>>> program will not crash but still the calling sequence is not a desired 
>>>>> behaviour.
>>>>>
>>>>> Looking forward to your reply.
>>>>>
>>>>> Thanks.
>>>>> Angela
>>>>>
>>>>>
>>>>> "Alexander Nickolov" <agnickolov@mvps.org> wrote in message 
>>>>> news:%230w1wbsEFHA.3200@TK2MSFTNGP10.phx.gbl...
>>>>>> If you refer to my very first reply, that's exactly what I told
>>>>>> you - you have reentrancy. I even gave you a possible solution
>>>>>> in one of the later replies. You haven't done anything about it
>>>>>> yet I assume.
>>>>>>
>>>>>> -- 
>>>>>> =====================================
>>>>>> Alexander Nickolov
>>>>>> Microsoft MVP [VC], MCSD
>>>>>> email: agnickolov@mvps.org
>>>>>> MVP VC FAQ: http://www.mvps.org/vcfaq
>>>>>> =====================================
>>>>>>
>>>>>> "Angela Yan" <yanyan9@hotmail.com> wrote in message 
>>>>>> news:O%23PGfGnEFHA.3536@TK2MSFTNGP15.phx.gbl...
>>>>>>> Hi,
>>>>>>>
>>>>>>> Sorry for replying so late. Just came back from holiday... :p
>>>>>>>
>>>>>>> I've tried using  "GetCurrentThreadId" in A( ) and B( ). They return 
>>>>>>> the same value. But I am very sure that A( ) will not call B( ). In 
>>>>>>> fact, in the whole dll, there is no B( ) call is made. B( ) is only 
>>>>>>> called by the client. And I don't use windows message loop in the 
>>>>>>> dll, only the Client uses it. Neither any thread message call is in 
>>>>>>> the dll.
>>>>>>>
>>>>>>> About the call stack, I can see A( ) is being called, then some 
>>>>>>> Cleint calls, then B( ) is called.
>>>>>>>
>>>>>>> B( )
>>>>>>> client call1
>>>>>>> client call2
>>>>>>> client call3
>>>>>>> client call4
>>>>>>> client call5
>>>>>>> ..
>>>>>>> ..
>>>>>>> ..
>>>>>>> C( )
>>>>>>> A( )
>>>>>>> client call100
>>>>>>> client call101
>>>>>>> client call102
>>>>>>> ..
>>>>>>> ..
>>>>>>> ..
>>>>>>>
>>>>>>> Please help.
>>>>>>>
>>>>>>> Thank you and
>>>>>>>
>>>>>>> Happy Chinese new year!!! ^_^
>>>>>>>
>>>>>>> Angela
>>>>>>>
>>>>>>>
>>>>>>> "Rossen Tzonev" <rossenbg@hotmail.com> wrote in message 
>>>>>>> news:opslo421ol8xxq1h@rossenbg-homepc...
>>>>>>>>
>>>>>>>> I will add to Alexander suggestions. This is also possible:
>>>>>>>>
>>>>>>>> 1. "GetCurrentThreadId" returns the same value when "A" and "B" 
>>>>>>>> are - variant 1. Then I can bet you are calling "B" somewhere 
>>>>>>>> during the call  to "A". Because your LOG shows that you are in 
>>>>>>>> "A", but "B" is being called
>>>>>>>>
>>>>>>>> 2. "GetCurrentThreadId" returns the same value when "A" and "B" 
>>>>>>>> are - variant 2. If somewhere durring "A" execution you are calling 
>>>>>>>> something  like "AtlWaitWithMessageLoop" or any code which gets 
>>>>>>>> current thread  message in the message queue and dispatches it, 
>>>>>>>> then it is possible that  "B" is called during "A" execution. 
>>>>>>>> That's because of COM rules for STA.  STA has message loop and all 
>>>>>>>> COM calls to any methods in objects created  in STA are done via 
>>>>>>>> COM private messages to the thread.
>>>>>>>>
>>>>>>>> 3. "A" and "B" are called from different threads. Then your problem 
>>>>>>>> is obvious. But this means that your code for creating the COM 
>>>>>>>> object is located not where it should be.
>>>>>>>>
>>>>>>>> Can you try to put breakpoint at the beginning and at the end of 
>>>>>>>> "A" and "B" and examine the call stack. If "B" is currently called 
>>>>>>>> and "A" is in the call stack then you have case (1). If "B" is 
>>>>>>>> called via the thread message loop code and after it there is a 
>>>>>>>> call to "A" in the stack then you have (2).
>>>>>>>>
>>>>>>>>
>>>>>>>> On Fri, 4 Feb 2005 09:43:20 -0800, Alexander Nickolov 
>>>>>>>> <agnickolov@mvps.org> wrote:
>>>>>>>>
>>>>>>>>> Why don't you log the thread ID and time stamp in your traces
>>>>>>>>> to be sure what's going on? If your client is misbehaving, there's
>>>>>>>>> nothing for you to fix...
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> -- 
>>>>>>>> Rossen Tzonev
>>>>>>>> Sofia, Bulgaria
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>
>>>
>>
>>
>
> 


Relevant Pages

  • Re: STA cannot prevent multiple client calls accessing at the same time??
    ... I've been searching for the STA info web sites these days, ... And maybe I am not an Appz developer, ... I have an ATL com in-proc dll project. ... Appz will hold the interface pointers of the EXE server. ...
    (microsoft.public.vc.atl)
  • Re: AD+ crash logs and .dmps Part II
    ... This a .dll provided by an online payment verification ... The Debugger docs have some basic info but overall, understanding ... details the formation and usage of the stack. ...
    (microsoft.public.inetserver.iis.security)
  • Re: E_NOINTERFACE error in ATL server extension dll
    ... STA where you initially created the object and registered ... it in GIT and that invalidated the object already in GIT. ... you close the STA thus both invalidating the interface pointer ... thread switching or the extension dll loading or execution. ...
    (microsoft.public.vc.atl)
  • Re: E_NOINTERFACE error in ATL server extension dll
    ... STA where you initially created the object and registered ... it in GIT and that invalidated the object already in GIT. ... you close the STA thus both invalidating the interface pointer ... thread switching or the extension dll loading or execution. ...
    (microsoft.public.vc.atl)
  • Re: tracking down error in MFC42.dll
    ... (I used WinDbg to do that, but the same can be done ... The same could be done for your dll if it would be built with debug ... If you put symbols of your dll on the target system (in the same ... what the call stack shows. ...
    (microsoft.public.vc.mfc)