Why does boost thread copy a functor?
I had code similar to the following... Functor object: struct Task { operator()() { // thread main } smartfd p; }; Note that smartfd is a wrapper around a file descriptor. In its dtor it closes the fd. Then, elsewhere I set up an instance of Task and spawn it with boost::thread... Task t; t.p = // open file descriptor boost::thread thr(t); The spawned thread gets an instance of Task, but it's not the same instance as from the main. What is the rationale? -- Chris Cleeland
On Sun, Jan 15, 2012 at 06:25:52PM -0600, Chris Cleeland wrote:
The spawned thread gets an instance of Task, but it's not the same instance as from the main.
What is the rationale?
Functors in general should be expected to be copied, so it might not be an explicit decision. It is, however, a good decision. The thread of execution generally outlives the scope where the thread is started. If the thread used a reference to the functor, then pretty much _all_ cases would force the user to store away function objects elsewhere and destroy them only when the thread has terminated completely. It would also prevent the use of temporaries as thread callables. If you want to get reference semantics, use a reference_wrapper. boost::thread t(boost::ref(task)); will have the reference semantics you crave. -- Lars Viklund | zao@acc.umu.se
Lars,
Thanks for the explanation and suggestion! It makes sense.
In my case, the main thread is the long-lived thread, while the boost
thread is not. Moreover, there is state in the Task that is intended to be
shared between both threads.
I wonder if there's a way to make the copying more explicit? Had it not
been for the unexpected behavior of the file descriptor being closed, the
copying notion would have been even more subtle--and my misunderstanding
even harder to find.
On Sunday, January 15, 2012, Lars Viklund
On Sun, Jan 15, 2012 at 06:25:52PM -0600, Chris Cleeland wrote:
The spawned thread gets an instance of Task, but it's not the same instance as from the main.
What is the rationale?
Functors in general should be expected to be copied, so it might not be an explicit decision.
It is, however, a good decision. The thread of execution generally outlives the scope where the thread is started. If the thread used a reference to the functor, then pretty much _all_ cases would force the user to store away function objects elsewhere and destroy them only when the thread has terminated completely.
It would also prevent the use of temporaries as thread callables.
If you want to get reference semantics, use a reference_wrapper.
boost::thread t(boost::ref(task)); will have the reference semantics you crave.
-- Lars Viklund | zao@acc.umu.se _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
-- Chris Cleeland
On Sun, Jan 15, 2012 at 07:36:10PM -0600, Chris Cleeland wrote:
Lars,
Thanks for the explanation and suggestion! It makes sense.
In my case, the main thread is the long-lived thread, while the boost thread is not. Moreover, there is state in the Task that is intended to be shared between both threads.
I wonder if there's a way to make the copying more explicit? Had it not been for the unexpected behavior of the file descriptor being closed, the copying notion would have been even more subtle--and my misunderstanding even harder to find.
Please do not top post, the list guidelines encourage bottom posting. Your wrapper does not seem to follow the rule of three: If a class defines one of the following it should probably explicitly define all three: * destructor * copy constructor * assignment operator Your wrapper type does not seem to have provided adequate implementations for the cctor and op=, or has not prevented the synthesised ones from existing. If you intend for a type to be noncopyable, either inherit from boost::noncopyable or make the copy constructor and assignment operator private. That way, if someone accidentally copies your type, you will get a nice compile-time error. Another alternative is that you can hold your state in the Task by reference or smart pointer, such that if it's copied, the guts still refer to the correct state. -- Lars Viklund | zao@acc.umu.se
Lars,
On Sun, Jan 15, 2012 at 8:01 PM, Lars Viklund
Please do not top post, the list guidelines encourage bottom posting.
Apologies; I was on my phone which makes editing difficult.
Your wrapper does not seem to follow the rule of three: If a class defines one of the following it should probably explicitly define all three: * destructor * copy constructor * assignment operator
Your wrapper type does not seem to have provided adequate implementations for the cctor and op=, or has not prevented the synthesised ones from existing.
I'll review the wrapper in light of your comments. Thanks!
Another alternative is that you can hold your state in the Task by reference or smart pointer, such that if it's copied, the guts still refer to the correct state.
Good suggestion, but that doesn't solve the situation where I wanted the actual object to be shared. While I understand the rationale now (thanks to your excellent explanation), I will also point out that I am not alone in my misunderstanding of the interface. I discussed the issue with a few colleagues and asked what they would expect to happen, and they all had similar expectations to mine (that the functor was not copied). The commonality of misunderstanding is what prompted my suggestion that the copying of the argument be more explicit. I truly appreciate the help and apologize for the top-posting. -- Chris Cleeland
"Chris Cleeland" wrote:
While I understand the rationale now (thanks to your excellent explanation), I will also point out that I am not alone in my misunderstanding of the interface. I discussed the issue with a few colleagues and asked what they would expect to happen, and they all had similar expectations to mine (that the functor was not copied). The commonality of misunderstanding is what prompted my suggestion that the copying of the argument be more explicit.
The documentation is crystal clear, though: "Effects: func is copied into storage managed internally by the thread library" (http://www.boost.org/doc/libs/1_48_0/doc/html/thread/thread_management.html#...)
Niklas,
On Thu, Jan 19, 2012 at 5:08 PM, Niklas Angare
"Chris Cleeland" wrote:
I discussed the issue with a few colleagues and asked what they would expect to happen, and they all had similar expectations to mine (that the functor was not copied).
The documentation is crystal clear, though: "Effects: func is copied into storage managed internally by the thread library"
(http://www.boost.org/doc/libs/1_48_0/doc/html/thread/thread_management.html#...)
Indeed, you are correct. I read many parts of the documentation, but not that. I even looked at examples and the tutorial. -- Chris Cleeland
participants (3)
-
Chris Cleeland
-
Lars Viklund
-
Niklas Angare