[Qt-interest] How to "snapshot" a QObject if it can't be copied?
Hostile Fork
hostilefork at gmail.com
Fri Oct 16 21:01:54 CEST 2009
Hostile Fork wrote:
> I have a QObject-derived class. It has signals and slots that modify
> the object's data in a multithreaded environment. (Yeah, yeah, who
> doesn't?) :)
>
> I'd like to be able to snapshot that object. Because certain routines
> would be harmed by getting different answers from method calls on this
> QObject (if modifications happen on the other thread while they are
> working). So routines have a QObject pointer, they call snapshot(),
> then they make all their method calls on the snapshot. That way they
> don't see the modifications.
David Walthall wrote:
> It sounds like what you actually need is a mutex. If you need an
> object
> to be unchanged during an operation, use a mutex to protect the
> object.
> In fact, if you can modify an object in one thread while accessing it
> in another thread, you need a mutex.
Hi David,
I'm afraid that holding a mutex would block CPU cores for too long in
my case, so I need a snapshot scheme. I want to only pay for the copy
at the moment a "writer" happens to run between the reader's
snapshot() call and the destruction of the snapshot. (If no writes
occur during that time, the snapshot will cost almost nothing.)
QSharedDataPointer implements exactly this pattern, which you also
find in types like QBitmap and QString. It lets you pass large
objects around by value, but only make a copy of the actual memory if
a non-const method of the object dereferences the shared data block.
It's thread-safe behind the scenes, and no mutex is needed:
http://labs.trolltech.com/blogs/2009/08/25/count-with-me-how-many-smart-pointer-classes-does-qt-have/
You can put an implicitly shared member inside a QObject, but it can't
be implicitly shared itself (for the simple reason that it can't be
copied by value). So that's one thing I was trying to work around; to
figure out a good way to apply this to a QObject.
It's tough because I'm trying to define a QObject base class which
people derive off and more or less get this behavior "for free" if
they follow a couple of rules. Not sure how to best form the classes
and those rules... gets very awkward quickly!
Thanks,
---Brian
> template< class DerivedDataType >
> class Base
> {
> // This is the base class and you make all your data
> // access methods derive from it, they should all
> // be const because if you modify it won't be reflected
> // back to the BaseHolder!
>
> protected:
> // Note: DerivedDataType should inherit from QSharedData or obey
> // the shared data protocol
> QSharedDataPointer< DerivedDataType > d;
>
> public:
> Base (unique_ptr< DerivedDataType >&& init)
> d (move(init))
> {
> }
>
> Base (const Base &other)
> : d (other.d)
> {
> }
>
> ~Base()
> {
> // NOT virtual, to reinforce that you are absolutely
> // not supposed to be putting data members in
> // derived classes. Put them in the DerivedDataType
> // class! otherwise how will they make it into the
> // snapshot?
> }
> };
>
> template< class DerivedDataType >
> class BaseHolder : public QObject
> {
> // This is the QObject class and its slots can modify
> // the state. some methods const and some non-const
>
> protected:
> Base< DerivedDataType > state;
>
> public:
> BaseHolder (unique_ptr< DerivedDataType >&& init) :
> Base(move(init))
> {
> }
>
> BaseHolder (const Base &other)
> : d (other.d)
> {
> }
>
> Base< DerivedDataType > snapshot()
> {
> // returns an object that is semantically equivalent
> // to making a copy of the current state, but which
> // defers the actual act of copying until a write occurs
> return state;
> }
>
> virtual ~BaseHolder()
> {
> // This is virtual to indicate that it is okay to clean up
> // any events or signals in your derived destructor.
> // There's only one QObject and if you have mutexes
> // or HttpRequests or whatever then that's fine to
> // clean up here. But remember that clients
> // won't work w/pointers to BaseHolder... just instances
> // of Base... so if something is part of the data interface
> // make it a method on Base which is a function
> // of DerivedDataType
> }
> };
http://hostilefork.com
More information about the Qt-interest-old
mailing list