5

According to here, GetMessage() is a blocking call which won't return until there's a message can be retrieved from the message queue.

So, how is this blocking behavior implemented?

Does GetMessage() use some kind of spin lock so that the UI thread just busy waits for new messages showing up in the message queue? If so, I guess at least one of my CPU cores should have high usage when a UI application is running. But I didn't see that happen. So how does it work?

ADD 1

Thanks for the hint in the comments. Spin lock is meant to reduce the cost of thread context switch. It shouldn't be used here. I also thought about maybe some event paradigm is used here. But if it is event-driven, how does this event model work?

My guess is like this:

  • An event for input checking is raised periodically. Perhaps via some hardware timer interrupt. Then the timer interrupt handler will check for various input device buffers for input events. And then put that into certain application's message queue based on the current desktop context, such as which is the active window.

And I guess maybe some other things are also based on the timer interrupt, such as thread context switching.

ADD 2

Based on replies so far. There's some event object that the UI thread waits on. But since UI thread is waiting on something, it is not active and can do nothing by itself yet. And the event object is just some passive state information. So there has to be someone else to wake up the thread upon the event state change. I tink it should be the thread scheduler. And the thread scheduler may be pulsed by the timer interrupt.

The thread scheduler will check the event state periodically and wake up thread and put messages into its queue as necessary.

Am I right about the whole picture?

ADD 3

And there's a remaining question: who modify the state of an event object? Based on here, it seems events are just some data structures that can be modified by any active parties. I think thread scheduler just use the relations among threads and events to decide which thread to run or not.

And by the time a thread is scheduled to run, all it's requirements should already been fulfilled. Such as a message should have been put into its queue before the event it waits on is raised. This is reasonable because otherwise it may be too late. (thanks to RbMm's comments.)

ADD 4

In JDK, the LinkedBlockingDeque type also offers a similar blocking behavior with the take() method.

Retrieves and removes the head of the queue represented by this deque (in other words, the first element of this deque), waiting if necessary until an element becomes available.

And the .NET counterpart is the BlockingCollection< T > type. A thread to discuss it.

Here is a thread about how to implement a blocking queue in C#.

smwikipedia
  • 61,609
  • 92
  • 309
  • 482
  • 1
    `spin lock` - `I guess at least one of my CPU cores should have high usage when a UI application is running` - of course no. `GetMessage` wait on some event object – RbMm Sep 04 '17 at 13:51
  • 2
    It waits on an event. The system signals the event when it adds a new queued message, in simple terms – David Heffernan Sep 04 '17 at 13:52
  • Regarding the edit, we refer to a Win32 event object. Websearch will tell you more about them. – David Heffernan Sep 04 '17 at 14:08
  • 2
    your guess complete wrong. when thread in windows begin wait on some synchronize object (event or another) and this object not in signaled state - the thread inserted to `DISPATCHER_HEADER.WaitListHead` via `KWAIT_BLOCK` and simply some another thread begin execute. when the event is signaled - system extract thread pointer from `KWAIT_BLOCK` (which is got from `WaitListHead`) and awaken thread – RbMm Sep 04 '17 at 14:10
  • 1
    `The thread scheduler will check/modify the event state periodically` - again you wrong here. nobody **periodically** nothing check. or another thread send/post message to our waiting thread. or when was event (interrupt) from keyboard or mouse - win32ksys insert this input event to thread and set event – RbMm Sep 04 '17 at 14:33
  • 1
    `input device drivers` - not direct. the *win32k.sys* open keyboard and mouse driver and do asynchronous read from it. when read is complete - *win32k.sys* decide to which thread push input event and awaken it. the input device drivers have no any clue about gui threads queue – RbMm Sep 04 '17 at 14:42
  • 1
    exist Raw Input Thread (RIT). in win32k wich do this job. also it accept software raw input – RbMm Sep 04 '17 at 14:51

1 Answers1

9

The exact implementation is internal and not documented.

Most of the window manager in Windows is implemented in kernel mode. GetMessage is no different. It waits on an event object in kernel mode. When a thread is waiting on an event (or any other kernel-based synchronization object), it does not use any CPU time, because it is not scheduled to run until the wait is satisfied.

When a message is posted to the waiting thread's message queue, or another thread sends a message to a window belonging to the waiting thread, or a timer in the waiting thread fires, or a window in the waiting thread is ready to be painted, the event is signaled, the thread wakes up, and GetMessage() acts accordingly, whether that is to return a posted message to the caller, or to dispatch a sent message directly to the target window procedure and then go back to waiting on the event.

You can see the implementation leaking into the public API if you compare the maximum number of objects you can wait on in WaitForMultipleObjects and MsgWaitForMultipleObjects:

The maximum number of object handles is MAXIMUM_WAIT_OBJECTS.

vs

The maximum number of object handles is MAXIMUM_WAIT_OBJECTS minus one.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Anders
  • 97,548
  • 12
  • 110
  • 164
  • 1
    _When the system/somebody sends/posts a message or a timer fires the event is signaled and the thread is woken up and GetMessage returns with the new message_ - `GetMessage()` only returns if the message was *posted* to the queue. Non-queued messages will be dispatched by `GetMessage()` *without returning*! – zett42 Sep 04 '17 at 14:58
  • @zett42 `GetMessage()` doesn't just return messages that are posted to the queue. It also returns fake messages that itself generates for timers, painting, etc when there are no other physical messages in the queue. – Remy Lebeau Sep 04 '17 at 17:27
  • 1
    @RemyLebeau This is just an implementation detail. The API contract says we have to treat messages returned by `GetMessage()` as posted messages. The focus of my comment is to correct the part of the answer that is saying that sent messages are also returned by `GetMessage()`. – zett42 Sep 04 '17 at 19:37
  • *...the thread wakes up...* my question is, who wakes up the thread? Or does the thread wake up *by itself*? – smwikipedia Sep 13 '17 at 13:09
  • 1
    When a thread calls a `*Wait*` function the thread is removed from the list of threads eligible to run (the objects it is waiting on is in another list AKA wait block (https://www.microsoftpressstore.com/articles/article.aspx?p=2233328&seqNum=4#)). When a different thread signals the object (calls SetEvent, ends with ExitProcess etc.) the thread is added back to the list of threads eligible to run. See http://windowsitpro.com/systems-management/inside-windows-nt-scheduler-part-1 or the NT internals book and their scheduler chapters. – Anders Sep 13 '17 at 13:27
  • 1
    ... and https://www.microsoftpressstore.com/articles/article.aspx?p=2233328&seqNum=7 has a nice graphic and list of a threads possible states and details about "wakeup boost" etc. but you really should get a copy of the Windows Internals books if you want all the details. – Anders Sep 13 '17 at 13:39
  • @Anders Thanks for the useful references. I will read that book. – smwikipedia Sep 13 '17 at 13:40