none
deadlock when using waitOne in a STA thread RRS feed

  • Question

  • Hi,

    I have the problem described bellow with the calls to WaitHandle.WaitOne
    causing re-entrancy on main GUI thread (STA) of a .NET application and I
    would like to find out:
       - did anybody else run into the same problem?
       - is it a known issue? is there a fix for it?
       - is there a workaround for it?
       - if no fix/workaround I would like to find out the complete list of
    messages that are being handled (dispatched by ole32.dll OLE/COM message
    pump) when doing WaitOne on a STA thread.

    The problem is:

    I have 2 (or more) calls pending to execute on the main gui thread (I am
    actually doing Invoke on some control)
    the first call, at some point does WaitHandle.WaitOne.
    at this point, if a Windows message comes to the app (like
    WM_POPUPSYSTEMMENU - 0x0313 ) triggered by a right-click on the task bar
    icon of the app the following happens:
        - the app processes this message
        - the app starts processing other pending messages - like my second
    invoke
        - at this point my app is deadlocked, because it is not supposed to
    enter the second call before finishing processing the first one.
        - also at this point the entire system is not behaving properly - for
    example I cannot bring up any window by clicking on the taskbar icon
    important note: if I would not do right click on the taskbar icon of my app,
    and just let it work in background, everything would be ok.

    After investigating this issue for a while, I found that WaitOne is not
    actually a blocking call when called from an STA thread.

    The only reference  I found related to this matter in  MSDN pages is here:
    http://msdn.microsoft.com/en-us/library/74169f59.aspx .
    Quoting from that page:
     "WaitHandle..::.WaitOne,WaitHandle..::.WaitAny, WaitHandle..::.WaitAll,
    Monitor..::.Enter, Monitor..::.TryEnter, Thread..::.Join,
    GC..::.WaitForPendingFinalizers, and so on are all responsive to
    Thread..::.Interrupt and to Thread..::.Abort. Also, if your thread is in a
    single-threaded apartment, all these managed blocking operations will
    correctly pump messages in your apartment while your thread is blocked."

    If the apartment state is STA this function actually creates a message loop
    that is processing some events (for now I have been able to identify 2 of
    them (WM_POPUPSYSTEMMENU and WM_ACTIVATEAPP).  The problem is that these
    messages triggers processing of other messages in the queue (like the
    registered messages used for invoke in .NET ).

    I have attached a simplified application to demonstrate the issue. In this
    app I have created a thread from where I am calling multiple times the same
    function which is suppose to execute on the GUI thread.
    I have put some console prints to show that it is executing another invoke
    on the main thread even if it is supposed to stay in the wait.

    Another thing, in my app the calls to some functions are not supposed to
    re-enter (and we are protecting them with the waitOnes) but if I modified
    the sample app (attached to this mail) to have a non-blocking re-entrancy ,
    I noticed if I right click on the taskbar icon while the first call is in
    waitOne, all the other calls will be executed before the first call ends -
    this is actually easy to explain since the other calls are not processed by
    the app message loop but they are processed by the message loop created by
    the WaitOne. however I don't think this should be the normal behavior - that
    is: when I invoke 3 calls on some thread, the order of execution should be
    the order of the invokes.

    waiting forward for your replies
    A.

    using System; 
    using System.Collections.Generic; 
    using System.ComponentModel; 
    using System.Data; 
    using System.Drawing; 
    using System.Text; 
    using System.Windows.Forms; 
    using System.Threading; 
     
    namespace DeadLockWaitOne 
    {     
        public partial class Form1 : Form 
        { 
            #region Designer 
            private Button button1; 
            
            public Form1() 
            { 
                InitializeComponent(); 
            } 
     
            private void InitializeComponent() 
            { 
                this.button1 = new System.Windows.Forms.Button(); 
                this.SuspendLayout(); 
                //  
                // button1 
                //  
                this.button1.Location = new System.Drawing.Point(12, 12); 
                this.button1.Name = "button1"
                this.button1.Size = new System.Drawing.Size(86, 31); 
                this.button1.TabIndex = 0; 
                this.button1.Text = "button1"
                this.button1.UseVisualStyleBackColor = true
                this.button1.Click += new System.EventHandler(this.button1_Click); 
                //  
                // Form1 
                //  
                this.ClientSize = new System.Drawing.Size(292, 273); 
                this.Controls.Add(this.button1); 
                this.Name = "Form1"
                this.ResumeLayout(false); 
     
            } 
            #endregion Designer 
            #region My Code 
            private void button1_Click(object sender, EventArgs e) 
            { 
                System.Threading.Thread t = new System.Threading.Thread(delegate() 
                { 
                    test(1); 
                    test(2); 
                    test(3); 
                    test(4); 
                    test(5); 
                } 
                ); 
                t.IsBackground = true
                t.Start(); 
            } 
     
            private delegate void TestDelegate(int callNo); 
            private static int enterCount = 0; 
            private void test(int callNo) 
            { 
                Console.WriteLine("Entering... {0}", callNo); 
                if ( this.InvokeRequired ) 
                { 
                    BeginInvoke(new TestDelegate(test), callNo); 
                    return
                } 
     
                int enterCountValue = Interlocked.Increment(ref enterCount); 
                Console.WriteLine("Entered {0} - {1} time", callNo, enterCountValue); 
                 
                System.Threading.AutoResetEvent ev = new System.Threading.AutoResetEvent(false); 
     
                // if you comment out the next line you will see that the order of execution changes - it becomes 2,3,4,5,1 
                // i've put the if here to simulate the situation i have in my app. 
                //if ( enterCountValue == 1 ) 
                { 
                    System.Threading.Thread t = new System.Threading.Thread(delegate() 
                    { 
                        Thread.Sleep(10000); 
                        ev.Set(); 
                    }); 
                    t.IsBackground = true
                    t.Start(); 
                } 
                ev.WaitOne(); 
     
                Console.WriteLine("Doing something meaningfull for call #{0}", callNo); 
     
                Console.WriteLine("Exited {0} - {1} time", callNo, enterCountValue); 
                enterCountValue = Interlocked.Decrement(ref enterCount); 
            } 
            #endregion My Code 
        } 
         
         
        public class Program 
        { 
            [STAThread] 
            public static void Main(string[] args) 
            { 
                System.Windows.Forms.Application.Run(new Form1()); 
            } 
        } 
     
     
     
     
     

    Wednesday, June 11, 2008 7:37 AM

Answers

  • Your post has nothing to do with the CLR, the topic of this forum.  You'll have to go elsewhere to find somebody to volunteer the info.

    In general, it is illegal to block an STA thread.  COM relies on the STA thread's message loop to marshal method calls between threads.  Without that loop pumping messages, your program will deadlock when you use WaitOne() or WaitForSingleObject().  Accordingly, both Windows (in the case of WaitForxxx) and the .NET framework (in the case of WaitOne/Any) start their own message loop when you try to block.  This has obvious side effects if you expect to block execution of window messages or COM method calls.

    There is no good solution for your problem.  Making the UI thread a non-STA thread is not an option, lots of stuff stops working, including the clipboard, drag and drop, shell dialogs and any ActiveX control.  You'll have to revise your design.

    Hans Passant.
    • Marked as answer by Bruno Yu Monday, June 16, 2008 6:30 AM
    Thursday, June 12, 2008 12:08 PM
    Moderator

All replies

  • Your post has nothing to do with the CLR, the topic of this forum.  You'll have to go elsewhere to find somebody to volunteer the info.

    In general, it is illegal to block an STA thread.  COM relies on the STA thread's message loop to marshal method calls between threads.  Without that loop pumping messages, your program will deadlock when you use WaitOne() or WaitForSingleObject().  Accordingly, both Windows (in the case of WaitForxxx) and the .NET framework (in the case of WaitOne/Any) start their own message loop when you try to block.  This has obvious side effects if you expect to block execution of window messages or COM method calls.

    There is no good solution for your problem.  Making the UI thread a non-STA thread is not an option, lots of stuff stops working, including the clipboard, drag and drop, shell dialogs and any ActiveX control.  You'll have to revise your design.

    Hans Passant.
    • Marked as answer by Bruno Yu Monday, June 16, 2008 6:30 AM
    Thursday, June 12, 2008 12:08 PM
    Moderator
  • what about the fact that the order of execution will be changed? that is the order of the invokes

    when i simply let the app run without any intervention, i get the folowing output:

    Entering... 1

    Entering... 2

    Entering... 3

    Entering... 4

    Entering... 5

    Entering... 1

    Entered 1 - 1 time

    Doing something meaningfull for call #1

    Exited 1 - 1 time

    Entering... 2

    Entered 2 - 1 time

    Doing something meaningfull for call #2

    Exited 2 - 1 time

    Entering... 3

    Entered 3 - 1 time

    Doing something meaningfull for call #3

    Exited 3 - 1 time

    Entering... 4

    Entered 4 - 1 time

    Doing something meaningfull for call #4

    Exited 4 - 1 time

    Entering... 5

    Entered 5 - 1 time

    Doing something meaningfull for call #5

    Exited 5 - 1 time

     

    if while waiting in the first invoke i right click on the taskbar icon of the app i get this (please notice the order the invokes get executed - the order of messages 'Doing something meaningfull for call #X'):

     

    Entering... 1

    Entering... 2

    Entering... 3

    Entering... 4

    Entering... 5

    Entering... 1

    Entered 1 - 1 time

    Entering... 2

    Entered 2 - 2 time

    Doing something meaningfull for call #2

    Exited 2 - 2 time

    Entering... 3

    Entered 3 - 2 time

    Doing something meaningfull for call #3

    Exited 3 - 2 time

    Entering... 4

    Entered 4 - 2 time

    Doing something meaningfull for call #4

    Exited 4 - 2 time

    Entering... 5

    Entered 5 - 2 time

    Doing something meaningfull for call #5

    Exited 5 - 2 time

    Doing something meaningfull for call #1

    Exited 1 - 1 time

     


    Thursday, June 12, 2008 4:27 PM
  • Agarici, the problem lies in your design. You're invoking delegates on the UI thread which in turn spawn threads and wait for them to finish. Hans has already discussed what happens when you wait for the event. If you run your program in the debugger and break into it during the 10 second sleep you may find an interesting call stack on the main thread.

    As to your design, a more common pattern would be to spawn the worker threads--or queue to the thread pool--and have those threads BeginInvoke back to the UI thread to report results (only). You might also consider using BackgroundWorker.

     


    Curt - http://www.codeneverwritten.com/
    • Proposed as answer by Bruno Yu Monday, June 16, 2008 6:30 AM
    Thursday, June 12, 2008 8:59 PM
  • you may know that you are not allowed to update the UI controls from a pool thread or some other thread than they were created.

    as to your "interesting call stack" on the main thread, i know about it. that's the reason for my post...

    anyway it seems like the change in the order invokes get executed does not bother anyone reading this...


    if this is out of topic for this forum do you have any other suggestion? because i see it as a .Net framework problem. i understand that the event processing in WaitOne is designed for COM to work fine, but i'm not using COM in this simple app and i am still affected...

    A.


    Friday, June 13, 2008 5:10 PM