7

I would like to upgrade my MFC production code to use the std::shared_ptr smart pointer when calling other windows or threads. Such calls are SendMessage, PostMessage and PostThreadMessage which pass wparam and lparam and which respectively are an unsigned int and long. Currently, I create a class object, new an object, make the call passing a pointer to the object, use the object on the receiving end and then delete it.

Since shared_ptr works so well in the rest of my code I wanted to at least explore the reasons why I can't do the same for windows calls.

Current call:

auto myParams = new MyParams(value1, value2, value3);
PostThreadMessage(MSG_ID, 0, reinterpret_cast< LPARAM >( myParams );

ReceivingMethod::OnMsgId( WPARAM wParam, LPARAM lParam)
{
  auto myParams = reinterpret_cast< MyParams * >( lParam );
  ... // use object
  delete myParams;
}

to a C++11-like smart pointer call:

std::shared_ptr< MyParams > myParams( new MyParams( value1, value2, value3 ) );
PostThreadMessage( MSG_ID, 0, ???myParams??? );

ReceivingMethod::OnMsgId( WPARAM wParam, LPARAM lParam )
{
  auto myParams = ???lParam???;
  ... // use object
}

Edit 1:

@Remy Lebeau: Here is the sample code revised to use the unique_ptr passing approach, however, there are leaks in my code when passing the object:

struct Logger
{
  Logger()
  {
    errorLogger = ( ErrorLogger * )AfxBeginThread( RUNTIME_CLASS( ErrorLogger ), THREAD_PRIORITY_BELOW_NORMAL );
  }

  ~Logger()
  {
    // gets properly dtor'ed upon app exit
  }

  void MakeLogMsg( ... );

  ErrorLogger * errorLogger;
  std::unique_ptr< LogParams > logParams;
};

Logger logger;
std::recursive_mutex logParamsRecursiveMu; // because of multiple requests to lock from same thread

struct ErrorLogger : public CWinThread
{
  ErrorLogger()
  {
  }

  ~ErrorLogger()
  {
    // gets properly dtor'ed before logger upon app exit
  }

  afx_msg void OnLog( WPARAM wParam, LPARAM lParam );
};

void Logger::MakeLogMsg( ... )
{
  // construct msg from logparams 

  // make msg smart object using unique ptr and send to errorlogger thread queue
  logParams = std::make_unique< LogParams >();

  // set logparams

  // with the addition of the mutex guard, the leaks are gone
  logParamsRecursiveMu.lock();
  logger.errorLogger->PostThreadMessage( ONLOG_MSG, 0, reinterpret_cast< LPARAM >( logParams.get() ) );
  logParams.release(); // no longer owns object
  logParamsRecursiveMu.unlock();
}

void ErrorLogger::OnLog( WPARAM wParam, LPARAM lParam )
{
  std::unique_ptr< LogParams > logParams( reinterpret_cast< LogParams * >( lParam ) );
}

Notice that when I comment out the passing of the unique_ptr, the leaks disappear. How does my code differ from your code that uses this approach and works?

Edit2:

With regard to @Remy Lebeau‘s answer showing how std::unique_ptr could be used instead of std::shared_ptr, I stated in a comment below that there were “…no extra objects to implement. No obvious cons.”. Well, that is not quite true. The MyParams object has to be created for each different type of message. Some apps might only have a few types, but some might have 100s or more. Each time I want to execute a function on the other side I have to craft a new struct which has a constructor that takes all the arguments of the destination call. Very tedious to implement and hard to maintain if there are many.

I think that it would be possible to eliminate that struct-building phase by merely passing the arguments.

Apparently there are new C++1x constructs that can help accomplish this. One is perhaps the std::forward_as_tuple which “Constructs a tuple of references to the arguments in args suitable for forwarding as an argument to a function.”

For my app I solved the problem by templatizing MyParams, but for anyone whose wants to avoid adding a lot of structs, he may want to look at using tuples and the like.

Edit 3: Both @RemyLebeau's answer number 1 and @rtischer8277’s answer number 5 are correct. Unfortunately, StackOverflow doesn’t recognize multiple correct answers. This StackOverflow limitation reflects a flawed PsychoLinguistic assumption that linguistic context is universal for the same language group, which it is not. (see “Introduction to Integrational Linguistics” by Roger Harris on the fixed-code language myth, p.34). In response to my original posting, @RemyLebeau answered the question based on the context described by the posted code that shows a newed MyParams (see Edit 2: for more explanation). Much later in Answer 5 (rtischer8277), I answered the question myself based on the original wording of the question which asked if std::shared_ptr could be used across threads using PostThreadMessage. As a reasonable consequence, I have re-assigned the correct answer back to @RemyLebeau it being the first correct answer.

EDIT4: I added a third legitimate answer to this posting. See the 6th Answer beginning with @Remy Lebeau and @rtischer8277 have so far submitted two answers to my original posting…. The effect of this solution is to turn cross-thread access into the conceptually simple RPC (remote procedure call). Although this Answer shows how to use a future to control ownership and synchronization, it does not show how to create a message object with an arbitrary number of parameters that can safely be used on either side of the PostThreadMessage call. That functionality is addressed in StackOverflow’s Answer to issue passing a parameter pack over a legacy function signature using forward_as_tuple.

Community
  • 1
  • 1
rtischer8277
  • 496
  • 6
  • 27
  • I think the big issue here is the lifetime of the `myParams`, how long would they live past the `PostThreadMessage` call (I presume they are "stack" based? – Niall Sep 04 '14 at 14:29
  • You could pass a dynamically allocated std::shared_ptr<>. I don't see any use for your specific example, but if you need to retain the shared ownership, it might make sense in certain cases. You still need to delete the shared_ptr instance (not the MyParams instance, which will be managed by the shared_ptr<>). – sstn Sep 04 '14 at 14:42
  • If the lifetime of the `myParams` exists past the processing of the message in `OnMsgId`, then you could have `MyParams` derive from `enable_shared_from_this`, to share the lifetimes, but I'm not sure what you will win with that. – Niall Sep 04 '14 at 14:44
  • My example tries to express how I think the shared_ptr idiom might be used across the MFC call idiom although I don't yet see how. Currently, I am passing the shared_ptr object's raw pointer, but that has its drawbacks. It's weak_ptr can't be used either since it merely reports whether the object still exists or not. – rtischer8277 Sep 04 '14 at 15:35
  • You could, at best, pass a pointer to the shared_ptr<>. You have to allocate it on the heap to keep it alive. This is not useful of course. Careful with the everything-looks-like-a-nail approach, what you had before is fine. – Hans Passant Sep 04 '14 at 16:03
  • @HansPassant: one of my goals is to reduce the complexity of my app as complexity is my real enemy. Templatizing my classes has so far eliminated about 60 classes, but that leaves about 290. Either I worry about deletes or I don't which is why I submitted this question. No exceptions. – rtischer8277 Sep 05 '14 at 19:00

6 Answers6

8

Since the message parameters have to outlive the calling scope, it does not make much sense to use shared_ptr in this example, as the source shared_ptr is most likely to go out of scope before the message is processed. I would suggest using unique_ptr instead, so that you can release() the pointer while it is in flight, and then obtain new ownership of it once the message is processed:

SendingMethod::SendMsgId( ... )
{
    ...

    std::unique_ptr<MyParams> myParams( new MyParams(value1, value2, value3) );
    if (PostThreadMessage(MSG_ID, 0, reinterpret_cast<LPARAM>(myParams.get()))
        myParams.release();

    ...
}

ReceivingMethod::OnMsgId( WPARAM wParam, LPARAM lParam)
{
    std::unique_ptr<MyParams> myParams( reinterpret_cast<MyParams*>(lParam) );
    ... // use object
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I have confirmed that this answer is works. It preserves the C++11 smart pointer idiom of not having to contend with deletes and requires no extra objects to implement. No obvious cons. – rtischer8277 Sep 05 '14 at 18:45
  • 1
    The only con is if the message is not processed then the object is leaked. If you want to avoid that, you would have to keep track of the object somewhere else. – Remy Lebeau Sep 05 '14 at 21:43
  • My project guarantees message delivery so using unique_ptr still works. – rtischer8277 Sep 09 '14 at 15:04
  • There is indeed a memory leak associated with this otherwise very logical solution of releasing the ownership of the unique_ptr and re-owning it on the other side of the windows call. Regardless of where I place the myParams object - like it is in the sample above, as a member of the surrounding class, or even as a global - there is an off-by-one leak and the last unique_ptr instantiation does not get processed. Anyone see the logic flaw? – rtischer8277 Aug 10 '15 at 17:56
  • The only leak in my example is if the message is posted but not processed. There is no off-by-one flaw in my example. If you keep the `myParams` object somewhere else that outlives the message, like a class member or global, its normal reference counting takes effect, and you would not need to take re-ownership of the object being pointed at in the message since you already have ownership of it elsewhere. There is no off-by-one error unless you are creating one yourself by mishandling the object you are keeping ownership of. If you think I am wrong, show your own example to demonstrate it. – Remy Lebeau Aug 10 '15 at 19:44
  • Important to know your code works as designed. I'll look for something causing the problem in my code. – rtischer8277 Aug 10 '15 at 20:11
  • I re-wrote the sample code to show how I am using the unique_ptr method. S – rtischer8277 Aug 11 '15 at 16:23
  • @rtischer8277: How do you know a leak is happening? In any case, if `MakeLogMsg()` is called while a message is still in flight and has not been processed yet, it is destroying the `Logger::logParams` object and creating a new one, losing the previous object. This is OK for the first form of `MakeLogMsg()` since the original is passed in `LPARAM`, but the second form will lose objects. It should put the objects into a thread-safe queue that `OnLog()` can then poll from. The second form of `MakeLogMsg()` is not thread-safe as-is since it is modifying the `Logger::logParams` member. – Remy Lebeau Aug 11 '15 at 18:01
  • @Remo Lebeau: "How do you know a leak is happening?" MFC automatically dumps information about objects that have not been deleted upon exit. During development I never proceed until I have a clean output pane in Visual Studio. As was the case here, sometimes this can cost a lot of dev time, but of course not nearly as painful if production code is released with leaks. A company once paid me a 5-digit figure just to tell them what amounted to showing them how to use this MFC tool. – rtischer8277 Aug 11 '15 at 22:16
  • I rather like the visual of unique_ptr ownership being tossed between the 'poster' and the 'handler,' like a frisbee. Literally "releasing" the frisbee on one side of the wall, and placing it into a unique_ptr as it's caught on the other side, in the message handler. I've been updating my own MFC codebase to use this approach, lately. – BTownTKD Apr 11 '18 at 15:09
  • I liked this approach, although I modified it to prevent memory leak in flight (which I've only tested with `SendMessage()`). The caller wraps `MyParams` in a `std::unique_ptr` and passes `(LPARAM) &myParamsUniquePtr` to the receiver, which immediately uses `std::move( *((std::unique_ptr*) lParam) )` to transfer ownership to its own `std::unique_ptr`. `wParam` could be used to confirm the transfer by passing the address of the owned object (`std::unique_ptr::get()`). The receiver compares that to its `std::unique_ptr::get()` and sets the appropriate return value for `SendMessage()`. – pbyhistorian Jun 03 '20 at 18:49
  • @pbyhistorian you don't need to transfer ownership when using `SendMessage()` since it is synchronous and will not return until the message is done being processess. The original `unique_ptr` will remain in scope and keep the held object alive while the message is in flight, and while being processed by the receiver. The sender created the object, so it should maintain ownership until it gets control back, then it can free the object. That should not be the responsibility of the receiver. Things are different when `Post(Thread)Message()` are used since they are asynchronous instead. – Remy Lebeau Jun 03 '20 at 18:59
  • @RemyLebeau In my case, the intent was specifically **to** transfer ownership. My receiver needed to persist the object and, in that scenario, `SendMessage()` worked perfectly. I do know that `PostMessage()` is asynchronous, so I didn't see how your transfer was going to work. I'm guessing the `if` statement keeps the `unique_ptr` in scope long enough to call `release()` and send the naked pointer. There is no `unique_ptr`-to-`unique_ptr` transfer so my approach is irrelevant here. – pbyhistorian Jun 13 '20 at 05:37
  • @pbyhistorian to transfer ownership with `SendMessage()`, you can pass a pointer to the `unique_ptr` itself rather than the raw pointer it holds. Then the receiver can `std::move()` the `unique_ptr`. With `PostThreadMessage()`, the `if` has nothing to do with scope. If `PostThreadMessage()` fails, the sender needs to retain ownership. The `release()` is only if the message is posted ok and the receiver will take ownership. – Remy Lebeau Jun 13 '20 at 18:15
  • @RemyLebeau Indeed, I _am_ sending a pointer to the `std::unique_ptr` as an `LPARAM` in `SendMessage()`, and the receiver _is_ using `std::move()` to transfer ownership with no chance of memory leakage. (Passing the raw pointer as a `WPARAM` just allows confirmation for obsessive/compulsive people like me). But I think your extra explanation now shows me enough to make `PostMessage()` work for my case too. It was bad practice to use `SendMessage()` because I was effectively using it as a signal to launch a lengthy task that held onto `SendMessage()` much too long. – pbyhistorian Jun 15 '20 at 17:07
  • 1
    @pbyhistorian Note that a message handler can use `ReplyMessage()` (when `InSendMessage()` returns true) to make `SendMessage()` return back to its caller without having to fully exit the message handler. For instance, after transferring ownership of the passed pointer. This is not common practice, but it is something to be aware of. – Remy Lebeau Jun 15 '20 at 18:48
1

One way:

  1. Derive your Params from std::enable_shared_from_this<Params> or from a shared-from-this enabled base class.

  2. Pass a raw pointer in the message.

  3. At the receiving end, call p->shared_from_this() to obtain a new shared_ptr.

Note that for posting a message, as opposed to sending, you need to ensure that the object is still valid when the message is processed.

One way to do that can be use a static shared_ptr. To ensure correct protocol you can then limit the accessibility of shared_from_this and wrap it in a getter that nulls out the static shared_ptr. Disclaimer: I haven't done that so there may be issues.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • @RemyLebeau's answer uses unique_ptr. I know my original posting said shared_ptr, but in light of his answer which uses unique_ptr, safely transferring ownership across threads and windows seems to be what I am really trying to do. – rtischer8277 Sep 05 '14 at 18:52
0

If this is not a speed-critical part of your application, then a solution could be to create a singleton that would hold every shared_ptr you want to pass.

When you need to pass a shared_ptr, you would store it in this singleton and get in return an ID that refers to it. This is this ID that you would transmit with PostThreadMessage.

On the other side, when the message is received, you can retrieve the shared_ptr using this ID. Of course, the singleton must delete its reference to the shared_ptr.

Pros:

  • this technique is quite easy to implement and should be compatible with your existing code,
  • using a singleton to store the shared_ptr will ensure that they cannot be destroyed before the message is transmitted.

Cons:

  • what happens if a message is never received? (is it even possible with PostThreadMessage?) You have to make sure the shared_ptr won't stay forever in your singleton.
  • accessing a shared_ptr requires to find it in a std::map (it could become slow if you have lots of messages at the same time),
  • since this is a multithreaded code, you need to use mutexes to secure the access to the map (shared_ptr are thread-safe),
  • you will need an object that can store any kind of shared_ptr and that can be stored in a std::map. Think about having a template class that can store any shared_ptr, and that would derive from a non-template base class (I don't remember the name of this trick).
  • obviously this technique won't work if you need to send messages between different processes.
Fabien
  • 89
  • 4
  • Although this answer may work, @RemyLebeau's answer has no obvious cons. – rtischer8277 Sep 05 '14 at 18:47
  • "*what happens if a message is never received? (is it even possible with `PostThreadMessage`?)*" - yes, it is possible, if you are not careful. The receiving message loop could end before the message is received (app shutdown, crash while processing a message, etc). Or a piece of code running in the receiving thread (modal UI, etc) might pump the message queue and discard the message before it reaches the handler. etc. – Remy Lebeau Apr 11 '18 at 15:28
0

Problem resolved. I wrapped the std::make_unique to logParams.release() statements in the revised sample code above in a mutex. No leaks reported after adding those two statements.

What the statements do is keep the errorLogger thread from deleting the unique object before its ownership is released.

There is still a problem though.

When commenting out the added 2 mutex statements, there are still no leaks. They should have re-appeared. Not only did the leaks get fixed by adding the statements, but it fixed permanently the leaks in both instances on my dev machine. The second instance's code had not been altered. This problem looks like a compiler bug. Fortunately, I have a nightly backup on another machine and it still exhibits the leak behavior.

What is clear though, is, Remy Lebeau's unique_ptr approach to passing smart ptrs via windows message calls works as long as the objects are wrapped in a mutex. He warned that the objects would leak if there were lifetime issues.

rtischer8277
  • 496
  • 6
  • 27
  • You only need a mutex is you are **sharing** variables across multiple threads. If you look at my example, that is not the case, so there is no thread syncing needed. Every call to the message sender uses a local variable to hold on to ownership of a new locally-created object until the posted message takes ownership of it, and then the receiver takes ownership when processing the message. – Remy Lebeau Aug 11 '15 at 23:58
0

@ Remy Lebeau and @rtischer8277 have so far submitted two answers to my original posting. The former used std:unique_ptr and the latter used std::shared_ptr. Both answers work but have the same limitation. @Rem Lebeau puts it like this: …so that you can release() the pointer while it is in flight, and then obtain new ownership of it once the message is processed…, which breaks the spirit of the smart pointer idiom. What is needed is a solution that works like the Remote Procedure Call (RPC) which has been around since the beginning of computing (RPC, DCE, CORBA, SQL and databases, and GOOBLE/BING/YAHOO searches, not to mention all Web page links and queries to date). It is obvious that there is clear motivation for an easily programmed cross-thread RPC MFC functionality. SendMessage is RPC for same-thread calls on objects derived from CWnd (aka, “window”). The solution I am providing here is just such a non-workaround solution which I am calling “SendThreadMessage”.

Below you will see a project that demonstrates this capability. Background: PostThreadMessage has two “overloads”. One that works with windows and uses the called thread’s m_hThreadID in its signature, and the other is CWinThread::PostThreadMessage and does not contain any windows (read: CWnd derived) classes. A practical example and clear explanation of the former overload is shown in CodeProject’s PostThreadMessage Demystified by ThatsAlok.

A second workaround for the illusory “SendThreadMessage” functionality is to simply send a message to the other thread and when that procedure is finished, send one back. Gorlash: … I used a two-message system for this, where the main thread sent a message to the task-handler thread, and when the task-handler was done it sent another message back to the caller (both of these being user-defined messages)…, and OReubens: … a PostThreadMessage, and a Post back (either to window or as thread message) is the safer/better/more controllable way out. Note: SendMessageCallback is not a solution because the callback is not synchronous for cross threads.

The two “overloads” have caused much confusion and there have been no definitive code examples of the CWinThread::PostThreadMessage overload. I have seen no solution that is not tedious compared to a clean synchronous RPC call.

So here is my solution: don’t ever release ownership of the unique_ptr object until it naturally goes out of scope. Use <future> to create a promise that is automatically passed over to the other thread via the LPARAM parameter. In the receiving thread, execute the code (ie, the remote procedure) using whatever the data is needed that was sent in the members of the passed-in object. When the remote thread is finished processing and setting its output data in that same object, use promise’s set_value to signal the waiting future object in the first thread that the call has completed and the results are stored in the unique_ptr object. This effectively simulates SendMessage synchronous RPC functionality using the simple modern C++ future construct and without having to work around or defeat C++’s normal ownership semantics.

Here is a link to the commented VS2015 project that demonstrates how this works: SendThreadMessage.sln

SendThreadMessage.sln is a VS console project with ATL and MFC checked. The console output shows unique_ptrs being created and going out of scope called pingThread and pongThread. The mainfrm thread is blocked while the ping and pong threads alternate sending a message to each other (with one second intervals). Each PostThreadMessage with futures demonstrates the “SendThreadMessage” synchronous RPC functionality using plain MFC and modern C++.

rtischer8277
  • 496
  • 6
  • 27
  • You are synchronizing threads. It may cause dead-lock. – guan boshen Jul 15 '20 at 07:27
  • @rtischer8277 Please do not host files on another server. Questions are expected to be self-contained, and external links break over time. Also, the link requires a OneDrive login, and not everyone has such a login. Please reduce the example code to a bare minimum and put it directly in the answer. – Remy Lebeau Jul 16 '20 at 17:11
-1

In Edit2: I identified a con with @Remy Lebeau’s answer: the MyParams object needs to be std::unique_ptr-created and then transferred and then re-owned on the destination thread. Also, my original motivation of wanting to use std::shared_ptrs across the PostThreadMessage remained in my application. In my OP code I did new the MyParams object, which is what @Remy Lebeau’s answer solved. The original code should have been:

struct MyParams { MyParams() {} int nn { 33 }; }; // doesn’t go out of scope w/r/t receiving method
...
std::shared_ptr< MyParams > myParams = std::make_shared< MyParams >();
...
PostThreadMessage( MSG_ID, 0, reinterpret_cast< LPARAM >( myParams.get() );

And here is the receiving code:

void ReceivingMethod::OnMsgId( WPARAM wParam, LPARAM lParam )
{
  std::shared_ptr< MyParams > myParamsX( reinterpret_cast< MyParams* >( lParam ) ); // error
  myParamsX->nn++; // ok, nn: 35
} // throws exception when going out of scope

Since weak_ptrs do not affect ownership, then I should be able to transfer its pointer across threads using PostThreadMessage’s lParam, I reasoned. There were several weak_ptr constructors that were available. Use the std::weak_ptr< A >&(ref) constructor. Here is the receiving code:

void ReceivingMethod::OnMsgId( WPARAM wParam, LPARAM lParam )
{
  std::weak_ptr< MyParams > myParamsX( reinterpret_cast< std::weak_ptr< MyParams >& >( lParam ) ); // ok: nn:34 but strong and weak refs reporting are random numbers
  //std::weak_ptr< MyParams > myParamsX( reinterpret_cast< std::weak_ptr< MyParams >& >( lParam ) ); // ok: also works
  myParamsX.lock()->nn++; // ok: nn: 35
  int nnX = myParamsX.lock()->nn; // ok: nnX: 35
} // ok: weak_ptr releases resource properly, but intellisense strong and weak ref counts are still bad

I tested incrementing myParam’s and myParamX’s nn members and both the myParamsWptr.lock()->nn++ and myParams->nn++ could increment the smart object. This time releasing the myParams object didn’t fail. I assume because myParamsWptr locks the object, no thread access problems will occur.

As expected there were no leaks in the program.

Why continue using PostThreadMessage at all? To communicate across windows user-interface (message pump) threads you use PostThreadMessage. I am still looking for Modern C++ techniques that are as good as that. std::promise, std::future and std::get_future work fine with worker threads which don’t have message pumps. In the meantime, I use PostThreadMessage in my MFC C++ apps.

rtischer8277
  • 496
  • 6
  • 27
  • You are sending a raw `MyParams*` pointer in the message's `LPARAM`. The receiver cannot cast the `LPARAM` to anything other than a raw `MyParams*` pointer. Certainly NOT to a `weak_ptr&`. As I explained earlier, keeping a smart pointer to the `MyParam` object while the message is in flight is dangerous, because if the smart pointer goes out of scope before the message is received, you risk the object being destroyed prematurely. That would account for the exception you see in the receiver. And you can't manually modify a `shared_ptr`'s reference count to prevent premature destruction, either. – Remy Lebeau Apr 11 '18 at 15:23
  • Instead of raw ptr, create unique_ptr and release it, perhaps atomically. Re-create the unique_ptr in the receiving handler. No chance of leak then unless the PostThreadMessage itself fails, which is highly improbable. – rtischer8277 Jul 16 '20 at 13:16
  • that is exactly what my answer shows. And there are other conditions besides `PostThreadMessage()` failing that could cause a leak, if you are not careful, those are explained in other comments (receiving message loop not running, a modal message loop discarding the message, etc) – Remy Lebeau Jul 16 '20 at 17:12