Copying big objects between threads


#1

Providing big chunks (maybe 200Mb) of audio sample data, loaded in one thread, to the audio-thread. Lock-free.  Thought is to create a special reference counted object. Pop a pointer to it into a lock-free queue to the audio thread.  When the objects reference count reaches zero pop it back in a queue to the non-critical thread for deletion. 

However. Is there a problem where the audio thread might see an incomplete object because it's got different memory data in the cache, or something has happened out of order.  Do I therefore also  need something like atomic_thread_fence*?

Is there some other way of doing it?

 


#2

Yeah, that sounds sensible - ReferenceCountedObjectPointer or SharedResourcePointer are good thread-safe ways of managing shared read-only data.


#3

But not lock-free?  This would just be a fancier ReferenceCountedObjectPointer where the deletes all happen on the message thread.  Though I'm starting to think it'd easier to hack it so the message thread just kept holding a reference until the audio thread signaled it was finished with it..

Anyway - Just sitting through an hour of Herb Sutter talking about this stuff. Then I'm going to look into the tea-leaves. If it still seems like a good idea afterwards I'm starting typing ... 


#4

Oh, I see what you mean. Yes, I've written a few classes like that where you have a manager object with a list of ref-counted pointers, and which uses a timer to occasionally clean-up any of its objects which have a ref-count of 1.


#5

I'll pop it up when I've done it.  I think it'll be a generally very useful object.  It's a bit like the timer, only it'll be designed just to ensure that the memory deallocation always happens on a safe thread. 

PS. This is an absolutely brilliant talk on C++ concurrency:

http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-1-of-2

And 38 minutes in I discovered that I don't need to do any more fancy stuff.  Because the FIFO code uses an atomic variable, it performs a release and so the audio thread will always see good data.   

 


#6

I've been looking at this off and on for a while now.  And I've got some working but slightly ugly code for ReferenceCountedObjects which does exactly the right thing and passes all the tests I can think of.

However - I was just looking at extending this into maintaining a one way synchronisation of a ValueTree to another thread, and it's not easy.  Making changes to a ValueTree is not a lock-free operation.  So I can't just send updates as they happen and have the critical thread apply them with the normal methods.

So I figured it might be good (for small trees at least) to just create a deep copy of the ValueTree if anything changes and pass it over. But addChild() is also not lock-free!

I think my solution for now is just to always copy the whole ValueTree over each time, so there's no adding or removing anything. 

However feature requests:

- it'd be nice to just update a single property value that's changed in the clone of the value tree. That'd be a lot easier if I could get a pointer to the underlying value tree object to use in a std::map ... 

- If ValueTree::addChild could be made lock-free then this would be useful too and I think could get away with copying less of the tree each time. But that requires ReferenceCountedArray's add() method to not allocate any memory.  This could be fixed by specifying the number of children to reserve space for when the tree is created. 

Or is there some other neat trick I've missed that makes this all much easier. 

 


#7

Ah - I've just spotted what you mean here.  I was doing this: 

http://pastebin.com/S4axfeEB

Where there is no list or manager class. The objects are entirely owned by the audiothread until their reference count == 0.  At which point they are sent home for deleting. 

You mean that the sender keeps a handle for what it's sent over, and then when the count goes back down to one it can delete it.  Which I think is easier. 

I had some silly ambition earlier to get rid of the thread-safety and use a SingleThreadedReferenceCountedObject - which is how I went down this delete message passing path.  But I think your solution might be neater, and the performance improvement from not having to do atomic reference counting (and the coding of making it work) isn't worth it.  

On the other hand, my solution works with hardly any modifications for non-reference counted objects... which might be neat.  I could have a ScopedAudioThreadPointer<> to hold them which generated the deletion message.