The dreaded “main” threading model

Date:June 2, 2004 / year-entry #217
Orig Link:
Comments:    7
Summary:In the absence of an explicit threading model for your COM object, you get the "main" threading model. The "main" threading model is little-known, and that's a good thing. It's a relic from the days before multi-threading. The first thread in a process to initialize COM becomes declared the "main" thread. (It might be the...

In the absence of an explicit threading model for your COM object, you get the "main" threading model. The "main" threading model is little-known, and that's a good thing. It's a relic from the days before multi-threading.

The first thread in a process to initialize COM becomes declared the "main" thread. (It might be the first thread to initialize COM in apartment model; I forget.) When a "main" threaded object is created, COM marshals the creation call to the main thread, creates the object, then marshals the result back to the creator's thread. Similarly, when you invoke any method on the object, the call is marshalled to the main thread, invoked, then the result is marshalled back.

In other words, a "main" threaded object is like an apartment threaded object, with the additional constraint that the only apartment that can use it is the one that the "main" thread belongs to.

As you can imagine, this is a horrific performance penalty in any multithreaded application, since there is so much marshalling going on. Even worse, it completely bottlenecks the main thread because there are now all these objects that must be serviced on that thread and no other thread.

Even worse than worse, all this marshalling creates new opportunities for re-entrancy. While waiting for the main thread to do its thing, the calling thread will likely process messages, which means that you can receive a window message at a time when you didn't expect it.

So why does this awful threading model exist at all?

For backwards compatibility with COM objects written before multithreaded support was added to COM. Back in those days, there was only one thread, so COM objects could be extremely lazy with their synchronization. In fact, they didn't need any! If you have only one thread, then you certainly don't need to coordinate your actions with other threads because there are none.

That's also why "main" threading model is the default. Threading models were invented when multithreading support was added to COM. Before then, there were no threads, so no threading models. All old objects therefore didn't specify a threading model in their registration.

The only reason you should even be aware of this ancient threading model in the first place is that if you forget to specify a threading model in your object registration, you will get the dreaded "main" threading model by default.

And then you will wonder why your application's performance is horrible, and why you have all these strange re-entrancy problems.

Comments (7)
  1. Johan Johansson says:

    Does affect a COM client that registers a callback with a COM server if the COM client uses CoInitialize rather than CoInitializeEx?

  2. Raymond Chen says:

    CoInitialize() creates a single-threaded apartment, as noted in the very first sentence of the documentation.

    In other words, CoInitialize(NULL) is 100% equivalent to CoInitializeEx(COINIT_APARTMENTTHREADED, NULL).

  3. Johan Johansson says:

    Yes, I know. It’s the leading sentences in your second paragraph that confuse me.

  4. Raymond Chen says:

    That paragraph describes how the "main" thread is determined and how the "main" thread is involved when somebody does a CoCreateInstance on an object that is marked as "main"-threaded.

    Since "main"-threaded is the same as "apartment-threaded on the main thread", it doesn’t matter whether the callback you hand back (on an STA thread) is main-threaded or apartment-threaded since the behavior is the same: Always call back on the thread it was created from.

  5. Jay Simmons says:

    (I work on the COMCOM+ team at Microsoft)

    Two small additions…Raymond is correct that the "main" STA thread is the first STA thread to be initialized in the process. And that all COM objects without an explicitly declared threading model are run on this thread. This was important, if memory serves, not just for COM objects lacking synchronization but also for certain older object runtimes that put lots of state in TLS and always expected that state to be there. So those objects *had* to be run on a dedicated thread, for the life of the process.

    Second, COM was originally written to expect that the "main" STA thread, once initialized, would never, ever, go away in the process. If it did, and you tried to create another "main"-threaded COM object, you’d get an error. We relaxed this in Windows XP and later, if the "main" thread goes away, COM will now allow a new "main" thread to be designated. This was done to allow folks to use antique COM objects without having to keep a wasteful dedicated thread around. Of course this will likely break those older object runtimes I mentioned (probably in hard-to-debug ways) but it may be okay for some objects in some situations.

  6. While I’m on the subject of COM and extension compatibility, another issue that affected a small number

  7. Multi-threaded apartments do not pump messages.

Comments are closed.

*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index