I am dealing with a single-threaded library (not thread safe) that I want to wrap in a COM out-of-process object to get around its thread non-safety. I need a behavior where each new COM object is instantiated in its own process - some form of out-of-process singleton.
It's been a while since I've been dealing with hardcore COM so I am not sure how to approach this or whether this is at all possible without some trickery.
Call CoRegisterClassObject() with the REGCLS argument set to REGCLS_SINGLEUSE. Beware of the cost, a process is not a cheap operating system object.
There is otherwise no reason why you couldn't ensure that marshaled method calls are thread-safe, just call CoInitializeEx() to ask for an STA. Works as well in a out-of-process server as it does in a in-process server. If you are only doing this to solve a threading problem then definitely favor in-process STA. Message loop required.
Related
This thread is great at explaining STA vs MTA for COM, however it doesn't address how to code for such or when to use one or the other, and only just discusses technicalities about COM apartments being used by thread-safe objects or not. I'd be willing to bet most users just want to know how to use the Win API through COM without any COM objects shared among multiple threads.
Should you use STA always if your COM objects aren't shared among threads, and your code makes use of multiple threads or a single thread each with its own COM object instances, none of the objects shared? Does it depend on the object you are using? If you don't always use STA for such when do you use MTA? Do you ever need a message pump in this case?
In my case I use the Task Scheduler API (ITaskService) and the Shell Links/Create Shortcut API (IShellLink) from the main GUI thread (using the Qt Framework), and the File Operation API (IFileOperation) and the Volume Shadow Copy Service API from a worker thread.
I call CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); from each thread before initializing and using the COM objects and CoUninitialize(); after. Is this proper usage of COM? Would the same apply using COM objects from the main GUI thread without a worker thread?
For making outbound COM calls to objects that you instantiated via CoCreateInstance, STA should be good enough, and is almost a must for your GUI thread (the one that has a GetMessage/DispatchMessage loop).
MTA starts to become relevant when hosting your own thread safe COM objects that are expected to be invoked from other processes.
The documentation for IFileOperation states this:
IFileOperation can only be applied in a single-threaded apartment
(STA) situation. It cannot be used for a multithreaded apartment (MTA)
situation. For MTA, you still must use SHFileOperation.
See all, this link: INFO: Calling Shell Functions and Interfaces from a Multithreaded Apartment
I suspect what the documentation is really trying to say is this:
The class implementing IFileOperation is not thread safe
It's ThreadingModel is declared "apartment" in the registry and will incur marhsalling overhead if accessed from an MTA thread.
On our application, have used ITaskScheduler on the main STA thread. And we use IFileOperation on a background STA thread that has its own message pump.
Some other links that I think are very useful:
https://support.microsoft.com/en-us/help/150777/info-descriptions-and-workings-of-ole-threading-models
https://devblogs.microsoft.com/oldnewthing/?p=22603
Consider writing a reusable custom function that inside its body creates COM objects and calls methods to some COM interfaces. For this to work properly, CoInitializeEx and the matching CoUninitialize APIs must be called.
Calling those COM initialization and cleanup APIs inside the function's body would hide a COM implementation detail to the caller, and would remove a burden from the caller as well.
But is calling CoInitializeEx and the matching CoUninitialize inside function's body considered a good coding practice?
Would calling those COM init/cleanup functions at the function-granularity level imply too much overhead for each function call?
Are there other drawbacks in this design?
It is a terrible practice and fundamentally wrong. What matters a great deal is the value for the 2nd argument (dwCoInit). It must be COINIT_APARTMENTTHREADED, often abbreviated to STA, or COINIT_MULTITHREADED (MTA). This is a promise that you make, cross-your-heart-hope-to-die style. If you break the promise then the program will die. Usually by deadlocking, not getting expected events or having unacceptably slow perf.
When you select STA then you promise that the thread is well-behaved and can support COM components that are not thread-safe. Fulfilling that promise requires that the thread pumps a message loop and never blocks. The common behavior of a thread that supports a GUI for example. The vast majority of COM components are not thread-safe.
When you select MTA then you don't promise any support at all. The component must now fend for itself to keep itself thread-safe. Often done automatically by having the COM infrastructure creating a thread by itself to give the component a safe home. A further detail that you need to take care of is marshaling the interface pointer, requires CoMarshalInterThreadInterfaceInStream() helper function or the more convenient IGlobalInterfaceTable interface. This ensures that a proxy is created that takes care of the required thread context switch.
MTA sounds convenient, but not without consequences, a simple property getter call can take as much as x10000 more time. Overhead imposed by the thread context switches and the need to copy any arguments and the return value across stack frames. And marshaling the interface pointer can easily fail, authors of COM components often don't provide the necessary proxy/stub or they intentionally omitted it because it is just plain too difficult or expensive to copy the data.
Key point is that the choice between STA and MTA can never be made by a library. It does not know beans about the thread, it did not create that thread. And cannot possibly know if the thread pumps a message loop or blocks. That's all code that is entirely out of the library's reach. Otherwise the exact reason that the COM infrastructure needs to know this as well, it likewise cannot make assumptions about the thread.
The choice must be made by the code that created and initialized the thread, invariably the app itself. Unless the library creates a thread for the purpose of making the calls safe. But then with the consequence of code always being slow. You remind the caller of your library that he didn't get it right by returning the inevitable CO_E_NOTINITIALIZED error code.
Fwiw, this is something you see back in the .NET Framework. The CLR always calls CoInitializeEx() before a thread can execute any managed code. Still a choice that must be made by the programmer of the app, or more typically the project template, done with the [STAThread] attribute on Main() or the Thread.SetApartmentState() call for a worker thread.
I know that modern Windows versions reclaim memory that was previously acquired with malloc, new and the like, after program termination, but what about COM objects? Should I call obj->Release() on them on program's exit, or will the system do this for me?
My guess it: it depends. For out of process COM, I should probably always call Release(), but for in-process COM, I think it really doesn't matter, because the COM objects die after program termination anyway.
If you're in the process itself then yes you should as you might not know where the server is and the server could be out of proc. If you're in a DLL it becomes more complicated.
In a DLL you should UNLESS you receive a DLL_PROCESS_DETACH notification, in which case you should do absolutely nothing and just let the application close. This is because this notification is called during process teardown. As such it is too late to clean up at that point. The kernel may have already reclaimed the blocks you call release on.
Remember as a DLL writer there is nothing you can do if the process exits ungracefully, you can only do what you can within reason to clean up after yourself in a graceful exit.
One easy solution is to use smart COM Pointers everywhere, the ATL and WRL have implementations that work nicely and make it so you don't have to worry about it for the most part. Even if these are stored statically their destructors will be called before process teardown or DLL unload, thus releasing safely at a time when it is safe to do so.
So the short answer is If you can e.g. you should always call release if it is safe to do so. However there are times when it is not and you should most definitely NOT do anything.
Depending on the implementation of the underlying object, there may or may not be a penalty. The object may have state that persists beyond process shutdown. A persistent lock in a local database is the easiest example that comes to mind.
With that in mind, I say it's better to call Release just in case.
in-process COM object will die with the process
out-of-process reference will be released on timeout
poorly designed servers and clients might remain in bad state holding pointers to objects (typically proxies), that are not available any longer, being unable to see they are dead. being unable to get rid of them
it is always a good idea to release the pointers gracefully
If you don't release COM pointers properly, and COM activity included marshaling, you are very likely to have exceptions in CoUninitialze which are both annoying and/or can end up showing process crash message to the user.
Is it a bad thing to spawn worker threads in your STA COM object (ie. COM object creates a thread to perform a task)? I think, the answer is - that depends!
For example in my case:
The worker threads that I am using will not interfere/access COM or COM Services.
Reason why I am asking this is because by STA COM definition STA can only house one thread. Spawning multiple threads kind of goes against this principle unless the worker threads and the work they do NOT interfere/deal with COM/COM services.
In this case I am thinking this is perfectly fine and in my opinion the worker threads should not be considered by COM as part of the logical STA.
What are your thoughts on this?
No, that's not a bad thing. Apartments explicitly exist to help you getting multi-threaded code working. An STA thread is a safe home for a COM server that's not thread-safe, COM's apartment threading model ensures that it is always used in a thread-safe way. All you have to do is the marshal the interface pointer you want to use in the worker thread (IGlobalInterfaceTable for example) and you can call the methods without doing anything special.
This doesn't come for free of course, there's overhead involved in marshaling the call. How much depends on how responsive the STA thread is when it pumps its message loop. If you intended to create the worker thread explicitly to use that COM server in a multi-threaded way then of course you'll not be ahead, you made it slower.
Don't let the worker threads use COM in any way, and you should be fine. This means you can't call COM objects in the worker and you can't call COM runtime APIs from the worker... either directly or indirectly.
The important thing to realize is that any new threads you create are new threads in their own right; it actually doesn't matter at all which thread created them. The two things that matter are: (1) that those new threads themselves call CoInitializeEx and either get their own STA each, or share an MTA together, and (2) any COM object pointers you transfer between threads get marshaled appropriately. Do not ever just pass a COM object pointer from one thread to another in a global variable; instead use the GIT or CoMarshalInterThreadInterfaceInStream as appropriate.
(One exception to this: you can pass COM pointers freely between MTA threads; but only once that pointer has been appropriately marshaled into the MTA in the first place.)
Also, you need to be very aware of there objects live and what their affinities are. If you create an object on a STA thread, and marshal a pointer to another thread, then the typical case is that the object will still live on that original STA thread with calls returning to that thread, unless you takes specific steps to specify otherwise. (Things to watch for here: what the object's threading model is, and whether it 'aggregates the free-threaded marshaller'.)
So it's not a bad thing; but be sure that you do it appropriately. For example, you might think that using two threads might be more efficient; but then later on realize that a lot of time is being spent by that worker thread calling back to the object on the original thread, giving you worse performance than a single-threaded case. So you need to think out your threads and object strategy carefully first.
(Having said all of that, you can of course spin up as many threads as you want that don't call CoInitialize, so long as they don't use COM or COM objects in any way; if those threads to need so somehow communicate with the threads that do use COM, it's up to you to manage that communication using any 'classic' IPC mechanism of your choice - eg. messages, globals, etc.)
If I call the same COM function from multiple threads to an in proc COM Dll, how thread safe is that?
Do all my objects in the COM DLL also need to be thread safe for this to work reliably?
COM takes care of threading on behalf of the COM server. The server publishes the kind of threading it supports with the ThreadingModel registry key. Very common settings are Apartment or Both. Free is very rare. A missing key is equivalent to Apartment.
COM requires a single-threaded apartment (STA) for apartment threaded servers. If you don't provide one (CoInitialize/Ex call) then it will create a dedicated thread for the server. A hard requirement for an STA thread is that it also pumps a Windows message loop. The message loop is the mechanism by which COM automatically marshals a method call from one thread to another.
So, the general answer to your question is, yes, it normally is thread-safe. There are still things that can go wrong. Deadlock is possible when a call is made from a worker thread but the STA thread isn't pumping. Or the server could be fibbing about the ThreadingModel it registered. Not uncommon with servers implemented in .NET. They get registered as Both, but there are few .NET classes that are actually thread-safe.
See this very detaled article. Basically COM will take care of synchronization - you don't need to bother. However in certain cases the consumer can experience significant slowdown because of synchronization.
Depends upon the COM objects threading model. If its free threaded then you are responsible for thread safety. If its in a single threaded apartment then you can only call it from one, and if it's in a multithreaded apartment, then you can can but as always you have to consider the implications for the object's state. there is a very good answer on SO Could you explain STA and MTA? explaining this.