Re: [boost] [thread] synchronized_value: value and move semantics
On Wed, Feb 20, 2013 at 10:02 PM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
Hi,
boost::synchronized_value (not released yet [1][2][3]) is based on [4]. See below part of this paper adapted to the Boost.Thread interface.
Currently boost::synchronized_value is in addition Copyable and Swappable. I was wondering if it is worth adding value and move semantics to synchronized_value. Making it EqualityComparable, LessThanComparable and Movable if the underlying type satisfy these requirements will allow to store them on a Boost.Container/C++11 container.
Do you see something completely wrong with this addition? Has some of you had a need for this? Could you share the context?
Sorry, I missed this discussion somehow. I've taken a quick look at the interface and have a few questions: 1. Why are there strict_lock_ptr and unique_lock_ptr? What are the differences and why we can't have one such ptr (presumably, unique_lock_ptr)? 2. I find value() and get() a bit confusing, since it is not apparent what is the difference between them. Maybe value() could be renamed to get_ref() or unsafe_get()? 3. Am I correct that having strict_lock_ptr/unique_lock_ptr acquired by calling synchronize() will not deadlock with operator-> when a non-recursive mutex is used?
Andrey Semashev-2 wrote
On Wed, Feb 20, 2013 at 10:02 PM, Vicente J. Botet Escriba <
vicente.botet@
wrote:
Hi,
boost::synchronized_value (not released yet [1][2][3]) is based on [4]. See below part of this paper adapted to the Boost.Thread interface.
Currently boost::synchronized_value is in addition Copyable and Swappable. I was wondering if it is worth adding value and move semantics to synchronized_value. Making it EqualityComparable, LessThanComparable and Movable if the underlying type satisfy these requirements will allow to store them on a Boost.Container/C++11 container.
Do you see something completely wrong with this addition? Has some of you had a need for this? Could you share the context?
Sorry, I missed this discussion somehow. I've taken a quick look at the interface and have a few questions:
1. Why are there strict_lock_ptr and unique_lock_ptr?
These a locking pointers that lock at construction and unlock at destruction. Both classes have pointer semantics.
What are the differences
strict_lock_ptr cannot be unlocked (something like lock_guard) and unique_lock_ptr is a model of Lockable so it provides the lock/unlock functions as unique_lock.
and why we can't have one such ptr (presumably, unique_lock_ptr)?
Sorry I don't understand.
2. I find value() and get() a bit confusing, since it is not apparent what is the difference between them. Maybe value() could be renamed to get_ref() or unsafe_get()?
You are right the names are not clear. get() is a explicit conversion. value() return a const reference.
3. Am I correct that having strict_lock_ptr/unique_lock_ptr acquired by calling synchronize() will not deadlock with operator-> when a non-recursive mutex is used?
To which operator-> are you referring to? the one from strict_lock_ptr/unique_lock_ptr? synchronize() is used to lock at block scope. The user must use the obtained locking pointer to access to the functions of the synchronized value using the operator->. I'm not sure to understand what could be the issue. best, Vicente -- View this message in context: http://boost.2283326.n4.nabble.com/thread-synchronized-value-value-and-move-... Sent from the Boost - Dev mailing list archive at Nabble.com.
On Wednesday 26 June 2013 08:55:33 Vicente Botet wrote:
Andrey Semashev-2 wrote
1. Why are there strict_lock_ptr and unique_lock_ptr?
These a locking pointers that lock at construction and unlock at destruction. Both classes have pointer semantics.
What are the differences
strict_lock_ptr cannot be unlocked (something like lock_guard) and unique_lock_ptr is a model of Lockable so it provides the lock/unlock functions as unique_lock.
and why we can't have one such ptr (presumably, unique_lock_ptr)?
Sorry I don't understand.
What I mean is that there is no apparent advantage of using strict_lock_ptr instead of unique_lock_ptr. strict_lock_ptr is a bit more limited than unique_lock_ptr but it doesn't provide anything in return. Yet it complicates synchronized_value interface and adds confusion (when should I call synchronize() and when unique_synchronize()?). So I don't see the point in having strict_lock_ptr. Note that although there exist both lock_guard and unique_lock, the former is more efficient when you don't need movability and optional locking of the latter. This is not the case with strict_lock_ptr and unique_lock_ptr since both use unique_lock internally.
3. Am I correct that having strict_lock_ptr/unique_lock_ptr acquired by calling synchronize() will not deadlock with operator-> when a non-recursive mutex is used?
To which operator-> are you referring to? the one from strict_lock_ptr/unique_lock_ptr? synchronize() is used to lock at block scope. The user must use the obtained locking pointer to access to the functions of the synchronized value using the operator->. I'm not sure to understand what could be the issue.
I was referring to synchronized_value<>::operator->. What I mean is this: synchronized_value< foo, mutex > sync_foo; auto locked = sync_foo.synchronize(); sync_foo->bar(); // deadlock?? If I use a non-recursive mutex, like the above, and store the strict_lock_ptr or unique_lock_ptr in locked, will I be able to call bar()? The operator-> is supposed to create a new strict_lock_ptr, which is supposed to attempt to lock the mutex again, isn't it? Perhaps, it would be better to restrict the number of lock_ptrs for a single synchronized_value that can exist concurrently to just one? Then the above code would be invalid and should be written as follows: synchronized_value< foo, mutex > sync_foo; auto locked = sync_foo.synchronize(); // sync_foo->bar(); // <-- assertion failure or exception locked->bar(); // ok
Le 26/06/13 19:55, Andrey Semashev a écrit :
On Wednesday 26 June 2013 08:55:33 Vicente Botet wrote:
Andrey Semashev-2 wrote
1. Why are there strict_lock_ptr and unique_lock_ptr? These a locking pointers that lock at construction and unlock at destruction. Both classes have pointer semantics.
What are the differences strict_lock_ptr cannot be unlocked (something like lock_guard) and unique_lock_ptr is a model of Lockable so it provides the lock/unlock functions as unique_lock.
and why we can't have one such ptr (presumably, unique_lock_ptr)? Sorry I don't understand. What I mean is that there is no apparent advantage of using strict_lock_ptr instead of unique_lock_ptr. strict_lock_ptr is a bit more limited than unique_lock_ptr but it doesn't provide anything in return. Yet it complicates synchronized_value interface and adds confusion (when should I call synchronize() and when unique_synchronize()?). So I don't see the point in having strict_lock_ptr.
Note that although there exist both lock_guard and unique_lock, the former is more efficient when you don't need movability and optional locking of the latter. This is not the case with strict_lock_ptr and unique_lock_ptr since both use unique_lock internally. The current implementation uses unique_lock, but I plan to use a specific implementation that is more efficient.
3. Am I correct that having strict_lock_ptr/unique_lock_ptr acquired by calling synchronize() will not deadlock with operator-> when a non-recursive mutex is used? To which operator-> are you referring to? the one from strict_lock_ptr/unique_lock_ptr? synchronize() is used to lock at block scope. The user must use the obtained locking pointer to access to the functions of the synchronized value using the operator->. I'm not sure to understand what could be the issue. I was referring to synchronized_value<>::operator->. What I mean is this:
synchronized_value< foo, mutex > sync_foo; auto locked = sync_foo.synchronize(); sync_foo->bar(); // deadlock??
If I use a non-recursive mutex, like the above, and store the strict_lock_ptr or unique_lock_ptr in locked, will I be able to call bar()? The operator-> is supposed to create a new strict_lock_ptr, which is supposed to attempt to lock the mutex again, isn't it? Right. Perhaps, it would be better to restrict the number of lock_ptrs for a single synchronized_value that can exist concurrently to just one? Then the above code would be invalid and should be written as follows:
synchronized_value< foo, mutex > sync_foo; auto locked = sync_foo.synchronize(); // sync_foo->bar(); // <-- assertion failure or exception locked->bar(); // ok
You could get the same behavior with a mutex that checks if the current thread is locking the mutex alteady. Boost.Thread contains a boost::testable_mutex (that I have not documented yet) that maybe can be updated to assert in this case. So the following would behave as you are requesting. synchronized_value< foo, testable_mutex > sync_foo; auto locked = sync_foo.synchronize(); // sync_foo->bar(); // <-- assertion failure or exception locked->bar(); // ok Best, Vicente
On Wednesday 26 June 2013 15:39:27 you wrote:
On Wed, Feb 20, 2013 at 10:02 PM, Vicente J. Botet Escriba <
vicente.botet@wanadoo.fr> wrote:
Hi,
boost::synchronized_value (not released yet [1][2][3]) is based on [4]. See below part of this paper adapted to the Boost.Thread interface.
Currently boost::synchronized_value is in addition Copyable and Swappable. I was wondering if it is worth adding value and move semantics to synchronized_value. Making it EqualityComparable, LessThanComparable and Movable if the underlying type satisfy these requirements will allow to store them on a Boost.Container/C++11 container.
Do you see something completely wrong with this addition? Has some of you had a need for this? Could you share the context?
Sorry, I missed this discussion somehow. I've taken a quick look at the interface and have a few questions:
1. Why are there strict_lock_ptr and unique_lock_ptr? What are the differences and why we can't have one such ptr (presumably, unique_lock_ptr)? 2. I find value() and get() a bit confusing, since it is not apparent what is the difference between them. Maybe value() could be renamed to get_ref() or unsafe_get()? 3. Am I correct that having strict_lock_ptr/unique_lock_ptr acquired by calling synchronize() will not deadlock with operator-> when a non-recursive mutex is used?
Also, if it's not too late yet: 4. Could synchronized_value be renamed to just synchronized? Besides being shorter, this naming seems to be aligned with optional and reads more naturally. Consider: optional< int > oi; synchronized< queue< int > > sqi; Just a thought.
On Wednesday 26 June 2013 23:56:27 you wrote:
On Wednesday 26 June 2013 15:39:27 you wrote:
On Wed, Feb 20, 2013 at 10:02 PM, Vicente J. Botet Escriba <
vicente.botet@wanadoo.fr> wrote:
Hi,
boost::synchronized_value (not released yet [1][2][3]) is based on [4]. See below part of this paper adapted to the Boost.Thread interface.
Currently boost::synchronized_value is in addition Copyable and Swappable. I was wondering if it is worth adding value and move semantics to synchronized_value. Making it EqualityComparable, LessThanComparable and Movable if the underlying type satisfy these requirements will allow to store them on a Boost.Container/C++11 container.
Do you see something completely wrong with this addition? Has some of you had a need for this? Could you share the context?
Sorry, I missed this discussion somehow. I've taken a quick look at the interface and have a few questions:
1. Why are there strict_lock_ptr and unique_lock_ptr? What are the differences and why we can't have one such ptr (presumably, unique_lock_ptr)? 2. I find value() and get() a bit confusing, since it is not apparent what is the difference between them. Maybe value() could be renamed to get_ref() or unsafe_get()? 3. Am I correct that having strict_lock_ptr/unique_lock_ptr acquired by calling synchronize() will not deadlock with operator-> when a non-recursive mutex is used?
Also, if it's not too late yet:
4. Could synchronized_value be renamed to just synchronized? Besides being shorter, this naming seems to be aligned with optional and reads more naturally. Consider:
optional< int > oi; synchronized< queue< int > > sqi;
...and also atomic: atomic< int > ai;
On 06/26/2013 09:56 PM, Andrey Semashev wrote:
4. Could synchronized_value be renamed to just synchronized? Besides being shorter, this naming seems to be aligned with optional and reads more naturally. Consider:
optional< int > oi; synchronized< queue< int > > sqi;
And the synchronize() function could be renamed to hold() to make the names more discernible.
Le 27/06/13 09:49, Bjorn Reese a écrit :
On 06/26/2013 09:56 PM, Andrey Semashev wrote:
4. Could synchronized_value be renamed to just synchronized? Besides being shorter, this naming seems to be aligned with optional and reads more naturally. Consider:
optional< int > oi; synchronized< queue< int > > sqi;
And the synchronize() function could be renamed to hold() to make the names more discernible.
I could change it if there is an agreement of the Boost community. Vicente
On Thu, Jun 27, 2013 at 8:48 PM, Vicente J. Botet Escriba < vicente.botet@wanadoo.fr> wrote:
I could change it if there is an agreement of the Boost community.
(sorry for the delay, I missed this one) I agree that it should be changed, in synchronized_value the value part is not necessary, and very verbose. I have another question: Why is there no move operator? I see a move constructor but no move operator, but I suppose it could be implemented if T is movable? Or is it not possible?
On Tuesday 24 September 2013 18:12:31 Klaim - Joël Lamotte wrote:
On Thu, Jun 27, 2013 at 8:48 PM, Vicente J. Botet Escriba <
vicente.botet@wanadoo.fr> wrote:
I could change it if there is an agreement of the Boost community.
(sorry for the delay, I missed this one)
I agree that it should be changed, in synchronized_value the value part is not necessary, and very verbose.
Do we plan to extract it from Boost.Thread to Boost.Sync? If so, the rename could be done in the process.
I have another question: Why is there no move operator? I see a move constructor but no move operator, but I suppose it could be implemented if T is movable? Or is it not possible?
synchronized_value has an internal mutex, which is not movable. But I think you should be able to move the contained value using the synchronize() functions.
On Tue, Sep 24, 2013 at 7:19 PM, Andrey Semashev
synchronized_value has an internal mutex, which is not movable. But I think you should be able to move the contained value using the synchronize() functions.
Yes that's what I was thinking about, the copy operator already copy the value but not the mutex (from the documentation).
Le 26/06/13 21:56, Andrey Semashev a écrit :
On Wednesday 26 June 2013 15:39:27 you wrote: Also, if it's not too late yet:
4. Could synchronized_value be renamed to just synchronized? Besides being shorter, this naming seems to be aligned with optional and reads more naturally. Consider:
optional< int > oi; synchronized< queue< int > > sqi;
I could change it if there is an agreement of the Boost community. Vicente
participants (5)
-
Andrey Semashev
-
Bjorn Reese
-
Klaim - Joël Lamotte
-
Vicente Botet
-
Vicente J. Botet Escriba