Asynchronous input vs synchronous input, a quick introduction

One of the topics I covered at my PDC talk was the asynchronous input model. I don’t think I ever discussed it on this Web site, so I guess I’ll do it now, so that I can point people at it in the future.

In the old days of 16-bit Windows, input was synchronous. All input went into a system-wide input queue, and the intuitive rule for input processing is that input messages are dispatched in chronological order. If the user clicked on one window, and then clicked on some other window, the first window must receive and process its click message before the second window will receive its own click message.

This model ensures that the user sees input being processed in the order it was generated, which is good. It also means that if an application stops processing input, it gums up the entire input system, which is bad. But since 16-bit Windows was coöperatively multi-tasked anyway, getting upset that a bad application could clog up the whole system by refusing to process input is missing the big picture: A bad application could clog up the whole system by simply refusing to release control of the CPU back to the operating system.

It’s like getting upset that somebody with the keys to your house could mess up the order of books on your shelf. I mean, they have the keys to your house. If they wanted to make your life miserable, they would do much more than just screw with your book collection.

In Win32, the input queues were made asynchronous. As well as a system input queue, there is also an input queue for each thread. Hardware devices post events into the system input queue, but the messages doesn’t stay in the system input queue for very long: A dedicated thread known as the raw input thread takes the events out of the system input queue and distributes them to the appropriate application input queues. (This distribution can’t be done at the time the hardware devices post the event, because that happens asynchronously. In unix speak, the hardware devices post the events into the system input queue from the bottom half, and the raw input thread processes them from the top half.)

The advantage of this model is that one thread that stops processing input does not prevent other threads from processing input, because the input for the two threads has been separated and are not dependent on each other. (This property of asynchronous input has been called the Holy Grail.)

More generally, input-related activities that were global in 16-bit Windows were made local in Win32. Each input queue has its own focus window, its own active window, its own caret, its own mouse cursor, and so on.

Things get weird when you have cross-thread input attachment, which can happen either when a parent/child or owner/owned relationship crosses a thread boundary, or by an explicit attachment created by the Attach­Thread­Input function. If cross-thread attachment is active, then all the threads which are attached to each other (which I call a thread group) share an input queue, and then the coöperative rules for input apply to that group of threads. It’s like putting on your skinny tie and shoulder-padded jacket and inviting all your friends over to pretend that it’s the 1980’s all over again.

Anyway, that’s just an introduction. We’ll dig in deeper as the week progresses.

Comments (20)
  1. Maurits says:

    Much as I love the dieresis in "coƶperate", I feel I should point out that it is archaic and the usual spelling today is "cooperate".…/cooperate

    (Another favorite of mine is "connexion".)

  2. Joker_vD says:

    Off-topic, but… why do you escape the second "o" in "cooperative"? Are the French taking over?

  3. Jesus says:

    I think Raymond wrote "coƶperate" so readers have something simple to comment on and they won't notice that he doesn't do proper error handling or accessibility in this posting.

  4. Joshua says:

    I have attached thread input queues cross-process in order to get windows stacked on top of each other cross-process. It seems to me this is nicer then setting topmost. As to whether or not this action should attach thread queues I have no idea.

  5. @Joker_vD:

    Not everyone knows that the second o in cooperative is not pronounced with the first. The dieresis is used to indicate that the vowels are pronounced separately.

    But I guess as a native speaker of English, there are some things that you take for granted. Well, I always write it as co-operative.

  6. Jon says:

    3/4 (or 4/5 including this) comments focusing on the dieresis. Looks like Raymond is going to need to add diphthongs to the list of things to avoid!

  7. Adrian says:

    I'm looking forward to more in this series.

    Lately I've been wondering why all sorts of normally well-behaved apps often show a blinking caret when they don't have the keyboard focus and often aren't even the active window.  It happens far more frequently on Vista and Windows 7 than it did on Windows XP, so I assume it's a combination of not-quite-right app code and some subtle change in the input model on the newer versions of the OS.  I'd love to understand that better and to make sure my applications don't confuse the user in the same way.

  8. Henke37 says:

    There is one type of input that a bad program can delay by accident. It is the media keys. I hope that this week will end up explaining how that happened.

  9. With all the complexities of queues, ordering, attaching queues across threads, sharing windows across processes, do you think that the model is self-consistent? Or are there inherently broken or undefined behaviors that can be triggered by calling the right APIs? Maybe ones that would destabilize the system (crash, create undestroyable windows, leaks, …).

  10. DWalker59 says:

    @Maurits:  I think you're reading too much into Wiktionary.  It says "the dieresis is becoming increasingly rare in US English typography, so the spelling cooperate predominates".  Just because something predominates doesn't mean that it is preferred.  Heck, txt-spk might soon predominate.

    Do you ever read The New Yorker magazine?  :-)

  11. Also need to mention: kernel-user callbacks, locks, windows classes and window-associated state, GDI (which seems to be integrated into user32 internally), direct x drawing to window handles, the DWM, messaging windows, hooks, sessions, window stations and desktops, UIPI. What crazy behaviors and bugs can we trigger by combining all of those pairwise? :)

  12. @xor88:

    It is more like User and GDI have some cross dependencies. But User has more dependencies on GDI (an example, BeginPaint, it is a User function, but requires a HDC, which is a GDI structure.)

    User and GDI are part of the Win32 Subsystem, so they are implemented in the same location. Up until NT 3.51, they were implemented in csrss.exe afair, and from NT4 they were both moved to Win32k.sys.

  13. 640k says:

    In win32, kernel object are too intertwined with gui objects. Win64 could have fixed that, but didn't.

    [What would be the benefit of such a fix? (I'm not sure what kernel objects you're referring to. GDI objects are not kernel objects, in the CloseHandle sense of "kernel object".) -Raymond]
  14. Joshua says:

    [What would be the benefit of such a fix?]

    Besides not running out of user heap on the service desktop when manipulating large images?

    [Now I'm confused. You originally complained about kernel/GDI interactions, but now you're complaining about user/GDI interactions. (At any rate, GDI objects do not occupy user desktop heap.) -Raymond]
  15. Joshua says:

    [Now I'm confused.]

    I'm not 640k.

    It would appear I was also confused, so who am I to complain.

  16. @640K

    Are you thinking of Multilingual User Interface or NLS? If so, you have it backwards, the locale is a system/session setting which affects the UI. There is no real cross dependencies between Kernel and User or GDI. The only little hiccup that I can think of is that DuplicateHandle can duplicate desktop handles, but that is about it.


    User should be responsible for allocating its own heap memory, so any fixes to Kernel would have no effect on your problem.

  17. .dan.g. says:

    Do people really order their book collections?

    My daughter maybe when she was younger, but half the fun (for me) of having a unorganised book collection is that when I'm looking for a specific book I bump into unexpected books along the way!

  18. >Lately I've been wondering why all sorts of normally well-behaved apps often show a blinking caret when they don't have the keyboard focus and often aren't even the active window.

    And why IE used to lose the blinking caret every so often. My best guess was it was leaking system timers (those generating WM_SYSTIMER).

  19. Mike Dimmick says:

    @Adrian, @alegr1: My understanding is that the classic caret API doesn't interoperate well with the newer drawing systems, such as Windows Presentation Foundation, Direct2D or DirectWrite. WPF has its own CaretElement; I don't think DirectWrite implements one at all. Visual Studio has been a WPF app since VS2010; Internet Explorer uses DirectWrite rather than GDI from version 9. Office 2013 is now a DirectWrite app and the caret behaviour is reported as being quite strange, often not keeping up with typing even if you have the 'smooth caret positioning' option turned off (can't remember exactly what it's called).

    I do find that often, when entering text on web pages in IE9 or later, the caret either doesn't appear, doesn't blink, or doesn't move correctly. This might be more to do with the 'enhanced' edit controls that are often used by web designers, rather than just using a standard multiline input control.

    I don't know whether Windows Runtime has more sensible/compatible caret behaviour.

  20. Adrian says:

    @Mike Dimmick:  Thanks for the insight.  I didn't realize that all the new-fangled drawing schemes re-invented the caret on their own.  But I've even seen good ol' Notepad blinking a caret when it doesn't have the focus.  Surely Notepad hasn't been corrupted?!

Comments are closed.