User interface code + multi-threaded apartment = death

There are single-threaded apartments and multi-threaded apartments. Well, first there were only single-threaded apartments. No wait, let's try that again.

First, applications had only one thread. Remember, 16-bit Windows didn't have threads. Each process had one of what we today call a thread, end of story. Compatibility with this ancient model still exists today, thanks to the dreaded "main" threading model. The less said about that threading model the better.

OLE was developed back in the 16-bit days, so it used window messages to pass information between processes, there being no other inter-process communication mechanism available. When you initialized OLE, it created a secret OleMainThreadWnd window, and those secret windows were used to communicate between processes (and in Win32, threads). As we learned some time ago, window handles have thread affinity, which means that these communication windows have thread affinity, which means that OLE has thread affinity. When you made a call to an object that belonged to another apartment, OLE posted a message to the owner thread's secret OleMainThreadWnd window to tell it what needs to be done, and then it went into a private message loop waiting for the owner thread to do the work and post the results back.

Meanwhile, the OLE team realized that there were really two parts to what they were doing. There was the low-level object and interface management stuff (IUnknown, CoMarshalInterThreadInterfaceInStream) and the high-level "object linking and embedding" stuff (IOleWindow, IOleDocument) that was the impetus for the OLE effort in the first place. The low-level stuff got broken out into a functional layer known as COM; the high-level stuff kept the name OLE.

Breaking the low-level and high-level stuff apart allowed the low-level stuff to be used by non-GUI programs, which for quite some time were eyeing that object management functionality with some jealousy. As a result, COM grew two personalities, one focused on the GUI customers and another focused on the non-GUI customers. For the non-GUI customers, additional functionality such as multi-threaded apartments were added, and since the customers didn't do GUI stuff, multi-threaded apartments weren't burdened by the GUI rules. They didn't post messages to communicate with each other; they used kernel objects and WaitForSingleObject. Everybody wins, right?

Well, yes, everybody wins, but you have to know what side your bread is buttered on. If you initialize a GUI thread as a multi-threaded apartment, you have violated the assumptions under which multi-threaded apartments were invented! Multi-threaded apartments assume that they are not running on GUI threads since they don't pump messages; they just use WaitForSingleObject. This not only clogs up broadcasts, but it can also deadlock your program. The thread that owns the object might try to send a message to your thread, but your thread can't receive the message since it isn't pumping messages.

That's why COM objects involved with user interface programming nearly always require a single-threaded apartment and why OleInitialize initializes a single-threaded apartment. Because multi-threaded apartments were designed on the assumption that there was no user interface. Once you're doing user interface work, you have to use a single-threaded apartment.

Comments (17)
  1. Tim Smith says:

    I’m sure that the basics of what you are saying is documented in MSDN.  However, I’ve always been the type of person who understands things better knowing the "why".

    Of course, with pure contract programming, why isn’t really important.

    Thanks for the info.

  2. David Walker says:

    I think that understanding "Why" helps you truly understand the "What", and makes you less likely to inadvertently make mistakes.  You’re also probably more likely to guess correctly in areas where the contract isn’t well-defined.

  3. Yuhong Bao says:

    That, combined with the fact that STA threads are required to pump messages, is why non-UI threads should be MTA, while UI threads should be STA. Unfortunately Visual Studio .NET defaults to STA even for console apps.

  4. Yuhong Bao says:

    “Compatibility with this ancient model still exists today, thanks to the dreaded “main” threading model.”

    VB ActiveX DLLs and controls used that threading model before a SP (3?) to VB 5 added support for the STA threading model.

    [“The less said about that threading model the better.” -Raymond]
  5. Yuhong Bao says:

    BTW, when was multi-threading support and the associated threading models added to COM? This is important if you are programming for Windows 95 or NT 3.5.

  6. jon says:

    Best explanation of the difference between STA and MTA I’ve ever read.

  7. Tim Smith says:

    Why is why important?

    It depends on how your brain is wired.

    For example, when I was getting my math degree, I had trouble remembering all those diverse formulas and rules.  However, by understanding the theory behind them, I could always re-derive the formulas because I understood the whys.  In many ways it provides a higher level understanding.

  8. Xepol says:

    This is what is known as insane by design.

    And now you all know why so few people even try to write multi-threaded code.  If the basic synchronization and communication tasks don’t get you, strange terms and stranger design choices will.

  9. Dean Harding says:

    Xepol: how would you have designed multi-threaded COM in 16-bit Windows, then?

  10. Tim Smith says:


    I’ve been doing multithreading programming for many many years.  Yes, many new people make the mistake of trying to do the UI from non-UI threads, but that is an easy lesson to learn.  But after you learn that lesson, the complexity of doing multithreaded code is huge compared to this lesson.

    Complexity of multithreaded code: infinity

    Complexity of multithreaded UI code: infinity+1

    In other words, if people are having trouble doing multithreaded UI code, they are also going to have trouble doing multithreaded code in general.  The UI aspect of it doesn’t add much complexity at all.

  11. noone in particular says:

    "how would you have designed multi-threaded COM in 16-bit Windows, then?"

    I would have thrown it overboard when switching to Win32 and designed something more straight.

    But we have heard time and time again how backward comatibility right into the palaeolithic was the one fixed requirement in the design of Windows.

  12. RandyOakley says:

    Multi-threaded code isn’t so hard, but you need to understand the constraints and methods used for synchronising threads.  The Windows messaging structure doesn’t support multiple threads and the Windows GDI gets messed up when GDI objects are allocated in one thread and deleted in a different thread. So for an app to benefit from multi-threading, the primary/UI thread runs apartment threaded and the background / worker threads run multi-threaded.  To avoid the major headaches have only the primary/UI thread pump windows messages, create windows and do GDI drawing calls.  The background threads can churn along and but not directly update the UI.  A simple technique I like to use for updating the UI after background threads have delivered data that can be used to update the UI is to use InterlockIncrement() to update a "this bit of the use needs to be updated" count.  Back on the UI thread a timer ticks — say once every 1/30 a second and checks the "this bit of the ui is dirty" counts and repaints / updates the UI as needed and clears the dirty count.  Simple, but effective.

  13. !bofh says:

    But polling is bad! Last time i checked, the events API can be used perfectly across threads.

  14. J says:

    "right into the palaeolithic"

    When this stuff was designed, the palaeolithic was last week.

  15. RandyOakley says:

    It’s true that events can be used.  However the are some advantanges to the simpler technquies.   1st of all human perception limits how frequently it is useful to update the UI — visual updates that happen more closely spaced that 1/30 will not be detetcable by the human eye.   2nd — "poling is bad" is a broad generalization — the method I’m suggesting isn’t actually poling in the typical sense.  

    Using an event based model — if five background tasks complete in one 1/30 second interval — the UI will get repainted 5 times.   Using the "repaint UI periodcially when marked dirty" model — in the same circumstance the UI will get painted only once — as far a human observer is concerned — there is no detetable difference — but the latter approach makes more system resources availble to the background tasks.

  16. Triangle says:

    "as far a human observer is concerned — there is no detetable difference — but the latter approach makes more system resources availble to the background tasks."

    Don’t be so sure about that. Instead of haphazardly guessing how good the users’ eyes are, you should use the best measurement available: The monitor refresh rate. The most efficient UI model is one where the screen is not redrawn unless there has been a change in the contents, and the most recent redraw was within the last monitor refresh. Obviously nobody would bother to implement the whole thing on their own, however if microsoft added it to a library so third parties could use it, it could be quite efficient (And would fit very neatly into the Vista window managers’ double buffering scheme)

  17. In general, it is important that any code in a managed Office add-in should execute on the main UI thread.

Comments are closed.