none
How can a thread have its Apartment State Changed? RRS feed

  • Question

  • Hello,

    In my application, several threads are running.
    I set no particular ApartmentState when launching the threads, but I can see that these threads are MTA.

    However, after some time (sometimes quite straight away, and sometimes after some minutes), these formerly MTA threads become STA threads. (through a temporary period while they are Unknown).

    I would like to understand how these threads can have their state changed, and as well how I could change their state back to MTA.

    Thanks
    Tuesday, October 28, 2008 12:16 AM

Answers

  • Apparment modes are only valid within the context of COM operations. A native thread doesn't really have an appartment mode until some COM code is executed. At that point, prior to calling any COM operations, you need to set up the COM context, and part of that setup specifies the appartment model. From my understanding, once that model is set, there's no way to reset it. If the model is set as STA, then the initiating thread is flagged as having joined an/the STA context.

    Also, there is no (managed) way to set the appartment mode (state) of a thread after it starts (that i know of), with the exception of the main thread, which always starts off as MTA (in reality, it's more like "unknown" because the COM context hasn't be initilized yet most likely) but can change to STA if the Main() entry point function is tagged with the [STAThreadAttribute]. At that point, the COM context is initialized and the thread joins the context as STA, and can no longer be reset to my knowledge, although the initial change might be deferred until an actual COM operation is invoked (that part I'm not sure about).

    This might also occur if you use COM components/libraries, which intermally, might initialize the COM context themselves before any COM operations are invoked. The act initializing the COM context could in theory set the thread state. However, if the COM context has already been set by another piece of code, and the new piece of code tries to use a different appartment model, then you'll get a low-level COM exception.
    -Rob Teixeira
    • Marked as answer by Zhi-Xin Ye Monday, November 3, 2008 12:13 PM
    Tuesday, October 28, 2008 10:05 PM

All replies

  • > temporary period while they are Unknown

    In .NET 2.0, this is only the case before the thread actually starts.

    > can have their state changed

    I think you may be mistaken.  Per the documentation, the state is not changable (other than the initial set which, if it happens at all, must happen before the thread is started):

    http://msdn.microsoft.com/en-us/library/system.threading.thread.setapartmentstate.aspx

    What debugger are you using to examine the threads and their apartment states?

    Tuesday, October 28, 2008 2:46 AM
  • BinaryCoder said:

    > temporary period while they are Unknown

    In .NET 2.0, this is only the case before the thread actually starts.


    That's what I was thinking, and why I am asking the question.

    > can have their state changed

    I think you may be mistaken.  Per the documentation, the state is not changable (other than the initial set which, if it happens at all, must happen before the thread is started):

    http://msdn.microsoft.com/en-us/library/system.threading.thread.setapartmentstate.aspx

    What debugger are you using to examine the threads and their apartment states?


    I am using WinDbg, with sos extension.

    Then, I issue a !threads command.

    To confirm this point, I tried to use Adplus in Hang Mode, and made several dumps, each one separated by a few seconds, and could clearly see that in some situation (not always the case when I start the process), a thread is tagged as MTA, then becomes Unk and then becomes STA.

    Actually, I'm asking this because that I noticed that over time, some of the threads I'm using is becoming STA and some time later, probably (??) because a COM object is not properly released, the finalizer tries to switch to its owning thread (this STA thread) but blocks in a ole32:GetToSTA call, which causes memory to grow till OOM Exception.

    The threads I am using are using Win32 IOCP. I am not specifically tagging these threads as MTA, but they are started as MTA by default.

    Is there an explanation to this?

    Tuesday, October 28, 2008 6:16 AM
  • There is no documented way to change the apartment type.  A thread joins an apartment by calling CoInitializeEx().  The dwCoInit argument selects the apartment type.  Calling it again with a different value returns the RPC_E_CHANGED_MODE error code.  A .NET thread always joins an apartment.  It is selected by either the [STAThread] attribute or SetApartmentState().  After the thread is started, it cannot be changed again.

    It is conceivable that the CLR hacks the internal COM data structures.  But very unlikely.  Memory corruption would be a likelier explanation.  The most likely explanation is that you see a *new* thread that happens to have the same thread ID.


    Hans Passant.
    Tuesday, October 28, 2008 9:45 AM
    Moderator
  • Hello,

    Thanks for your reply.

    I am rather sure that's the same thread that's having its state changed.

    You can find below the "!threads" result made a three different times:

    @18h23'04 - 825
    0:000> !threads -special
    ThreadCount: 25
    UnstartedThread: 0
    BackgroundThread: 23
    PendingThread: 0
    DeadThread: 1
    Hosted Runtime: no
                                          PreEmptive   GC Alloc           Lock
           ID OSID ThreadOBJ    State     GC       Context       Domain   Count APT Exception
       0    1  500 00153fd0      6020 Enabled  00000000:00000000 00148628     0 STA
       2    2 21e0 0014b738      b220 Enabled  00000000:00000000 00148628     0 MTA (Finalizer)
       5    3 26c8 00198dc8   880b220 Enabled  00000000:00000000 00148628     0 MTA (Threadpool Completion Port)
       7    4 275c 0019d9b8    80a220 Enabled  00000000:00000000 00148628     0 MTA (Threadpool Completion Port)
    XXXX    5    0 001a42b8     10820 Enabled  00000000:00000000 00148628     0 Ukn
       8    6 22f0 001a4b60   180b220 Enabled  00000000:00000000 00148628     0 MTA (Threadpool Worker)
      12    7 19a8 0022eb70   200b220 Enabled  00000000:00000000 00148628     0 MTA
      13    8 1360 03e8dde0   200b220 Enabled  00000000:00000000 00148628     0 MTA
      14    a  c48 03ed2140   200b220 Enabled  05ca9500:05caaafc 00148628     0 MTA
      15    b  ce0 00203750   200b220 Enabled  05cef440:05cf0afc 00148628     0 MTA
      17    c  b3c 03ed9c98   200b220 Enabled  00000000:00000000 00148628     0 MTA
      18    d 219c 03f2db90   380b220 Enabled  00000000:00000000 00148628     0 MTA (Threadpool Worker)
      20    9 1b7c 0021eef0   200b220 Enabled  05cd3eb8:05cd4afc 00148628     0 MTA
      21    e 1da8 0565c818   200b220 Enabled  00000000:00000000 00148628     0 MTA
      22    f 1ecc 0564d528   200b220 Enabled  05cd28b0:05cd2afc 00148628     0 MTA
      23   10 1a0c 0566b308   200b220 Enabled  05ca6b88:05ca8afc 00148628     0 MTA
      24   11 1820 0566c2c8      b220 Enabled  05ce8948:05ce8afc 00148628     0 MTA
      25   12 2480 0566d4c0      b220 Enabled  05ce0d5c:05ce2afc 00148628     1 MTA
      26   13 12e4 0566ece8   200b220 Enabled  00000000:00000000 00148628     0 MTA
      27   14 1358 05670890   200b220 Enabled  05cb1480:05cb2afc 00148628     0 MTA
      28   15 1c4c 05672460   200b220 Enabled  00000000:00000000 00148628     0 MTA
      29   16 1fe4 05673728   200b220 Enabled  00000000:00000000 00148628     0 MTA
      30   17 19a0 05675a90   200b220 Enabled  00000000:00000000 00148628     0 MTA
      31   18 1270 05676e60   200b220 Enabled  00000000:00000000 00148628     0 MTA
      32   19 16b0 05679998   200b220 Enabled  00000000:00000000 00148628     0 MTA

    @18h23'09 - 341
    0:000> !threads
    ThreadCount: 27
    UnstartedThread: 0
    BackgroundThread: 25
    PendingThread: 0
    DeadThread: 1
    Hosted Runtime: no
                                          PreEmptive   GC Alloc           Lock
           ID OSID ThreadOBJ    State     GC       Context       Domain   Count APT Exception
       0    1  500 00153fd0      6020 Enabled  00000000:00000000 00148628     0 STA
       2    2 21e0 0014b738      b220 Enabled  00000000:00000000 00148628     0 MTA (Finalizer)
       5    3 26c8 00198dc8   880b220 Enabled  05cf0cd4:05cf2afc 00148628     0 MTA (Threadpool Completion Port)
       7    4 275c 0019d9b8    80a220 Enabled  00000000:00000000 00148628     0 MTA (Threadpool Completion Port)
    XXXX    5    0 001a42b8     10820 Enabled  00000000:00000000 00148628     0 Ukn
       8    6 22f0 001a4b60   180b220 Enabled  00000000:00000000 00148628     0 MTA (Threadpool Worker)
      12    7 19a8 0022eb70   200b220 Enabled  00000000:00000000 00148628     0 MTA
      13    8 1360 03e8dde0   200b220 Enabled  00000000:00000000 00148628     0 MTA
      14    a  c48 03ed2140      b220 Disabled 05e58c14:05e59fb4 00148628     0 MTA
      15    b  ce0 00203750      b220 Enabled  05e47998:05e47fb4 00148628     0 MTA
      17    c  b3c 03ed9c98   200b220 Enabled  00000000:00000000 00148628     0 MTA
      18    d 219c 03f2db90   380b220 Enabled  00000000:00000000 00148628     0 MTA (Threadpool Worker)
      20    9 1b7c 0021eef0      b220 Enabled  05ec1e10:05ec1fb4 00148628     1 MTA
      21    e 1da8 0565c818      b220 Enabled  05e6aa3c:05e6bfb4 00148628     0 MTA
      22    f 1ecc 0564d528      b220 Enabled  05ed2848:05ed3fb4 00148628     0 MTA
      23   10 1a0c 0566b308   200b220 Enabled  05f19e64:05f19fb4 00148628     0 MTA
      24   11 1820 0566c2c8      b220 Enabled  05f38150:05f39fb4 00148628     0 MTA
      25   12 2480 0566d4c0      b220 Enabled  05f3a0b0:05f3bfb4 00148628     0 MTA
      26   13 12e4 0566ece8      b220 Enabled  05eb1ad4:05eb1fb4 00148628     1 MTA
      27   14 1358 05670890   200b220 Enabled  05ef6168:05ef7fb4 00148628     0 MTA
      28   15 1c4c 05672460   200b220 Enabled  05ee4b94:05ee5fb4 00148628     0 MTA
      29   16 1fe4 05673728   200b220 Enabled  05e9eb94:05e9ffb4 00148628     0 MTA
      30   17 19a0 05675a90   200b220 Enabled  05e7c794:05e7dfb4 00148628     0 MTA
      31   18 1270 05676e60   200b220 Enabled  05f08168:05f09fb4 00148628     0 MTA
      32   19 16b0 05679998      b220 Enabled  05f2b010:05f2bfb4 00148628     0 Ukn
      33   1a 1a64 056699a8   880b220 Enabled  00000000:00000000 00148628     0 MTA (Threadpool Completion Port)
      34   1b 1f54 00209808   880b220 Enabled  00000000:00000000 00148628     0 MTA (Threadpool Completion Port)

    @18h23'12 - 028
    0:000> !threads
    ThreadCount: 27
    UnstartedThread: 0
    BackgroundThread: 25
    PendingThread: 0
    DeadThread: 1
    Hosted Runtime: no
                                          PreEmptive   GC Alloc           Lock
           ID OSID ThreadOBJ    State     GC       Context       Domain   Count APT Exception
       0    1  500 00153fd0      6020 Enabled  00000000:00000000 00148628     0 STA
       2    2 21e0 0014b738      b220 Enabled  00000000:00000000 00148628     0 MTA (Finalizer)
       5    3 26c8 00198dc8   880b220 Enabled  00000000:00000000 00148628     0 MTA (Threadpool Completion Port)
       7    4 275c 0019d9b8    80a220 Enabled  00000000:00000000 00148628     0 MTA (Threadpool Completion Port)
    XXXX    5    0 001a42b8     10820 Enabled  00000000:00000000 00148628     0 Ukn
       8    6 22f0 001a4b60   180b220 Enabled  00000000:00000000 00148628     0 MTA (Threadpool Worker)
      12    7 19a8 0022eb70   200b220 Enabled  00000000:00000000 00148628     0 MTA
      13    8 1360 03e8dde0   200b220 Enabled  00000000:00000000 00148628     0 MTA
      14    a  c48 03ed2140      b220 Enabled  05afe590:05aff0e4 00148628     0 MTA
      15    b  ce0 00203750      b220 Enabled  05c94e94:05c950e4 00148628     0 MTA
      17    c  b3c 03ed9c98   200b220 Enabled  00000000:00000000 00148628     0 MTA
      18    d 219c 03f2db90   380b220 Enabled  00000000:00000000 00148628     0 MTA (Threadpool Worker)
      20    9 1b7c 0021eef0      b220 Enabled  05bc4bf4:05bc50e4 00148628     0 MTA
      21    e 1da8 0565c818      b220 Enabled  05bacbd8:05bad0e4 00148628     1 MTA
      22    f 1ecc 0564d528      b220 Enabled  05ba0590:05ba10e4 00148628     0 MTA
      23   10 1a0c 0566b308      b220 Enabled  05cd6a00:05cd70e4 00148628     0 MTA
      24   11 1820 0566c2c8      b220 Enabled  05cb5e78:05cb70e4 00148628     0 MTA
      25   12 2480 0566d4c0      b220 Enabled  05ba99c8:05bab0e4 00148628     0 MTA
      26   13 12e4 0566ece8      b220 Enabled  05ae78d0:05ae90e4 00148628     0 MTA
      27   14 1358 05670890      b220 Enabled  05b233ec:05b250e4 00148628     0 MTA
      28   15 1c4c 05672460      b220 Enabled  05c7bec8:05c7d0e4 00148628     0 MTA
      29   16 1fe4 05673728      b220 Enabled  05c2e5d0:05c2f0e4 00148628     0 MTA
      30   17 19a0 05675a90      b220 Enabled  05cce590:05ccf0e4 00148628     0 MTA
      31   18 1270 05676e60      b220 Enabled  05c24e94:05c250e4 00148628     0 MTA
      32   19 16b0 05679998      b220 Enabled  05c80590:05c810e4 00148628     0 STA
      33   1a 1a64 056699a8   880b220 Enabled  00000000:00000000 00148628     0 MTA (Threadpool Completion Port)
      34   1b 1f54 00209808   880b220 Enabled  00000000:00000000 00148628     0 MTA (Threadpool Completion Port)

    As you can see, these dumps have been made at very close times. Regarding all the info above, would it be possible that we are in fact facing two different threads with the same ID?

    The second possibility you mentioned would be a kind of memory corruption. Here again, we can see that the state is MTA -> Ukn -> STA. In case of memory corruption, I don't think we would have this transition?

    What else could I do to investigate the problem further?




    Tuesday, October 28, 2008 10:52 AM
  • Apparment modes are only valid within the context of COM operations. A native thread doesn't really have an appartment mode until some COM code is executed. At that point, prior to calling any COM operations, you need to set up the COM context, and part of that setup specifies the appartment model. From my understanding, once that model is set, there's no way to reset it. If the model is set as STA, then the initiating thread is flagged as having joined an/the STA context.

    Also, there is no (managed) way to set the appartment mode (state) of a thread after it starts (that i know of), with the exception of the main thread, which always starts off as MTA (in reality, it's more like "unknown" because the COM context hasn't be initilized yet most likely) but can change to STA if the Main() entry point function is tagged with the [STAThreadAttribute]. At that point, the COM context is initialized and the thread joins the context as STA, and can no longer be reset to my knowledge, although the initial change might be deferred until an actual COM operation is invoked (that part I'm not sure about).

    This might also occur if you use COM components/libraries, which intermally, might initialize the COM context themselves before any COM operations are invoked. The act initializing the COM context could in theory set the thread state. However, if the COM context has already been set by another piece of code, and the new piece of code tries to use a different appartment model, then you'll get a low-level COM exception.
    -Rob Teixeira
    • Marked as answer by Zhi-Xin Ye Monday, November 3, 2008 12:13 PM
    Tuesday, October 28, 2008 10:05 PM
  • Hello,

    Thanks for your reply.

    I finally understood how comes the Apartment State of my thread changed: this is due to the Sybase ADO.NET driver we are using.

    Actually, whay happens is that anytime this driver releases a connection (but without closing it - connection pool), it calls COInitializeEx with COINIT_APARTMENTTHREADED. Most of the time, this doesn't have any effect, as my thread is an MTA thread, and this call probably fails.

    We can see that here:

    0:028> kb
    ChildEBP RetAddr  Args to Child             
    0546e658 77502a46 00000000 00000002 0546e748 ole32!CoInitializeEx
    0546e668 0844e2db 00000000 08630450 08443b22 ole32!CoInitialize+0xf
    WARNING: Stack unwind information not available. Following frames may be wrong.
    0546e814 7c34218f 08631ee4 09578384 00000000 sybdrvado115a!GetXaSwitch+0x3b
    0546e848 01587bf8 07c8c296 09576970 020ed52c MSVCR71!free+0xc8 [f:\vs70builds\3052\vc\crtbld\crt\src\free.c @ 103]
    0546e850 09576970 020ed52c 020fa1b8 00000000 0x1587bf8
    0546e854 020ed52c 020fa1b8 00000000 037b27e9 0x9576970
    0546e858 020fa1b8 00000000 037b27e9 00000000 0x20ed52c
    0546e85c 00000000 037b27e9 00000000 09576970 0x20fa1b8
    0:028> !clrstack
    OS Thread Id: 0x1788 (14)
    ESP       EIP    
    0546e760 774fef6b [NDirectMethodFrameSlim: 0546e760] Sybase.Data.AseClient.Unmanaged.OleToXAEnlistTransaction(IntPtr, System.String, System.EnterpriseServices.ITransaction)
    0546e774 0841cbbe Sybase.Data.AseClient.AseConnectionImpl.EnlistDistributedTransaction(System.EnterpriseServices.ITransaction)
    0546e788 0841c9dc Sybase.Data.AseClient.AseConnectionPool.ReturnConnection(Sybase.Data.AseClient.AseConnectionImpl)
    0546e7b4 0841c926 Sybase.Data.AseClient.AseConnectionPoolManager.ReturnConnection(Sybase.Data.AseClient.AseConnectionImpl)
    0546e7bc 0841c817 Sybase.Data.AseClient.AseConnection.Close(Boolean)
    0546e80c 0841c5f2 Sybase.Data.AseClient.AseConnection.Close()

    However, it happens that when this same driver physically closes a connection (for example, because it has been opened for too long), it calls COUnitialize, which has the effect to set the thread state to Unknown:

    0:014> kb
    ChildEBP RetAddr  Args to Child             
    0546e578 0844e366 08517118 7c91188a 7c34240d ole32!CoUninitialize
    WARNING: Stack unwind information not available. Following frames may be wrong.
    00000000 00000000 00000000 00000000 00000000 sybdrvado115a!GetXaSwitch+0xc6

    0:014> !clrstack
    OS Thread Id: 0x1788 (14)
    ESP       EIP    
    0546e620 774fee36 [NDirectMethodFrameStandalone: 0546e620] Sybase.Data.AseClient.Unmanaged.DestroyConnection(IntPtr)
    0546e630 08889154 Sybase.Data.AseClient.AseConnectionImpl.Close()
    0546e664 08888edb Sybase.Data.AseClient.AseConnectionImpl.Dispose(Boolean)
    0546e66c 0841ca1c Sybase.Data.AseClient.AseConnectionPool.ReturnConnection(Sybase.Data.AseClient.AseConnectionImpl)
    0546e698 0841c926 Sybase.Data.AseClient.AseConnectionPoolManager.ReturnConnection(Sybase.Data.AseClient.AseConnectionImpl)
    0546e6a0 0841c817 Sybase.Data.AseClient.AseConnection.Close(Boolean)
    0546e6f0 0841c5f2 Sybase.Data.AseClient.AseConnection.Close()
    0546e6f4 0841c565 Sybase.Data.AseClient.AseDataReader.Close()
    0546e700 0841c4aa Sybase.Data.AseClient.AseDataReader.Dispose(Boolean)
    0546e708 0841c477 Sybase.Data.AseClient.AseDataReader.Dispose()
    ...

    And here, what happens is simple: the next time this driver uses a connection on the same thread, it calls again CoInitializeEx as above, but this time, as the threading model is unknown, the call succeeds, and the state of the thread changes to STA.

    I don't understand why the driver tries to do this, and the rationale behind it.

    So, actually, I'm quite stuck, because my thread state can change from MTA to STA without my knowing, and this can lead to havoc (because my thread is not ready to reply to COM calls from other thread, like the finalizer thread trying to release a COM component).

    I would be happy if I could prevent the driver from doing this kind of call... But that's quite hopeless.

    Thanks for your help.



    Thursday, October 30, 2008 6:38 PM