0

I am working on an C++ MFC/COM application (including VBA and .NET code) that needs to execute code on the COM STA thread from another thread.

One way, that works, is to use the PostMessage function to post a message to a hidden HWND_MESSAGE window, but it's quite slow (in my application it can take ~4ms to get onto the STA this way.

Instead using SendNotifyMessage instead is significantly faster. In this case it takes < ~0.1ms to get onto the STA.

Due to the nature of the application and the number of calls that happen (outside of my control) this difference matters. Unfortunately I'm running into strange errors which are hard to pinpoint. Some might possibly relate to STA reentrancy, for example if some .NET code is pumping messages internally while blocking on a WaitHandle.WaitOne I have noticed reentrancy. VBA is also involved.

My questions are:

  • can SendNotifyMessage be used to get onto the STA thread, instead of PostMessage?
  • if so, is there a way to make it "play nice" with .NET? (making it non-blocking or using blocking non-pumping waits are not an option)
  • (bonus) how does SendNotifyMessage actually get onto the STA thread so fast?
Marcus
  • 5,987
  • 3
  • 27
  • 40
  • 1
    IMHO, to "play nice", use official ways to communicate between COM objects, w/o using exotic plumbing. Are there reasons why do you need all this? – Simon Mourier Mar 21 '18 at 09:49
  • The problem with PostMessage is eventually that it takes time until the thread wakes up, pumps the message and delivers it. SendNotifyMessage seams to be a shortcut for this. But it places its message at the top of the message list that must be delivered and so it bypasses other messages that were initiated with PostMessage, I would use a simple normal STA COM way to do this all... marshal a COM pointer and use it... – xMRi Mar 21 '18 at 09:50
  • Window message are always dispatched on the thread, that owns the window. Either one of the API calls in this question will subsequently run code on the thread that owns the (message-only) window. I'm not sure what issues you have run into, but reentrancy could well be one of the reasons. [When can a thread receive window messages?](https://blogs.msdn.microsoft.com/oldnewthing/20040608-00/?p=38983) is probably a relevant read. – IInspectable Mar 21 '18 at 09:50
  • @xMRi: `PostMessage` does not need to wait for the receiver to wake up and dispatch messages. It simply enqueues the messages into the message queue, and returns. `SendMessage`, on the other hand, requires that the receiving thread is running, and calling either of the message retrieval functions. – IInspectable Mar 21 '18 at 09:53
  • @IInspectable: I didn't wrote about SendMessage, also I know about the behaviour of PostMessage. What was wrong with my comment? I just want to say that after PostMessage is executed, it takes time that the other thread will wake up and fetch the message... SendNotifyMessage does this all in once... – xMRi Mar 21 '18 at 09:58
  • @SimonMourier the reason I'm looking at this is, as I mentioned, performance. AFAIK the "official way" would marshal through PostMessage. – Marcus Mar 21 '18 at 10:00
  • It is unclear, though, what performance aspect you need optimized: Latency or throughput? It is also unclear to me, what those measurements are: Are those the time spans it takes for the API calls, or are they the interval between the call and the receiver handling the message? – IInspectable Mar 21 '18 at 10:05
  • The times, roughly measured, are the time from PostMessage or SendNotifyMessage being called on non-STA thread to when the callback on the STA starts running. I have a high number of calls that need processed, and switching to SendNotifyMessage can be observed to be significantly faster. – Marcus Mar 21 '18 at 10:23
  • @xMRi: Presumably, `SendNotifyMessage` also needs to wait for the receiving thread to become dispatch-ready. That requires, that the calling thread wakes up, and runs up to a point, where it can dispatch incoming cross-thread messages. The link I posted above lists all conditions, where a thread can dispatch incoming cross-thread sent messages. – IInspectable Mar 21 '18 at 10:24
  • That sounds like the figures represent latency. But when you have a high number of messages, that need to be marshaled, it sounds more like you would want to optimize throughput. At any rate, context switches are expensive. To optimize throughput you would probably have to sacrifice responsiveness, and switch to a model, where you send batches of messages instead of a single message. – IInspectable Mar 21 '18 at 10:27
  • I hardly understand this performance thing. If COM performance is such an issue, just forget about window messages, avoid marshaling, use MTA instead of STA, etc. – Simon Mourier Mar 21 '18 at 14:48

1 Answers1

0

When you do this special communication between threads and you have a large amount of messages (as you wrote in a comment) wouldn't it be better to when you use a complete different form of communication.

Creating an internal list of your messages, protect it with a critical section and use a event to notify the other thread.

Let the other thread do its message loop and use MsgWaitForMultipleObjects instead of PeekMessage / GetMessage and wait for the event.

If the event is set, process all messages that are collected...

xMRi
  • 14,982
  • 3
  • 26
  • 59
  • That still doesn't answer the question(s) asked. While the first one is answerable, the second one isn't. That really sounds like an [XY Problem](http://xyproblem.info), and it's asking for something, that `SendNotifyMessage` is already doing: It *is* a non-blocking (well, sort of) call. To provide a solution to the issue the OP is having, they would have to analyze and explain their issue first. As written this is really just a stab in the dark. – IInspectable Mar 21 '18 at 11:17