none
user32 PostMessage latency RRS feed

  • Question

  • Hi,
     
    While developing a .NET app, I noticed that on some machines, calls to PostMessage would occasionally take over 100 msecs.  So I made this small repro app.  It calls PostMessage once a second.  I let it run for hours and it would observe this.  Does anyone know why PostMessage <which should just update the UI thread queue and return> would take that long?  The machines are not anywhere near max CPU.
     
    Here's the very simple repro.

        public partial class MainForm : Form {
            private IntPtr                              controlHandle;
            private uint                                invokeMessage;
            private readonly System.Threading.Timer     timer; 
            private double                              timeCollected;
    
            public MainForm() {
                InitializeComponent();
    
                timer = new System.Threading.Timer(TimerCallback);
            }
    
            private void MainForm_Load(object sender, EventArgs e) {
                controlHandle = this.Handle;
                invokeMessage = RegisterWindowMessage("Rho.Windows.Forms.ControlInvoker");
                if (invokeMessage == 0) {
                    throw new Win32Exception();
                }
                timer.Change(1000, 1000);
            }
    
            private void TimerCallback(object state) {
                HandleRef handleRef = new HandleRef(this, controlHandle);
    
                DateTime startTime = DateTime.UtcNow;
                bool ok = PostMessage(handleRef, invokeMessage, new IntPtr(1), IntPtr.Zero);
                DateTime endTime = DateTime.UtcNow;
    
                if (startTime != endTime) {
                    double msec = endTime.Subtract(startTime).TotalMilliseconds;
                    if (msec >= 100.0) {
                        Interlocked.Exchange(ref timeCollected, msec);
                    }
                }
            }
    
            protected override void WndProc(ref Message m) {
                bool processed = false;
                if (m.Msg == invokeMessage) {
                    processed = true;
                    string newData = string.Empty;
                    double msec = Interlocked.Exchange(ref timeCollected, 0);
                    if (msec > 0) {
                        newData += msec.ToString() + Environment.NewLine;
                    }
                    if (newData != string.Empty) {
                        textBox1.Text += newData;
                    }
                }
    
                if (!processed) {
                    base.WndProc(ref m);
                }
            }
    
            [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto), SuppressUnmanagedCodeSecurity]
            public static extern uint RegisterWindowMessage(string lpString);
    
            [return: MarshalAs(UnmanagedType.Bool)]
            [DllImport("user32.dll", SetLastError = true), SuppressUnmanagedCodeSecurity]
            public static extern bool PostMessage(HandleRef hWnd, uint msg, IntPtr wParam, IntPtr lParam);
        }
    
     
    Thanks!
    YeeMan
    Friday, April 24, 2009 7:30 PM

Answers

  • -> I'd like to know if there are any other reasons/considerations other than Windows just happen to pre-empt my thread right before calling PostMessage.

    From the unmanaged side, so the PostMessage will needs to update the shared state, aka inserting the message into the "post message" queue of the targetting UI thread, there is some of the synchronization going on here which will block for a while in the process of inserting the message which is a part of PostMessage call.

    From the managed side, another potential reason is GC which the GC will supsend the thread if it's in the managed frame aka executing managed code, so there is a potential that before the CLR prepares the transition frame for the Pinvoke call, an asynchronous GC happens (asynchronous GC means that the current thread is not the thread which triggers GC).

    Whenever you have suspision on the performance issue, it's always helpful and constructive to use good profiling tool to figure out what's going on, you could try xperf which could profile both user mode and kernel mode code.

    Thanks
    Marco

    Another Paradigm Shift
    http://shevaspace.blogspot.com
    • Edited by Marco Zhou Wednesday, April 29, 2009 5:05 AM bad typo
    • Marked as answer by YeeManB Wednesday, April 29, 2009 8:13 PM
    Wednesday, April 29, 2009 3:57 AM

All replies

  • It takes a context switch to the thread that pumps the message queue for the window to get the message processed.  An occasional 100 msec delay on Windows is certainly not unusual.  You'd need a high priority thread waiting on a synchronization object to minimize those delays.  And well-behaved device drivers.  You can't get that from PostMessage, consider a named pipe.

    Hans Passant.
    Friday, April 24, 2009 8:11 PM
    Moderator
  • Is it different if you use Stopwatch instead of substracting DateTimes?
    Friday, April 24, 2009 8:15 PM
  • Hi Hans,


    From MSDN
    The PostMessage function places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message.


    Does it need to context switch just to post a message and return?  Your answer makes sense for SendMessage which waits for the message to be processed.


    Thanks for your help!
    YeeMan

    Monday, April 27, 2009 6:50 PM
  • Erm, no, it takes a context switch for the receiving window call GetMessage() and process the message.  Nevertheless, it is quite possible that the thread gets pre-empted by the clock interrupt just as it is calling PostMessage().  Once in a while.  Are we talking about the same thing?

    Hans Passant.
    Monday, April 27, 2009 7:02 PM
    Moderator
  • In my code above, I am timing just the call to PostMessage.  

                DateTime startTime = DateTime.UtcNow;
                bool ok = PostMessage(handleRef, invokeMessage, new IntPtr(1), IntPtr.Zero);
                DateTime endTime = DateTime.UtcNow;


    It is possible as you say for the thread to get pre-empted just as it is calling PostMessage.  I just find it odd that it would be 100+ msec sometimes and I only see this on certain machines.  I understand that Windows isn't a real time OS. 

    Here is a sample of the msecs > 100 that I would get.  (This happened in 1 hour)
    125
    264
    217
    203
    188
     
    I'd like to know if there are any other reasons/considerations other than Windows just happen to pre-empt my thread right before calling PostMessage.



    Thanks!
    YeeMan
    • Marked as answer by YeeManB Wednesday, April 29, 2009 8:14 PM
    • Unmarked as answer by YeeManB Wednesday, April 29, 2009 8:14 PM
    Monday, April 27, 2009 7:37 PM
  • -> I'd like to know if there are any other reasons/considerations other than Windows just happen to pre-empt my thread right before calling PostMessage.

    From the unmanaged side, so the PostMessage will needs to update the shared state, aka inserting the message into the "post message" queue of the targetting UI thread, there is some of the synchronization going on here which will block for a while in the process of inserting the message which is a part of PostMessage call.

    From the managed side, another potential reason is GC which the GC will supsend the thread if it's in the managed frame aka executing managed code, so there is a potential that before the CLR prepares the transition frame for the Pinvoke call, an asynchronous GC happens (asynchronous GC means that the current thread is not the thread which triggers GC).

    Whenever you have suspision on the performance issue, it's always helpful and constructive to use good profiling tool to figure out what's going on, you could try xperf which could profile both user mode and kernel mode code.

    Thanks
    Marco

    Another Paradigm Shift
    http://shevaspace.blogspot.com
    • Edited by Marco Zhou Wednesday, April 29, 2009 5:05 AM bad typo
    • Marked as answer by YeeManB Wednesday, April 29, 2009 8:13 PM
    Wednesday, April 29, 2009 3:57 AM
  • Thanks for the pointer to xperf Marco.  I will look into it.

    For GC, I have ruled it out by watching my custom performance counter which counts the big gaps (above) and the 3 built in GC Gen 0, 1, and 2 counters in Perfmon.  They do not match up.

    I read about the THREADINFO sttructure in Richter's book Programming Applications.  I was hoping MSFT would have implemented a lockless queue for the PostMessage queue. 
    Wednesday, April 29, 2009 8:13 PM