Gauging interest in a thread-safe containers library

Hi all, I've recently been doing some work that required thread-safe containers (queue, channel) and I found that in addition to these, a locking_ptr with similar semantics to boost::shared_ptr (except being thread-safe too) allowed me to perform all my threaded operations without needing to worry about synchronisation outside the container/locking_ptr code, and without a major impact on performance compared to coding locks in a slightly less generic (== paranoid) way. These have been re-used with some success by others, and a couple of containers (specifically the queue and channel) seem to be used commonly enough to be worthy of an inclusion in Boost.Thread or similar. This discussion has been had in the past, on this thread: http://lists.boost.org/Archives/boost/2004/09/71454.php Someone else mentioned it later, but there doesn't seem to be much else said here: http://lists.boost.org/Archives/boost/2005/11/96991.php I was wondering if there is more interest now, a few years on. Some of the pros I can think of are: * Writing correct thread-safe code can be tricky (I messed up the first time I wrote my queue). * Use of multithreading is perhaps more prevalent now, with many-core CPUs, than it was 4 years ago * Queues / channels in particular are very useful for building threaded pipelines etc, and more people might be tempted to use such programming techniques if a safe option was available "out-of-the-box" * A thread-safe shared pointer provides a lazy but effective way of synchronising data in many scenarios (my implementation supports the usual boost::shared_ptr stuff except for get(), -> and *: dereferencing is provided by calling an acquire() function which returns a scoped_lock object on which you can call * and ->.The destructor of the scoped_lock releases the lock.) * The thread-safe shared pointer means you could use a pop-copy queue without significant overhead (depending on use) * The code length for these class templates on a single platform is actually quite short (a hundred lines of code or so) Some cons: * Need to support all the platforms Boost works on * Not much interest for these containers last time * More code to maintain * Questions about how re-usable a synchronised queue is due to performance worries (I believe this not to be a major problem for a major class of problems - e.g. pipelining fairly long operations, such as in image and video processing) I'm sure I've missed some things, but I'd be interesting in hearing whether there's a positive or negative reaction to this. I'd be happy to contribute code for this - it wouldn't be the fastest possible code, but I believe that robust, lightweight, maintainable code is better than nothing in this case, even if it's not the very best lock-free implementation. Thanks for reading! Eric

IMHO what would be really a nice to have is an (almost) lock-free queue. -- Edouard

Eric Greveson wrote:
I've recently been doing some work that required thread-safe containers (queue, channel) and I found that in addition to these, a locking_ptr with similar semantics to boost::shared_ptr (except being thread-safe too)
shared_ptr is already thread-safe. Did you mean you perform a lock around the access to the wrapped pointer? Why is it tied to shared ownership? This should be distinct things. Apart from that, if I were to use a thread-safe queue, I would probably use one designed for maximum efficiency using lock-free techniques, rather than one designed in terms of naive locking. Intel TBB provides that kind of thing already; and there had been discussion of integrating TBB into Boost in the past.

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Wednesday 14 January 2009 08:06 am, Mathias Gaunard wrote:
Eric Greveson wrote:
I've recently been doing some work that required thread-safe containers (queue, channel) and I found that in addition to these, a locking_ptr with similar semantics to boost::shared_ptr (except being thread-safe too)
shared_ptr is already thread-safe. Did you mean you perform a lock around the access to the wrapped pointer?
Why is it tied to shared ownership? This should be distinct things.
Yes, you could build a monitor based on a boost::optional model rather than a shared_ptr. I agree it seems better to provide the monitor functionality at a lower level, orthogonal to shared_ptr. That said, I too wrote a locking shared_ptr some time ago and as I recall the main reasons for going that way were: 1) Easy to support non-copyable types. At the time, I was probably not aware that boost::optional supported in-place factories, but in any case shared_ptr supports non-copyable types in a simpler way. 2) If two threads are both accessing the same object, they will often also want to share ownership of the object. I guess what I'd like to see in boost would be something like a boost::monitor<T> which would be like a boost::optional<T> plus monitor functionality, and maybe a boost::monitor_ptr<T> which would be a boost::shared_ptr<boost::monitor<T> > with some syntactic sugar. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFJbffU5vihyNWuA4URAmQyAKDPrHhHo94YQQ5CT1uAFgMdlu1s6QCfee3O IFCrP2UXkt7SUBBxXXk9Sls= =9KVQ -----END PGP SIGNATURE-----

----- Original Message ----- From: "Frank Mori Hess" <frank.hess@nist.gov> To: <boost@lists.boost.org> Sent: Wednesday, January 14, 2009 3:33 PM Subject: Re: [boost] Gauging interest in a thread-safe containers library
I guess what I'd like to see in boost would be something like a boost::monitor<T> which would be like a boost::optional<T> plus monitor functionality, and maybe a boost::monitor_ptr<T> which would be a boost::shared_ptr<boost::monitor<T> > with some syntactic sugar.
Hi, I know that you have already a monitor class in your Poet library. Why don't you propose Poet for inclusion in Boost? What about a review request? Regards, Vicente

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Tuesday 10 February 2009 15:05 pm, Vicente Botet wrote:
----- Original Message ----- From: "Frank Mori Hess" <frank.hess@nist.gov> To: <boost@lists.boost.org> Sent: Wednesday, January 14, 2009 3:33 PM Subject: Re: [boost] Gauging interest in a thread-safe containers library
I guess what I'd like to see in boost would be something like a boost::monitor<T> which would be like a boost::optional<T> plus monitor functionality, and maybe a boost::monitor_ptr<T> which would be a boost::shared_ptr<boost::monitor<T> > with some syntactic sugar.
Hi, I know that you have already a monitor class in your Poet library. Why don't you propose Poet for inclusion in Boost? What about a review request?
I would like to rewrite poet::monitor to be more like boost::optional, instead of the current implementation which was built on top of poet::monitor_ptr, mostly for historical reasons (it was convenient at the time). However, I don't know when (or even if) I'll get around to actually doing that. Anyone is welcome to use whatever parts of the libpoet interface or code they like and incorporate it into their own submission though. I wouldn't mind at all. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFJkzil5vihyNWuA4URAp/xAJ0cmY2Yjz7Tej/w9S+W61xa8PrTyQCeNwMR zejLAuYVC5uvicaNm286CrM= =7p0z -----END PGP SIGNATURE-----

----- Original Message ----- From: "Mathias Gaunard" <mathias.gaunard@ens-lyon.org> To: <boost@lists.boost.org> Sent: Wednesday, January 14, 2009 2:06 PM Subject: Re: [boost] Gauging interest in a thread-safe containers library
Apart from that, if I were to use a thread-safe queue, I would probably use one designed for maximum efficiency using lock-free techniques, rather than one designed in terms of naive locking. Intel TBB provides that kind of thing already; and there had been discussion of integrating TBB into Boost in the past.
It will be rely good tohave TBB included on Boost. I'm not sure Intel is interested in having a portable implementation of TBB. Respect to the fact that there is a discussion, I have not hear too much since a long time. I hope that there are other boosters working on lock-free techniques and that êven if TBB is a good library we could work without. Best, Vicente

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Tuesday 13 January 2009 18:34 pm, Eric Greveson wrote:
* A thread-safe shared pointer provides a lazy but effective way of synchronising data in many scenarios (my implementation supports the usual boost::shared_ptr stuff except for get(), -> and *: dereferencing is provided by calling an acquire() function which returns a scoped_lock object on which you can call * and ->.
Why not support operator->()? It would be a convenience. Due to the way operator->() works, all it has to do is return something like your scoped_lock. Also, I think it would be worthwhile to follow the concepts of Boost.Threads/C++0x with respect to mutexes and locks. That is, make the monitor fulfill the Lockable concept, and give your locks the interfaces of boost::unique_lock, shared_lock, etc. except with added operator->() and operator*().
The destructor of the scoped_lock releases the lock.) -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQFJbfoH5vihyNWuA4URAu9SAJsG6e3RUc+dsA1XpyCJAERZuEe1ygCfQZZk hxMX+QWY35avYq2rA5Ts6sc= =U37n -----END PGP SIGNATURE-----

Thanks for the constructive comments so far!
IMHO what would be really a nice to have is an (almost) lock-free queue.
Agreed - if a generic thread-safe queue was to be implemented, it should be lock-free for maximum utility.
shared_ptr is already thread-safe. Did you mean you perform a lock around the access to the wrapped pointer?
Yes, sorry, that's what I meant.
Why is it tied to shared ownership? This should be distinct things. and I guess what I'd like to see in boost would be something like a boost::monitor<T> which would be like a boost::optional<T> plus monitor functionality, and maybe a boost::monitor_ptr<T> which would be a boost::shared_ptr<boost::monitor<T> > with some syntactic sugar. and Why not support operator->()? It would be a convenience.
I haven't used boost::optional myself, but looking at the docs for it, this sounds like it could be a good way to go. While it's true that the underlying monitoring / sharing concepts should be distinct, I think that a convenience class (like the suggested boost::monitor_ptr) would be useful simply because data sharing between threads in this way is a common idiom.
Intel TBB provides that kind of thing already; and there had been discussion of integrating TBB into Boost in the past.
I think that the TBB license means that it can't be integrated with Boost. The queue provided in TBB is certainly something similar to what I had in mind, although the interface looks a little bit bloaty...
Also, I think it would be worthwhile to follow the concepts of Boost.Threads/C++0x with respect to mutexes and locks. That is, make the monitor fulfill the Lockable concept, and give your locks the interfaces of boost::unique_lock, shared_lock, etc. except with added operator->() and operator*().
Sure, that makes sense. Before going into too much detail on what exactly should be included, it seems that there is at least some interest in having some more thread-friendly data structures in Boost. It's true that libraries like TBB provide some of the required functionality, but I think TBB might be a bit higher-level (in general) than what could be provided by Boost. It seems that more than one person has been writing their own data-sharing monitors/pointers and queues - to my mind these are fairly basic requirements for several multi-threading scenarios which should be provided by a standard-ish library. If we focus on the following as the most common requirements, then: - monitor / monitor_ptr - lock-free queue, e.g. They're obviously not useful in all multi-threaded scenarios, but I think they would make lots of programmers' lives easier some of the time. I'd like to hear more reasons for not including these: any pointers to other libraries that implement these in a portable way, with the same kind of license as Boost, would be one reason (although having all your basic threading functionality in One Big Library is still a bonus). cheers Eric

----- Original Message ----- From: "Eric Greveson" <eric@greveson.co.uk> To: <boost@lists.boost.org> Sent: Thursday, January 15, 2009 12:27 AM Subject: Re: [boost] Gauging interest in a thread-safe containers library
If we focus on the following as the most common requirements, then: - monitor / monitor_ptr - lock-free queue, e.g.
They're obviously not useful in all multi-threaded scenarios, but I think they would make lots of programmers' lives easier some of the time. I'd like to hear more reasons for not including these: any pointers to other libraries that implement these in a portable way, with the same kind of license as Boost, would be one reason (although having all your basic threading functionality in One Big Library is still a bonus).
Hi, I don't think there is any reason to don't include any feature in Boost. AFAIK, a lot of posts talk about these extension but no body has push until proposing a library for review. I'm working on a Sync library including: a.. Lock traits: very specific traits classes, some of them encapsulate a single trait for a Lockable type; for example, is a lock recursive (is_recursive), is useful in a multi threaded context (is_multi_threaded). b.. Finding the best lock: Inverse traits can match a lockable type based on specific traits, for a given family of lock types. c.. Synchronization family: A class that will do internal locking can be parameterized by the type of synchronization family needed to achieve the desired level of concurrency control. This depends of the usage scope of this class, and this can be mono_threaded, multi_threaded, multi_process. d.. Strict lockers: A strict_locker is a scoped lock guard ensuring the mutex is locked on the scope of the lock, by locking the mutex on construction and unlocking it on destruction. e.. External lockers: An alternative or complementary approach to internal locking is to support external locking for an object - Multiple calls may be grouped within the same externally defined critical region. f.. Reverse locker: reverse_lock. g.. Concurrent components may interact in different ways: they may access the same objects by, for example, executing functions of these objects; or they may communicate directly by executing functions of each other. This library includes monitor for guaranteeing exclusive access to an object and also the so-called rendezvous mechanism for handling direct communication between active objects. For the moment all this is an draft state. You can get them from the Sandbox. https://svn.boost.org/svn/boost/sandbox/synchro I'll try to put something on the Vault this week. Best, Vicente

Hi Eric, ----- Original Message ----- From: "Eric Greveson" <eric@greveson.co.uk> To: <boost@lists.boost.org> Sent: Wednesday, January 14, 2009 12:34 AM Subject: [boost] Gauging interest in a thread-safe containers library
Hi all,
I've recently been doing some work that required thread-safe containers (queue, channel) and I found that in addition to these, a locking_ptr with similar semantics to boost::shared_ptr (except being thread-safe too)
locking_ptr is not thread-safe, but it allows you to make the pointee data thread-safe.
allowed me to perform all my threaded operations without needing to worry about synchronisation outside the container/locking_ptr code, and without a major impact on performance compared to coding locks in a slightly less generic (== paranoid) way. These have been re-used with some success by others, and a couple of containers (specifically the queue and channel) seem to be used commonly enough to be worthy of an inclusion in Boost.Thread or similar.
This discussion has been had in the past, on this thread:
Sorry but I have not yet started to see these atomics operations, so I can not help on tis.
Someone else mentioned it later, but there doesn't seem to be much else said here:
The queue from Olivier could be greate,but offen the user has already other locks and so what we need is external locking. My Synchro library provides several ways to get this.
I was wondering if there is more interest now, a few years on. Some of the pros I can think of are:
* Writing correct thread-safe code can be tricky (I messed up the first time I wrote my queue). * Use of multithreading is perhaps more prevalent now, with many-core CPUs, than it was 4 years ago
I agree.
* Queues / channels in particular are very useful for building threaded pipelines etc, and more people might be tempted to use such programming techniques if a safe option was available "out-of-the-box"
Could you be more explicit about threaded pipelines? Could the ThreadPool library help you?
* A thread-safe shared pointer provides a lazy but effective way of synchronising data in many scenarios (my implementation supports the usual boost::shared_ptr stuff except for get(), -> and *: dereferencing is provided by calling an acquire() function which returns a scoped_lock object on which you can call * and ->.The destructor of the scoped_lock releases the lock.)
Ye this is a realy useful and easy to use class. I have also my own.
* The thread-safe shared pointer means you could use a pop-copy queue without significant overhead (depending on use)
Could you be more explicit?
* The code length for these class templates on a single platform is actually quite short (a hundred lines of code or so)
Well the relative small code size is not what makes these classes more usable and maintenable. We need to take care of most of user needs.
Some cons:
* Need to support all the platforms Boost works on
This should not be a big problem for most of the features (except lock-free if asm is needed.)
* Not much interest for these containers last time
This can change.
* More code to maintain
I agree. But also to test, document, ...
* Questions about how re-usable a synchronised queue is due to performance worries (I believe this not to be a major problem for a major class of problems - e.g. pipelining fairly long operations, such as in image and video processing)
Have you take a look to the Dataflow library?
I'm sure I've missed some things, but I'd be interesting in hearing whether there's a positive or negative reaction to this. I'd be happy to contribute code for this - it wouldn't be the fastest possible code, but I believe that robust, lightweight, maintainable code is better than nothing in this case, even if it's not the very best lock-free implementation.
You can contribute to my Synchro library with suggestions, code&tests, documentation. See https://svn.boost.org/svn/boost/sandbox/synchro for the moment. Best regards, Vicente
participants (6)
-
Edouard A.
-
Eric Greveson
-
Frank Mori Hess
-
Mathias Gaunard
-
Vicente Botet
-
vicente.botet