How to use shared_ptr using PostThreadMessage?

I would like to update 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 are unsigned int and long respectively. I am currently creating a class object, a new object, making a call passing a pointer to the object, using the object on the receiving side, and then deleting it.

Since shared_ptr works so well in the rest of my code, I would like to at least study the reasons why I cannot 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; } 

for C ++ an 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 } 

Change 1:

@Remy Lebeau: Here is an example of code that has been modified to use the unique_ptr transfer method, however there are leaks in the transmission of an object in my code.

 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 ) ); } 

Note that when I comment on the passage of unique_ptr, the leaks disappear. How is my code different from your code that uses this approach and works?

Edit2:

Regarding @Remy Lebeau ’s answer, showing how std::unique_ptr can be used instead of std::shared_ptr , I said in a comment below that β€œ... there are no additional objects to implement. There are no obvious minuses”. Well, that’s not entirely true. A MyParams object must be created for each type of message. Some applications may have only a few types, but some may have 100 or more. Each time I want to execute a function on the other hand, I have to create a new structure that has a constructor that takes all the arguments of the target call. Very tiring to implement and difficult to maintain if there are many.

I think that this phase of building the structure could be eliminated by simply passing arguments.

There are apparently new C ++ 1x constructs that can help with this. One of them is probably std::forward_as_tuple , which "Creates a tuple of references to arguments in arguments suitable for transfer as a function argument".

For my application, I solved the problem by templatizing MyParams , but for those who want to avoid adding a lot of structures, they might want to take a look at using tuples, etc.

Edit 3: The answers to the answer number @RemyLebeau 1 and @ rtischer8277s are correct. Unfortunately, StackOverflow does not recognize several correct answers. This StackOverflow limitation reflects the erroneous psycholinguistic assumption that the linguistic context is universal for the same language group, and this is not so. (see "Introduction to Integral Linguistics" by Roger Harris for the myth of the language of fixed code, p. 34). In response to my original post, @RemyLebeau answered a question based on the context described by the published code that shows new ed MyParams (see "Editing 2: for a more detailed explanation"). Much later, in answer 5 (rtischer8277), I myself answered the question, based on the original wording of the question, which asked if std::shared_ptr can be used on threads using PostThreadMessage . As a reasonable consequence, I returned the correct answer to @RemyLebeau, this is the first correct answer.

EDIT4: I added a third legitimate response to this post. See the 6th answer starting with @Remy Lebeau and @ rtischer8277 so far have provided two answers to my original post .... The effect of this solution is to turn the end-to-end stream into a conceptually simple RPC (remote procedure call) . Although this answer shows how to use the future to control ownership and synchronization, it does not show how to create a message object with an arbitrary number of parameters that can be safely used on both sides of the PostThreadMessage call. This functionality is addressed in StackOverflows Response to the release of passing a parameter package over an obsolete function signature using forward_as_tuple .

+5
source share
6 answers

Since the message parameters must survive the call area, in this example it makes no sense to use shared_ptr , since the original shared_ptr is likely to leave the area before the message is processed. Instead, I suggest using unique_ptr so that you can release() pointer while in flight, and then gain new ownership after processing the message:

 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 } 
+5
source

One of the methods:

  • Derive Params from std::enable_shared_from_this<Params> or from a base class with access enabled.

  • Pass the raw pointer in the message.

  • At the end of the call, call p->shared_from_this() to get the new shared_ptr .

Please note that to send a message, unlike sending, you need to make sure that the object remains valid when processing the message.

As one of the methods, you can use static shared_ptr . To provide the correct protocol, you can limit the availability of shared_from_this and wrap it in a getter that disables static shared_ptr . Disclaimer: I did not do this, so problems may occur.

+1
source

If this is not a critical part of your application, then the solution could be to create a singleton that will contain all the shared_ptr that you want to pass.

When you need to pass shared_ptr, you save it in this singlet and get an identifier that refers to it in response. This is the identifier that you pass using PostThreadMessage .

On the other hand, when a message is received, you can get shared_ptr using this identifier. Of course, a singleton should remove its link to shared_ptr.

Pros

  • This method is pretty easy to implement and should be compatible with your existing code,
  • using one syntax to store shared_ptr will ensure that they cannot be destroyed before the message is sent.

against

  • What happens if a message is not received? (is this possible with PostThreadMessage ?). You need to make sure that shared_ptr will not remain forever in your singlet.
  • Accessing shared_ptr requires finding it on std :: map (it can become slow if you have many messages at the same time),
  • since this is multi-threaded code, you need to use mutexes to provide access to the map ( shared_ptr is thread-safe ),
  • you will need an object that can store any type of shared_ptr and which can be stored on std :: map. Think about whether there is a template class that any shared_ptr can store, and this will come from a base class without a template (I don't remember the name of this trick).
  • obviously this method will not work if you need to send messages between different processes.
0
source

The problem is resolved. I wrapped std :: make_unique in logParams.release () in the revised code example above in the mutex. After the addition of these two statements, no leaks were reported.

What the claims do is that the errorLogger thread deletes a unique object before its ownership is released.

There is still a problem.

There are still no leaks when commenting on added 2-mutex statements. They should have appeared again. Not only were leaks fixed by adding statements, but they constantly fixed leaks in both instances on my dev machine. The code for the second instance has not been changed. This problem looks like a compiler error. Fortunately, I have a nightly backup on another machine, and it still demonstrates leak behavior.

It is clear, however, that the Remy Lebeau unique_ptr approach for passing smart ptrs through Windows message calls works as long as the objects are wrapped in a mutex. He warned that objects would flow if there were problems with life.

0
source

In Edit2: I defined con with @Remy Lebeaus answer: MyParams object must be std :: unique_ptr-created and then passed and then reused in the destination stream. Also, my initial motivation to want to use std::shared_ptr through PostThreadMessage remained in my application. In my OP code, I made a new MyParams object, which is @Remy Lebeau s solution. The source 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 receipt 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, I should be able to pass its pointer to threads using PostThreadMessage s lParam, I reasoned. There were several weak_ptr constructors that were available. Use the constructor std::weak_ptr< A >& (ref). Here is the receipt 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 the increment myParam s and myParamX s nn , and both myParamsWptr.lock()->nn++ and myParams->nn++ could increase the smart object. This time, releasing the myParam object myParam not work. I assume that since myParamsWptr blocking the object, there will be no problems accessing the stream.

As expected, there were no leaks in the program.

Why use PostThreadMessage at all? For communication between user interface threads (message flow), you use PostThreadMessage . I'm still looking for modern C ++ methods that are so good. std::promise , std::future and std::get_future work great with workflows that don't have messages. In the meantime, I am using PostThreadMessage in my MFC C ++ applications.

0
source

@ Remy Lebo and @ rtischer8277 have so far provided two responses to my original post. The former used std:unique_ptr , and the latter used std::shared_ptr . Both answers work, but have the same limitations. @Rem Lebeau puts it this way: ... so that you can free () the pointer while it is in flight, and then gain new ownership after processing the message ... which violates the spirit of a smart pointing idiom. You need a solution that works like a Remote Procedure Call (RPC), which exists from the very beginning of calculations (RPC, DCE, CORBA, SQL and databases, as well as searching GOOBLE / BING / YAHOO, not to mention all the link web pages and requests for today). Obviously, there is a clear motivation for the easily programmed MFC gateway RPC functions. SendMessage is an RPC for single-threaded calls to objects received from CWnd (aka, "window"). The solution I provide here is just a solution that I do not call "SendThreadMessage".

Below you will see a project demonstrating this feature. Background: PostThreadMessage has two overloads. The one that works with windows and uses the called m_hThreadID threads in its signature, and the other, CWinThread::PostThreadMessage and does not contain any windows (readable: CWnd derivatives) classes. A practical example and a clear explanation of the previous overload is shown in CodeProjects PostThreadMessage Demystified ThatsAlok.

The second way to bypass the SendThreadMessage illusionary function is to simply send a message to another thread, and when this procedure is complete, send it back. Throat: ... For this, I used a two-message system, where the main thread sent a message to the task handler thread, and when the task handler was executed, it sent another message back to the caller (both of them were user defined messages) ... and OReubens: ... PostThreadMessage and Post back (either in a window or as a stream) is a safer / better / more manageable output. Note: SendMessageCallback not a solution because the callback is not synchronous for cross-threads.

The two "overloads" caused a lot of confusion and there were no final examples of the overload code CWinThread::PostThreadMessage . I have not seen a solution that is not tedious compared to a pure synchronous RPC call.

So here is my solution: never unique_ptr ownership of a unique_ptr object until it naturally goes beyond. Use <future> to create a promise that is automatically passed to another thread through the LPARAM parameter. In the receiving stream, execute the code (i.e., the Remote Procedure), using what is required by the data that was sent in the elements of the transmitted object. When the remote thread completes processing and sets its output to the same object, use promise s set_value to signal a pending future object in the first thread that completed the call, and the results are stored in a unique_ptr object. This effectively mimics the synchronous functionality of RPC SendMessage , using the simple modern design of C ++ future and without the need to work or endure the semantics of normal C ++ ownership.

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

SendThreadMessage.sln is a VS console project with ATL and MFC validation. Console output shows that unique_ptrs is created and unique_ptrs from an area called pingThread and pongThread . The mainfrm blocked when the ping and pong threads alternate, sending a message to each other (with an interval of one second). Each PostThreadMessage with future demonstrates the synchronous RPC function SendThreadMessage using simple MFC and modern C ++.

0
source

Source: https://habr.com/ru/post/1201826/


All Articles