Using Boost.Atomic for shared_ptr

Dear All, I have just had a quick look at how easily shared_ptr could be adapted to use Helge's proposed Boost.Atomic. To me it looks very simple, but maybe someone who knows more about all this could have a look? I took the non-threaded implementation in sp_counted_base_nt.hpp as a starting point, and simply changed the two counts from long to boost::atomic<long> : boost::atomic<long> use_count_; // #shared boost::atomic<long> weak_count_; // #weak + (#shared != 0) Then, the majority of the methods don't need to change since the atomic template provides atomic versions of e.g. operator++. The one that does need more work is add_ref_lock, which I've implemented as follows: bool add_ref_lock() // true on success { while (1) { long c = use_count_; if (c == 0) return false; if (use_count_.compare_exchange_weak(c,c+1)) return true; } } There is also the question of memory barriers. I understand that things like operator++ provide the most pessimistic memory barriers, so ideally these should be replaced with calls that use the right barriers. Does anyone have any thoughts about how this would compare with the current custom assembler implementations? Cheers, Phil.

Am Sunday 20 December 2009 20:16:03 schrieb Phil Endecott:
Dear All,
I have just had a quick look at how easily shared_ptr could be adapted to use Helge's proposed Boost.Atomic. To me it looks very simple, but maybe someone who knows more about all this could have a look? I took the non-threaded implementation in sp_counted_base_nt.hpp as a starting point, and simply changed the two counts from long to boost::atomic<long> :
boost::atomic<long> use_count_; // #shared boost::atomic<long> weak_count_; // #weak + (#shared != 0)
wouldn't "int" do?
Then, the majority of the methods don't need to change since the atomic template provides atomic versions of e.g. operator++. The one that does need more work is add_ref_lock, which I've implemented as follows:
bool add_ref_lock() // true on success { while (1) { long c = use_count_; if (c == 0) return false; if (use_count_.compare_exchange_weak(c,c+1)) return true; } }
I would suggest: bool add_ref_lock() // true on success { long c = use_count_.load(memory_order_relaxed); do { if (c == 0) return false; } while(!use_count_.compare_exchange_weak(c, c+1, memory_order_relaxed)); return true; } I have the nagging thought in my mind that the last one must be "memory_order_acquire" but I am too tired to really convince me either way :(
There is also the question of memory barriers. I understand that things like operator++ provide the most pessimistic memory barriers, so ideally these should be replaced with calls that use the right barriers.
I have some code in the "examples" section on this topic: http://www.chaoticmind.net/~hcb/projects/boost.atomic/doc/atomic/usage_examp...
Does anyone have any thoughts about how this would compare with the current custom assembler implementations?
For ppc and x86 it should end up mostly the same (not always same instructions, but the same effect both in functionality and performance), have not yet looked at the others. Best regards, Helge

Helge Bahmann wrote:
boost::atomic<long> use_count_; // #shared boost::atomic<long> weak_count_; // #weak + (#shared != 0)
wouldn't "int" do?
Yes it will. There aren't many 16 bit platforms left. :-)
I would suggest:
bool add_ref_lock() // true on success { long c = use_count_.load(memory_order_relaxed); do { if (c == 0) return false; } while(!use_count_.compare_exchange_weak(c, c+1, memory_order_relaxed)); return true; }
I have the nagging thought in my mind that the last one must be "memory_order_acquire" but I am too tired to really convince me either way :(
Relaxed is fine for add_ref. The decrements need to be acqrel. use_count() needs acquire.

On Mon, 21 Dec 2009, Peter Dimov wrote:
Relaxed is fine for add_ref. The decrements need to be acqrel. use_count() needs acquire.
I think that decrement can be: if (use_count_.fetch_sub(1, memory_order_release)==1) { atomic_thread_fence(memory_order_acquire); ... } i.e. save the acquire unless the object is going to be deleted. Helge

Helge Bahmann wrote:
On Mon, 21 Dec 2009, Peter Dimov wrote:
Relaxed is fine for add_ref. The decrements need to be acqrel. use_count() needs acquire.
I think that decrement can be:
if (use_count_.fetch_sub(1, memory_order_release)==1) { atomic_thread_fence(memory_order_acquire); ... }
It can, this is one of the motivating examples for atomic_thread_fence. :-)

"Phil Endecott" <spam_from_boost_dev@chezphil.org> wrote in message news:1261336563627@dmwebmail.dmwebmail.chezphil.org...
Dear All,
I have just had a quick look at how easily shared_ptr could be adapted to use Helge's proposed Boost.Atomic.
Apologies for perhaps going slightly offtopic/broadening the discussion, but wouldn't it be even better if the whole boost smart_ptr 'architecture' was refactored to extract/separate all of the orthogonal functionality/concepts that could then be used to build whatever specific smart pointers we like: - reference count: none, single threaded, multi threaded, COM ... - deleter: fixed, statically specified, dynamically specified - copy semantics: move, deep, shallow, shared, disabled/scoped - 'unused bits usage'/data 'compression'/injection': none, based on type alignement, platform... - empty/null pointer dereference handler: crash, assert, throw, return a static/global/default value/instance... - weak_ptr... ... It would also make it easier to merge the knowledge and functionality currently existing in the private shared_ptr's reference count implementation with boost::atomic and at the same time we would get a public/'official' and reusable boost reference count implementation... I'd volunteer to work/help on such a change. -- "That men do not learn very much from the lessons of history is the most important of all the lessons of history." Aldous Huxley
participants (4)
-
Domagoj Saric
-
Helge Bahmann
-
Peter Dimov
-
Phil Endecott