[thread] Interest in a locking pointer and lockable concept

Hi, is there any interest in something like a locking pointer and a type that I call "lockable" that pairs any type with a mutex? Inspired by Andrei Alexandrescu's article "volatile - Multithreaded Programmer's Best Friend" on ddj I implemented a concept similar to a locking pointer called "lock_acquirer" that collects acquisition of a mutex and a volatile cast of the locked type. It is even more threadsafe to use than a locking pointer. Additionally I made a class "lockable" that pairs a (volatile) type with a mutex type, wrapping both in a distinct type, which has first the advantage that the type to protect and the mutex are glued together, and second that it encapsulates volatile qualifier correctly. Also, the locked type in a lockable can, if wanted, by a restricted interface only be accessed by a lock_acquirer thus forcing the programmer to a very threadsafe programming style. I can post my implementation along with a simple example to the boost vault. Klaus

On Sunday 16 March 2008 10:55, klaus triendl wrote:
Hi,
is there any interest in something like a locking pointer and a type that I call "lockable" that pairs any type with a mutex?
I'd like to see something along these lines in boost. I'm not sure how much interest there is yet though. I've also got some boost licensed monitor code in libpoet I've posted about in the past, but didn't get a lot of response to. Nonetheless, I may try to get it into the review queue at some point in the future if nothing else gets in, to try and "throw down the glove", so to speak.
I can post my implementation along with a simple example to the boost vault.
Please do. -- Frank

klaus triendl wrote:
is there any interest in something like a locking pointer and a type that I call "lockable" that pairs any type with a mutex?
Inspired by Andrei Alexandrescu's article "volatile - Multithreaded Programmer's Best Friend" on ddj I implemented a concept similar to a locking pointer called "lock_acquirer" that collects acquisition of a mutex and a volatile cast of the locked type. It is even more threadsafe to use than a locking pointer.
Additionally I made a class "lockable" that pairs a (volatile) type with a mutex type, wrapping both in a distinct type, which has first the advantage that the type to protect and the mutex are glued together, and second that it encapsulates volatile qualifier correctly.
I've mentioned something like this here a couple of times; did you see my Proto review? http://thread.gmane.org/gmane.comp.lib.boost.devel/171620 My feeling is that the basic Lockable<T> that pairs a mutex with the data that it locks is by itself too trivial for Boostification. Where it gets interesting is when you provide an interface to do automatic locking and unlocking, i.e. your locking pointer; I'd be interested to see some example usage for your version. In my case I found that the locking pointer (or locking reference) was actually more verbose than explicitly locking the mutex and accessing the data, so the only benefit is that the locking can be mandatory. But normally the only "enforcement" that I need is a comment: /* lock this before writing that */. Perhaps you have something more concise than I managed. Something else to consider is the interaction with atomic operations and lock-free data structures. It would be great if Locked<int> and Atomic<int>, or Locked<list<int>> and lockfree_list<int>, had very similar interfaces. I have also previously mentioned the benefit of placing the mutex and the data nearby in memory for performance reasons; this is an argument in favour of Lockable<T>. I notice that Anthony Williams used the term "lockable concept" in the Boost.Threads release notes that he posted here recently. Anthony, would it be possible to say e.g. "mutex concept" in your context so that we can keep the term "lockable" available for something like this? (Or maybe they are actually the same thing. Hmmm.) Phil.

Phil Endecott <spam_from_boost_dev <at> chezphil.org> writes:
I notice that Anthony Williams used the term "lockable concept" in the Boost.Threads release notes that he posted here recently. Anthony, would it be possible to say e.g. "mutex concept" in your context so that we can keep the term "lockable" available for something like this? (Or maybe they are actually the same thing. Hmmm.)
I've used the concept "lockable" to describe something which can be locked. I felt this was a better term than "mutex", since some of the things that can be locked are not mutexes. For example unique_lock<some_mutex> also models "Lockable", so you can have a unique_lock<unique_lock<some_mutex> >. Hopefully the new boost.thread docs will give you a better idea. I would not describe a pair of data+mutex as "lockable" unless you could indeed lock it and unlock it (with lock()/unlock() member functions). However, even if you could, the term "lockable" wouldn't (in my view) describe what the combined data structure was. Would "protected" better describe the intent? I know it's a keyword, but surely the idea is to protect the data from concurrent access.
Something else to consider is the interaction with atomic operations and lock-free data structures. It would be great if Locked<int> and Atomic<int>, or Locked<list<int>> and lockfree_list<int>, had very similar interfaces.
The C++0x atomic<> template will work with any type that is "trivially copy assignable and bitwise equality comparable", and it is intended that operations are protected with a mutex where they cannot be done with atomic ops. The is_lock_free() member distinguishes the two cases. I would expect this to cover many uses, though obviously you can't write atomic<list<int>>, since list<> is definitely not bitwise equality-comparable. How about synchronized<> to pinch a term from Java? Anthony -- Anthony Williams Just Software Solutions Ltd - http://www.justsoftwaresolutions.co.uk Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Monday 17 March 2008 09:29 am, Anthony Williams wrote:
I've used the concept "lockable" to describe something which can be locked. I felt this was a better term than "mutex", since some of the things that can be locked are not mutexes. For example unique_lock<some_mutex> also models "Lockable", so you can have a unique_lock<unique_lock<some_mutex> >. Hopefully the new boost.thread docs will give you a better idea.
I would not describe a pair of data+mutex as "lockable" unless you could indeed lock it and unlock it (with lock()/unlock() member functions). However, even if you could, the term "lockable" wouldn't (in my view) describe what the combined data structure was. Would "protected" better describe the intent? I know it's a keyword, but surely the idea is to protect the data from concurrent access.
The monitor and monitor_ptr classes from libpoet model the (old) Mutex concepts, with scoped_lock, scoped_try_lock, etc member types. Then they add things like supporting operator->() and operator*() on the scoped lock types, for access to the data. - -- Frank -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFH3nqM5vihyNWuA4URApOmAJ41QPfU5xCZWK+w7AIxnzFDKllKcACgkMtW 75X5byFG/IsCwLFGjeyl4p8= =wwME -----END PGP SIGNATURE-----

Frank Mori Hess <frank.hess <at> nist.gov> writes:
The monitor and monitor_ptr classes from libpoet model the (old) Mutex concepts, with scoped_lock, scoped_try_lock, etc member types. Then they add things like supporting operator->() and operator*() on the scoped lock types, for access to the data.
Looks good. Anthony -- Anthony Williams Just Software Solutions Ltd - http://www.justsoftwaresolutions.co.uk Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL

Anthony Williams wrote:
Phil Endecott <spam_from_boost_dev <at> chezphil.org> writes:
I notice that Anthony Williams used the term "lockable concept" in the Boost.Threads release notes that he posted here recently. Anthony, would it be possible to say e.g. "mutex concept" in your context so that we can keep the term "lockable" available for something like this? (Or maybe they are actually the same thing. Hmmm.)
I've used the concept "lockable" to describe something which can be locked. I felt this was a better term than "mutex", since some of the things that can be locked are not mutexes. For example unique_lock<some_mutex> also models "Lockable", so you can have a unique_lock<unique_lock<some_mutex> >.
OK, that makes sense.
Hopefully the new boost.thread docs will give you a better idea.
(Off-topic: in the release notes that you posted yesterday, I didn't see a sentence saying the changes are to make Boost.Threads closer to the std::thread proposal.)
I would not describe a pair of data+mutex as "lockable" unless you could indeed lock it and unlock it (with lock()/unlock() member functions).
Well you can do exactly that if you have something like template <typename T, typename MUTEX_T = mutex> class Lockable: public T { MUTEX_t mut; public: void lock() { mut.lock(); } void unlock() { mut.unlock(); } }; Or even inherit from T and MUTEX_T. I think I'm starting to like this idea. If Lockable<T> models your "lockable concept", then it can be used with the multi-lock algorithm and so on.
However, even if you could, the term "lockable" wouldn't (in my view) describe what the combined data structure was.
I would disagree, but it just comes down to how we have individually come to use these informal terms.
Would "protected" better describe the intent? I know it's a keyword, but surely the idea is to protect the data from concurrent access.
Well, "protected" doesn't actually say the "...from concurrent access" bit. It could be protected from anything. Synchronized is a bit better (though I'd need a #define to let me spell it right :-)). I'd still vote for Lockable though.
Something else to consider is the interaction with atomic operations and lock-free data structures. It would be great if Locked<int> and Atomic<int>, or Locked<list<int>> and lockfree_list<int>, had very similar interfaces.
The C++0x atomic<> template will work with any type that is "trivially copy assignable and bitwise equality comparable", and it is intended that operations are protected with a mutex where they cannot be done with atomic ops. The is_lock_free() member distinguishes the two cases. I would expect this to cover many uses, though obviously you can't write atomic<list<int>>, since list<> is definitely not bitwise equality-comparable.
Why have the "trivially copy assignable and bitwise equality comparable" restriction, if using a mutex is an acceptable solution? Cheers, Phil.

Phil Endecott <spam_from_boost_dev <at> chezphil.org> writes:
(Off-topic: in the release notes that you posted yesterday, I didn't see a sentence saying the changes are to make Boost.Threads closer to the std::thread proposal.)
You're right: there wasn't one. I'll add one.
Anthony Williams wrote:
I would not describe a pair of data+mutex as "lockable" unless you could indeed lock it and unlock it (with lock()/unlock() member functions).
Well you can do exactly that if you have something like
template <typename T, typename MUTEX_T = mutex> class Lockable: public T { MUTEX_t mut; public: void lock() { mut.lock(); } void unlock() { mut.unlock(); } };
Or even inherit from T and MUTEX_T.
I think I'm starting to like this idea. If Lockable<T> models your "lockable concept", then it can be used with the multi-lock algorithm and so on.
Yes.
However, even if you could, the term "lockable" wouldn't (in my view) describe what the combined data structure was.
I would disagree, but it just comes down to how we have individually come to use these informal terms.
Indeed. Rereading my sentence above, it is not quite as clear as I intended. Any type that implemented the appropriate interface for the concept would be "lockable" from my POV. However, "lockable" doesn't highlight for me the intent of packaging a data item with a mutex: Lockable<int> is not just a "lockable int" (i.e. "an int that can be locked") --- the intent (safe concurrent access) is more than that.
Would "protected" better describe the intent? I know it's a keyword, but surely the idea is to protect the data from concurrent access.
Well, "protected" doesn't actually say the "...from concurrent access" bit. It could be protected from anything. Synchronized is a bit better (though I'd need a #define to let me spell it right ). I'd still vote for Lockable though.
Naming is tricky :-). safe_for_concurrent_access<int> is a bit long-winded.
The C++0x atomic<> template will work with any type that is "trivially copy assignable and bitwise equality comparable", and it is intended that operations are protected with a mutex where they cannot be done with atomic ops. The is_lock_free() member distinguishes the two cases. I would expect this to cover many uses, though obviously you can't write atomic<list<int>>, since list<> is definitely not bitwise equality-comparable.
Why have the "trivially copy assignable and bitwise equality comparable" restriction, if using a mutex is an acceptable solution?
I don't know. I've been focusing on the use of specializations that really are atomic when I've been reading the papers. I'll raise the issue. Anthony -- Anthony Williams Just Software Solutions Ltd - http://www.justsoftwaresolutions.co.uk Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL

Anthony Williams <anthony_w.geo <at> yahoo.com> writes:
Phil Endecott <spam_from_boost_dev <at> chezphil.org> writes:
Anthony Williams wrote:
The C++0x atomic<> template will work with any type that is "trivially copy assignable and bitwise equality comparable", and it is intended that operations are protected with a mutex where they cannot be done with atomic ops. The is_lock_free() member distinguishes the two cases. I would expect this to cover many uses, though obviously you can't write atomic<list<int>>, since list<> is definitely not bitwise equality-comparable.
Why have the "trivially copy assignable and bitwise equality comparable" restriction, if using a mutex is an acceptable solution?
I don't know. I've been focusing on the use of specializations that really are atomic when I've been reading the papers. I'll raise the issue.
The response from Lawrence is that firstly, types that do satisfy these restrictions can be lock-free on more platforms, and secondly if a type doesn't satisfy these constraints then you're calling user code whilst holding a lock. I think this is an important point, and it might be worth having a separate type to highlight the distinction. For types that satisfy the current requirements, you can't tell by use whether or not a lock is used, since the externally-visible effects are the same, so users might not think about the possible presence of a mutex, just that the ops are atomic (which is the idea). For types that had user-written copy constructors or comparison operators, you could potentially end up with deadlock, and race conditions, even though there's a mutex --- e.g. if the comparison operator referenced global state which could be accessed from elsewhere without the mutex. If we have a separate synchronisable<T> type that always uses a mutex, the issue is much more explicit, and people are more likely to check their code for such problems. Of course, people will still make mistakes sometimes, but these will be less common. Anthony -- Anthony Williams Just Software Solutions Ltd - http://www.justsoftwaresolutions.co.uk Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL

Hi, Phil Endecott schrieb:
I've mentioned something like this here a couple of times; did you see my Proto review? http://thread.gmane.org/gmane.comp.lib.boost.devel/171620
I've just taken a look at your proto review and to me it just asserts the usefulness of a lockable. The same goes for Frank's monitoring_ptr. My feeling is that some sort of type/mutex pairing and an expressive threadsafe access is desirable.
My feeling is that the basic Lockable<T> that pairs a mutex with the data that it locks is by itself too trivial for Boostification. Where it gets interesting is when you provide an interface to do automatic locking and unlocking, i.e. your locking pointer; I'd be interested to see some example usage for your version. In my case I found that the locking pointer (or locking reference) was actually more verbose than explicitly locking the mutex and accessing the data, so the only benefit is that the locking can be mandatory. But normally the only "enforcement" that I need is a comment: /* lock this before writing that */. Perhaps you have something more concise than I managed.
Well, you are right, a lockable<T> is a very trivial wrapper; however under the premise that I like encapsulation :) it does more than that: - it encapsulates a certain feature and gives it a (hopefully) sound name, still being generic - you can clearly express in a relatively easy technical way your intention that a variable should be accessed in a threadsafe manner. If I see a lockable eventually together with a lock_acquirer I immediately know that some threadsafe access is happening - a lockable<T> is easier to share with a shared_ptr<lockable<T>> between multiple threads and places of code than something else - transitive constness: no matter whether the lockable<T> is shared const (even if T is non-const) or T itself is const or the programmer requires read-only access, she only gets const-access to T (which doesn't mean that she gets non-const access to const T if she requires write access) - if lockable<T>'s T is only accessible through interface methods you can clearly see in which way you access T - encapsulation of volatile-ness: I don't know how important the volatile qualifier is for a lockable but since my lockable<T> stores T volatile and the proper interface methods and the lock_aqcuirer gives me non-volatile access I don't have to think so much about some type being volatile or not. Having read Andrei's article on ddj (btw: http://www.ddj.com/dept/cpp/184403766) I had the impression that the volatile qualifier is quite important. The lock_acquirer functions with lockables as well as with free-floating variables and mutexes. It's more a locking reference than a pointer; first I considered pointer semantics but it is even safer to have a protected friend template function (access_acquiree) that requires a named lock_acquirer object and that is only accessible by its unqualified name through ADL. Sure, the creation of a named lock_acquirer is quite verbose - I thought already of the c++09 auto typing feature together with RVO (return value optimization) like: auto some_lock = make_lock_acquirer(some_lockable); Still, to me the syntactic overhead seems worth the effort and might be solved by other ways yet to be found. Please have a look at my code, it can be found in the boost vault, name "thread_lockable.zip". The project contains a solution for msvc 2008 but should be easy to compile with any compiler. Klaus

klaus triendl wrote:
Please have a look at my code, it can be found in the boost vault, name "thread_lockable.zip".
Hi Klaus, I finally got around to looking at this. Yes, it does what I expect it to. One detail I noticed was that you make the mutex mutable, so that you can lock a const object. This is something that I have worried about, because I fear that there are "gotchas". Do any experts have opinions about this? I said before that I found my own locking pointer (or locking reference) was more verbose than explicitly locking the mutex and accessing the data, and so have not used it much. I've now remembered another practical problem with it. Here's the code without Lockable<>: void start_updater(mutex& m, int& val) { // spawn a thread that periodically locks m and changes val } mutex m; array<int,100> values; for (int i=0; i<100; ++i) { start_updater(m,values[i]); } The point is that all of the values share a mutex. Our Lockable<> classes could be used here if you wanted one mutex per value, but that could be wasteful and perhaps wrong. How can we adapt Lockable<> to work in this sort of situation? I'm wondering about something like this: Lockable< array<int,100 > values; for (int i=0; i<100; ++i) { start_updater(LockableRef<int>(values,values[i]); } i.e. LockableRef contains a reference to the mutex and one element of its "parent" Lockable<>, and presents the same interface as a data-full Lockable<>. But because Lockable and LockableRef aren't actually the same type, I can't write void start_updater(Lockable<int> i); and use it with both. Is this something that you have thought about? Phil.

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Sunday 23 March 2008 18:47 pm, Phil Endecott wrote:
The point is that all of the values share a mutex. Our Lockable<> classes could be used here if you wanted one mutex per value, but that could be wasteful and perhaps wrong. How can we adapt Lockable<> to work in this sort of situation? I'm wondering about something like this:
Lockable< array<int,100 > values;
for (int i=0; i<100; ++i) { start_updater(LockableRef<int>(values,values[i]); }
i.e. LockableRef contains a reference to the mutex and one element of its "parent" Lockable<>, and presents the same interface as a data-full Lockable<>.
I think what you would want to do is just create one scoped lock, perhaps outside of the loop (a lock_acquirer in the lockable lib syntax) and use that to access the values in the array. It could just pass an ordinary reference to the start_updater function, as long as start_updater doesn't keep any persistent copies of the reference around. You do give me an idea though, I could make poet::monitor_ptr (pointer-like monitor) constructible from poet::monitor (value-like monitor) which would I think be similar to what you're getting at with LockableRef. It would actually be very easy, since I implemented poet::monitor using monitor_ptr (out of laziness). Incidentally, I find the example usage of lock acquirers in the thread_lockable examples a bit verbose and dangerous. That is stuff like: mbox::writelock_acquirer<mutexlockable_int> int_lock(*pLockableInt); int& i = access_acquiree(int_lock); i = 5; Why not just support operator* (and operator-> for calling class methods) on the lock_acquirer class? Then you could do something like: mbox::writelock_acquirer<mutexlockable_int> int_lock(*pLockableInt); *int_lock = 5; Its less verbose, and by using the lock_acquirer directly it reduces the possibility of a dangling reference outliving the lock_acquirer it was obtained from. - -- Frank -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFH57+S5vihyNWuA4URAro/AKDJKYhHEjUCcPFW+tgv35Ak5NT87ACePs+F sfW6gMAdgxWAoivYda2YL+M= =ssSm -----END PGP SIGNATURE-----

Frank Mori Hess wrote:
On Sunday 23 March 2008 18:47 pm, Phil Endecott wrote:
The point is that all of the values share a mutex. Our Lockable<> classes could be used here if you wanted one mutex per value, but that could be wasteful and perhaps wrong. How can we adapt Lockable<> to work in this sort of situation? I'm wondering about something like this:
Lockable< array<int,100 > values;
for (int i=0; i<100; ++i) { start_updater(LockableRef<int>(values,values[i]); }
i.e. LockableRef contains a reference to the mutex and one element of its "parent" Lockable<>, and presents the same interface as a data-full Lockable<>.
I think what you would want to do is just create one scoped lock, perhaps outside of the loop (a lock_acquirer in the lockable lib syntax) and use that to access the values in the array. It could just pass an ordinary reference to the start_updater function, as long as start_updater doesn't keep any persistent copies of the reference around.
No, I don't think so. You mean something equivalent to this: mutex m; array<int,100> values; { scoped_lock l(m); for (int i=0; i<100; ++i) { start_updater(LockableRef<int>(values,values[i]); } } Right? That's not the scenario that I'm trying to describe.
It could just pass an ordinary reference to the start_updater function, as long as start_updater doesn't keep any persistent copies of the reference around.
My point is that start_updater does keep a copy of the reference; from my original post: void start_updater(mutex& m, int& val) { // spawn a thread that periodically locks m and changes val } Or to be more explicit: void run_updater(mutex& m, int& val) { while (1) { sleep(1000); scoped_lock l(m); val = read_sensor(); } } void start_updater(mutex&m, int& val) { thread t(bind(run_updater,m,val)); } Phil.

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Monday 24 March 2008 11:27 am, Phil Endecott wrote:
My point is that start_updater does keep a copy of the reference; from my original post:
void start_updater(mutex& m, int& val) { // spawn a thread that periodically locks m and changes val }
Ah, sorry I missed that. But the poet::monitor_ptr constructed from a poet::monitor idea I mentioned would appear to cover your use case? On the other hand, if you're not concerned about shared ownership, I'm not clear on what the advantage of passing a LockableRef would be over just passing a plain reference to a Lockable? - -- Frank -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFH582I5vihyNWuA4URAqNoAJ9FIasidhIOKNjqYkKRZSfPgjy2nQCgp+TQ 3Hn1z6fDRtv00o0bu+CQYyM= =SZHZ -----END PGP SIGNATURE-----

Frank Mori Hess wrote:
I'm not clear on what the advantage of passing a LockableRef would be over just passing a plain reference to a Lockable?
The Lockable that I have is a Lockable< array<int,100> >. I need something like a Lockable<int> to pass to the function, because the function is only interested in part of the protected data. I want some way to reference the "parent" Lockable's mutex and a portion of its data (element of array, field of a struct etc.) Phil.

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Monday 24 March 2008 12:13 pm, Phil Endecott wrote:
The Lockable that I have is a Lockable< array<int,100> >. I need something like a Lockable<int> to pass to the function, because the function is only interested in part of the protected data. I want some way to reference the "parent" Lockable's mutex and a portion of its data (element of array, field of a struct etc.)
Oh, I see now. So you would want something along the lines of a pointer/reference-like Lockable that has a constructor which can take a reference (or maybe a shared_ptr) to an already existing mutex. Is there some way to make it possible to use boost::bind for this? So you could bind together a Lockable and a function returns a reference to the object you're interested in, and then use it later to create a scoped_lock/lock_acquirer? - -- Frank -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFH5+mX5vihyNWuA4URApyLAKCm0SFKEp4kdcuOnpTtixosY9xS/wCeOlGY MTt/KdxNusTJZOzx1HGSTKQ= =NpCo -----END PGP SIGNATURE-----

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Monday 24 March 2008 13:49 pm, Frank Mori Hess wrote:
On Monday 24 March 2008 12:13 pm, Phil Endecott wrote:
The Lockable that I have is a Lockable< array<int,100> >. I need something like a Lockable<int> to pass to the function, because the function is only interested in part of the protected data. I want some way to reference the "parent" Lockable's mutex and a portion of its data (element of array, field of a struct etc.)
Oh, I see now. So you would want something along the lines of a pointer/reference-like Lockable that has a constructor which can take a reference (or maybe a shared_ptr) to an already existing mutex.
Is there some way to make it possible to use boost::bind for this? So you could bind together a Lockable and a function returns a reference to the object you're interested in, and then use it later to create a scoped_lock/lock_acquirer?
Or how about an aliasing constructor for poet::monitor_ptr that works similarly to the one for shared_ptr? Using it would result in a monitor_ptr that shares its mutex (and ownership) with the original monitor_ptr, but which can have an underlying pointer of a different type and address. - -- Frank -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFH5+0S5vihyNWuA4URAsMiAKCvdXcB9l5XAnYlgXuQnbgAOJxJiwCgzIpI ejhStYEhamzrNoeoPgrL0oM= =TqGK -----END PGP SIGNATURE-----

Frank Mori Hess schrieb:
Incidentally, I find the example usage of lock acquirers in the thread_lockable examples a bit verbose and dangerous. That is stuff like:
mbox::writelock_acquirer<mutexlockable_int> int_lock(*pLockableInt); int& i = access_acquiree(int_lock); i = 5;
Why not just support operator* (and operator-> for calling class methods) on the lock_acquirer class? Then you could do something like:
I like the less verbose method with operator*... the intent is to force a named lvalue lock_acquirer object.
mbox::writelock_acquirer<mutexlockable_int> int_lock(*pLockableInt); *int_lock = 5;
But what prevents me from writing: int& i = *int_lock; Klaus

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Wednesday 02 April 2008 16:50 pm, klaus triendl wrote:
Frank Mori Hess schrieb:
Incidentally, I find the example usage of lock acquirers in the thread_lockable examples a bit verbose and dangerous. That is stuff like:
mbox::writelock_acquirer<mutexlockable_int> int_lock(*pLockableInt); int& i = access_acquiree(int_lock); i = 5;
Why not just support operator* (and operator-> for calling class methods) on the lock_acquirer class? Then you could do something like:
I like the less verbose method with operator*... the intent is to force a named lvalue lock_acquirer object.
mbox::writelock_acquirer<mutexlockable_int> int_lock(*pLockableInt); *int_lock = 5;
But what prevents me from writing: int& i = *int_lock;
Nothing, it just makes it less tempting to do so. You only need to add one character (the asterisk) to get a reference from the lock_acquirer directly. Presumably, the only reason you were creating the reference before was because it was more convenient to write i = 5; than access_acquiree(int_lock) = 5; - -- Frank -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFH9OGU5vihyNWuA4URAp8DAJ0fNWckUD0ADjUfmvuS1qe8KAaJgACgkplz mHOSapSE19SN4rY0YsFGPeQ= =EYOn -----END PGP SIGNATURE-----

Phil Endecott schrieb:
Hi Klaus,
Hi Phil, sorry for being quiet for some time, I was on holidays...
I finally got around to looking at this. Yes, it does what I expect it to.
One detail I noticed was that you make the mutex mutable, so that you can lock a const object. This is something that I have worried about, because I fear that there are "gotchas". Do any experts have opinions about this?
Hmm, what "gotchas" could you think of? The idea is that e.g. one thread shares a lockable and is allowed to write to it and other threads share a lockable but only get read access - this is expressed with the const-qualifier on the lockable. I would think of different ways, e.g. to refine const/non-const with derivation: <code> template<typename T, typename T_mutex> struct lockable: boost::mpl::eval_if< std::tr1::is_const<T>, lockable_base<T_mutex>, lockable<const T>
::type { // ... }; </code>
I said before that I found my own locking pointer (or locking reference) was more verbose than explicitly locking the mutex and accessing the data, and so have not used it much. I've now remembered
Do you think that using the lock_acquirer is too verbose, too? I admit that it is verbose... The intent is to force a named lvalue lock_acquirer object. I like the idea of Frank's suggestion for an operator *().
another practical problem with it. Here's the code without Lockable<>:
void start_updater(mutex& m, int& val) { // spawn a thread that periodically locks m and changes val }
mutex m; array<int,100> values;
for (int i=0; i<100; ++i) { start_updater(m,values[i]); }
The point is that all of the values share a mutex. Our Lockable<> classes could be used here if you wanted one mutex per value, but that could be wasteful and perhaps wrong. How can we adapt Lockable<> to work in this sort of situation? I'm wondering about something like this:
Lockable< array<int,100 > values;
for (int i=0; i<100; ++i) { start_updater(LockableRef<int>(values,values[i]); }
i.e. LockableRef contains a reference to the mutex and one element of its "parent" Lockable<>, and presents the same interface as a data-full Lockable<>.
But because Lockable and LockableRef aren't actually the same type, I can't write
void start_updater(Lockable<int> i);
and use it with both.
Is this something that you have thought about?
Well, I didn't think about this one. But what do you think of the following? <code> template<typename T_type, typename T_mutex> struct lockable //: ... { // ... // return a lockable_ref, see below lockable_ref<std::tr1::remove_extent<T_type>, T_mutex> operator [](std::size_t idx) { return lockable_ref<std::tr1::remove_extent<T_type>, T_mutex>(access_volatile()[idx], mutex()); } }; // a lockable_ref like I imagine it template<typename T_type, typename T_mutex> struct lockable_ref: lockable_tag { // copy construct from rvalue and lvalue ref lockable_ref(lockable_ref&& rvalue): //parent_type(rvalue.mutex()), m_mutex_ref(rvalue.mutex()), m_obj_ref(rvalue.access_volatile()) {} // implicitly construct from lockable typedef lockable<T_type, T_mutex> lockable_type; lockable_ref(lockable_type& rvalue): //parent_type(rvalue.mutex()), m_mutex_ref(rvalue.mutex()), m_obj_ref(rvalue.access_volatile()) {} // construct from a variable and a mutex lockable_ref(T_type& rvalue, T_mutex& rmutex): //parent_type(rvalue.mutex()), m_mutex_ref(rmutex), m_obj_ref(rvalue) {} T_mutex& m_mutex_ref; T_type& m_obj_ref; }; </code> now you can write start_updater like this: <code> void start_updater(lockable_ref<int, mutex> i) { } lockable<array<int, 100>, mutex> values; lockable<int, mutex> one_value; for (int i=0; i<100; ++i) { start_updater(values[i]); } start_updater(one_value); </code> Klaus

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Wednesday 02 April 2008 16:46 pm, klaus triendl wrote:
Phil Endecott schrieb:
i.e. LockableRef contains a reference to the mutex and one element of its "parent" Lockable<>, and presents the same interface as a data-full Lockable<>.
But because Lockable and LockableRef aren't actually the same type, I can't write
void start_updater(Lockable<int> i);
and use it with both.
Is this something that you have thought about?
Well, I didn't think about this one. But what do you think of the following?
<code> template<typename T_type, typename T_mutex> struct lockable //: ... { // ...
// return a lockable_ref, see below lockable_ref<std::tr1::remove_extent<T_type>, T_mutex> operator [](std::size_t idx) { return lockable_ref<std::tr1::remove_extent<T_type>, T_mutex>(access_volatile()[idx], mutex()); } };
This would only support array-like types. Are you familiar with the aliasing constructor of shared_ptr? I put something similar in poet::monitor_ptr, but it's only in cvs at the moment. Anyways, it's a more general solution to this kind of situation, although it doesn't provide much of a safeguard against misuse. Anyways, your example snippet would look something like this: <code> void start_updater(poet::monitor_ptr<int> i) { //... } poet::monitor_ptr<array<int, 100> > values; poet::monitor_ptr<int> one_value; //... for (int i=0; i<100; ++i) { start_updater(poet::monitor_ptr<int>(values, &values->at(i))); } start_updater(one_value); </code> - -- Frank -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFH9N2N5vihyNWuA4URAieWAKDPq4o2VKHIoRKeiBZmz3gN6N7VIgCg5MgQ PnAdePswe+M7crNq7SLcOlk= =IC+K -----END PGP SIGNATURE-----

Hi Klaus, From: "klaus triendl" <klaus@triendl.eu> To: <boost@lists.boost.org> Sent: Sunday, March 16, 2008 3:55 PM Subject: [boost] [thread] Interest in a locking pointer and lockable concept
Hi,
is there any interest in something like a locking pointer and a type that I call "lockable" that pairs any type with a mutex?
Inspired by Andrei Alexandrescu's article "volatile - Multithreaded Programmer's Best Friend" on ddj I implemented a concept similar to a locking pointer called "lock_acquirer" that collects acquisition of a mutex and a volatile cast of the locked type. It is even more threadsafe to use than a locking pointer.
I'm interested in your implementation, but much more in seen how this lock_acquirer differs from the locking_ptr and in seen how it can works with the shared mutex. Could you be more explicit about how it is more threadsafe? There are other locking pointer like inspired also by Andrei Alexandrescu's work that will be interesting as on_derreference_locking_ptr(on_derreference_lock_acquirer) and externally_locked.
Additionally I made a class "lockable" that pairs a (volatile) type with a mutex type, wrapping both in a distinct type, which has first the advantage that the type to protect and the mutex are glued together, and second that it encapsulates volatile qualifier correctly.
Related to the lockable class, there is a lot of work on the ACE library that could inspire you, as lock, adaptive_lock, look_adapter that are the polymorphic counterpart of mutex. I'll reserve lockable for the concept Lockable. lockable is not a noum. Maybe locker. locker<T> will be a model of the Lockable concept.
Also, the locked type in a lockable can, if wanted, by a restricted interface only be accessed by a lock_acquirer thus forcing the programmer to a very threadsafe programming style.
Could you show us how?
I can post my implementation along with a simple example to the boost vault.
Klaus
Please do it if you have not yet done. Regards ____________________ Vicente Juan Botet Escriba

vicente.botet schrieb:
Inspired by Andrei Alexandrescu's article "volatile - Multithreaded Programmer's Best Friend" on ddj I implemented a concept similar to a locking pointer called "lock_acquirer" that collects acquisition of a mutex and a volatile cast of the locked type. It is even more threadsafe to use than a locking pointer.
I'm interested in your implementation, but much more in seen how this lock_acquirer differs from the locking_ptr and in seen how it can works with the shared mutex. Could you be more explicit about how it is more threadsafe?
There are other locking pointer like inspired also by Andrei Alexandrescu's work that will be interesting as on_derreference_locking_ptr(on_derreference_lock_acquirer) and externally_locked.
Hi Vicente, I expressed myself too vague - the lock_acquirer itself isn't more threadsafe than a locking_ptr but the way the programmer has to use it leads to more threadsafe programming behaviour, I believe. Everything depends on the usage scenario, of course. A locking_ptr takes the lockable (locker, or whatever you call it) and provides access via operator ->() and operator *(); thus you can write: <code> lockable<T, mutex> l; T* p = locking_ptr<lockable<T, mutex> >(l).operator ->(); T& o = *locking_ptr<lockable<T, mutex> >(l); </code> ... constructing the locking_ptr and accessing an object in a single line. lock_acquirer doesn't aim to be a smart pointer (though I like also the idea of having an automatic locking while accessing an object's method). It is constructed from a lockable but access to the object is only granted via a protected friend template function forcing to pass a named lock_acquirer object: <code> lockable<T, mutex> l; lock_acquirer<lockable<T, mutex> > a(l); T& o = access_acquiree(a); </code> An additional bonus of lock_acquirer is that the programmer can specify a locking policy (read/write) as a template parameter and the lock_acquirer selects an appropriate r/w lock for the given mutex type. If the locking policy is read access then lock_acquirer grants const-access only to the synchronized object even if T in lockable<T> is non-const: <code> lockable<int, mutex> l; // readlock_acquirer derives from // lock_acquirer<readlock, lockable<int, mutex> > readlock_acquirer<lockable<int, mutex> > a(l); const int& o = access_acquiree(a); </code>
Also, the locked type in a lockable can, if wanted, by a restricted interface only be accessed by a lock_acquirer thus forcing the programmer to a very threadsafe programming style.
Could you show us how?
The lockable type has public access methods, a safe_lockable however makes lock_acquirer a friend: template<typename T, typename T_mutex> struct safe_lockable: public lockable_base<T_mutex> { template<...> friend class lock_acquirer; protected: volatile_T& access_volatile(); T& access_nonvolatile(); }; Klaus

Hi Klaus, Thanks to post your implementation on the vault. Regards, Vicente ____________________ Vicente Juan Botet Escriba ----- Original Message ----- From: "klaus triendl" <klaus@triendl.eu> To: <boost@lists.boost.org> Sent: Tuesday, March 18, 2008 10:20 AM Subject: Re: [boost] [thread] Interest in a locking pointer and lockableconcept
vicente.botet schrieb:
Inspired by Andrei Alexandrescu's article "volatile - Multithreaded Programmer's Best Friend" on ddj I implemented a concept similar to a locking pointer called "lock_acquirer" that collects acquisition of a mutex and a volatile cast of the locked type. It is even more threadsafe to use than a locking pointer.
I'm interested in your implementation, but much more in seen how this lock_acquirer differs from the locking_ptr and in seen how it can works with the shared mutex. Could you be more explicit about how it is more threadsafe?
There are other locking pointer like inspired also by Andrei Alexandrescu's work that will be interesting as on_derreference_locking_ptr(on_derreference_lock_acquirer) and externally_locked.
Hi Vicente,
I expressed myself too vague - the lock_acquirer itself isn't more threadsafe than a locking_ptr but the way the programmer has to use it leads to more threadsafe programming behaviour, I believe. Everything depends on the usage scenario, of course.
A locking_ptr takes the lockable (locker, or whatever you call it) and provides access via operator ->() and operator *(); thus you can write: <code> lockable<T, mutex> l; T* p = locking_ptr<lockable<T, mutex> >(l).operator ->(); T& o = *locking_ptr<lockable<T, mutex> >(l); </code>
... constructing the locking_ptr and accessing an object in a single line.
lock_acquirer doesn't aim to be a smart pointer (though I like also the idea of having an automatic locking while accessing an object's method). It is constructed from a lockable but access to the object is only granted via a protected friend template function forcing to pass a named lock_acquirer object: <code> lockable<T, mutex> l; lock_acquirer<lockable<T, mutex> > a(l); T& o = access_acquiree(a); </code>
An additional bonus of lock_acquirer is that the programmer can specify a locking policy (read/write) as a template parameter and the lock_acquirer selects an appropriate r/w lock for the given mutex type. If the locking policy is read access then lock_acquirer grants const-access only to the synchronized object even if T in lockable<T> is non-const: <code> lockable<int, mutex> l; // readlock_acquirer derives from // lock_acquirer<readlock, lockable<int, mutex> > readlock_acquirer<lockable<int, mutex> > a(l); const int& o = access_acquiree(a); </code>
Also, the locked type in a lockable can, if wanted, by a restricted interface only be accessed by a lock_acquirer thus forcing the programmer to a very threadsafe programming style.
Could you show us how?
The lockable type has public access methods, a safe_lockable however makes lock_acquirer a friend:
template<typename T, typename T_mutex> struct safe_lockable: public lockable_base<T_mutex> { template<...> friend class lock_acquirer;
protected: volatile_T& access_volatile(); T& access_nonvolatile(); };
Klaus
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Hi, From: "klaus triendl" <klaus@triendl.eu>
vicente.botet schrieb: [snip]
I'm interested in your implementation, but much more in seen how this lock_acquirer differs from the locking_ptr and in seen how it can works with the shared mutex. Could you be more explicit about how it is more threadsafe?
There are other locking pointer like inspired also by Andrei Alexandrescu's work that will be interesting as on_derreference_locking_ptr(on_derreference_lock_acquirer) and externally_locked.
Hi Vicente,
I expressed myself too vague - the lock_acquirer itself isn't more threadsafe than a locking_ptr but the way the programmer has to use it leads to more threadsafe programming behaviour, I believe. Everything depends on the usage scenario, of course.
A locking_ptr takes the lockable (locker, or whatever you call it) and provides access via operator ->() and operator *(); thus you can write: <code> lockable<T, mutex> l; T* p = locking_ptr<lockable<T, mutex> >(l).operator ->(); T& o = *locking_ptr<lockable<T, mutex> >(l); </code>
... constructing the locking_ptr and accessing an object in a single line.
lock_acquirer doesn't aim to be a smart pointer (though I like also the idea of having an automatic locking while accessing an object's method). It is constructed from a lockable but access to the object is only granted via a protected friend template function forcing to pass a named lock_acquirer object: <code> lockable<T, mutex> l; lock_acquirer<lockable<T, mutex> > a(l); T& o = access_acquiree(a); </code>
In (*) below, is the mutex locked or not? If yes, why do we need access_acquiree(a)? If not when the mutex is locked? <code> lockable<T, mutex> l; lock_acquirer<lockable<T, mutex> > a(l); //(*) T& o = access_acquiree(a); //(**) </code> I supose that it is not possible to unlock l in (**). Your design recall the one of externally_locked class. externally_locked cloaks an object of type T and a reference of a Lockable objet, used to synchronize this object and possibily others. Actually provides full access to that object through the get member function (set could also be considered), provided you pass a reference to a Locker object that owns the Lockable. Here it is my implementation: <code> template <typename T, typename Lockable> class externally_locked { public: externally_locked(T& obj, Lockable& lockable) : obj_(obj) , lockable_(lockable) {} template <class Locker> T& get(Locker& locker) { BOOST_STATIC_ASSERT(is_strict_locker<Locker>); if (locker) { Locker try_lock(lockable_, locking_traits<Lockable>::try_to_lock()); if (!try_lock) { return obj_; } else { try_lock.unlock(); throw bad_lock(); } } else { throw bad_lock(); } } /// .... other functions private: T& obj_; Lockable& lockable; }; </code> And used as follows <code> T t; boost::mutex m; externally_locked<T, boost::mutex> el_t(t, m); { boost::lock_guard<mutex> g(l); ... /// from here can use el_t.get(g). to access 't' in a thread safe mode. el_t.get(g).fct(); } </code> is_strict_locker is true if the locker is unable to unlock the lockable instance and locking_traits provides some traits associated to the locable concept.
An additional bonus of lock_acquirer is that the programmer can specify a locking policy (read/write) as a template parameter and the lock_acquirer selects an appropriate r/w lock for the given mutex type. If the locking policy is read access then lock_acquirer grants const-access only to the synchronized object even if T in lockable<T> is non-const: <code> lockable<int, mutex> l; // readlock_acquirer derives from // lock_acquirer<readlock, lockable<int, mutex> > readlock_acquirer<lockable<int, mutex> > a(l); const int& o = access_acquiree(a); </code>
Which is the type of access_acquiree parameter? Have you un implementation using the boost shared_mutex, ?
Also, the locked type in a lockable can, if wanted, by a restricted interface only be accessed by a lock_acquirer thus forcing the programmer to a very threadsafe programming style.
Could you show us how?
The lockable type has public access methods, a safe_lockable however makes lock_acquirer a friend:
template<typename T, typename T_mutex> struct safe_lockable: public lockable_base<T_mutex> { template<...> friend class lock_acquirer;
protected: volatile_T& access_volatile(); T& access_nonvolatile(); };
Shouldn't safe_lockable use private or protected inheritance? This recalls me the backdoor pattern already used in the preceding version of the threads library. I really liked how the mutex were designed. It was really safe. No possibility to lock without unlock. The single problem was that the backdoor class was not provided as part of the interface, it was on the detail namespace. This two level interface, safe interface through the public interface and an unsafe one using a backdoor seams to me very promising. IMHO it was a pitie that the thread proposal for C++0x has removed this. I suppose that they had good raisons to remove it. Here it is the design of a mutex and a lock_guard with a backdoor. template <class Lockable> struct lockable_backdoor { lockable_backdoor(Lockable&); lock(); unlock(); Lockable lock_; }; class mutex : boost::noncopyable{ public: // no safe interface other than using guards as lock_guard, ... private: template <typename> friend class lockable_backdoor; // ... }; template<typename Lockable> class lock_guard { public: explicit lock_guard(Lockable& m): m_(m){ lockable_backdoor(m_).lock(); }; ~lock_guard() { lockable_backdoor(m_).lock(); } }; Best regards Vicente
participants (6)
-
Anthony Williams
-
Frank Mori Hess
-
Frank Mori Hess
-
klaus triendl
-
Phil Endecott
-
vicente.botet