Trolltech Home | Qt-interest Home | Recent Threads | All Threads | Author | Date
All threads index page 1

Qt-interest Archive, August 2006
thread-safe read-function for a boolean without locking


Message 1 in thread

Hi,

I would like to add a property "dirty" to a class by having a 
read-function dirty() and a write function setDirty().
These property must be thread-safe (it gets read and written by 
different threads at the same time). Therefor I lock using a mutex if 
m_Dirty gets written by setDirty().

public:
	bool dirty() const
	{
		return m_Dirty;
	};
	void setDirty(const bool d)
	{
		m_DirtyMutex.lock();
		m_Dirty = d;
		m_DirtyMutex.unlock();
	};
private:
	bool m_Dirty;
	QMutex m_DirtyMutex;


I wonder if I have to lock in the read-function dirty(), too.
I do not want to lock in dirty() because than this function wouldn't be 
"const" anymore, because it would change the member-variable m_DirtyMutex.

In qt4/examples/threads/mandelbrot/renderthread.cpp line 90 I see, that 
you do not have to lock if you want to read a boolean. Is reading a 
boolean therefor atomic? -- or is even reading any member atomic?

Thanks for any help,
Greetings
Jens

--
 [ signature omitted ] 

Message 2 in thread

On Thursday 03 August 2006 11:56, Jens wrote:
> Hi,
>
> I would like to add a property "dirty" to a class by having a
> read-function dirty() and a write function setDirty().
> These property must be thread-safe (it gets read and written by
> different threads at the same time). Therefor I lock using a mutex if
> m_Dirty gets written by setDirty().
>
> public:
> 	bool dirty() const
> 	{
> 		return m_Dirty;
> 	};
> 	void setDirty(const bool d)
> 	{
> 		m_DirtyMutex.lock();
> 		m_Dirty = d;
> 		m_DirtyMutex.unlock();
> 	};
> private:
> 	bool m_Dirty;
> 	QMutex m_DirtyMutex;
>
>
> I wonder if I have to lock in the read-function dirty(), too.
> I do not want to lock in dirty() because than this function wouldn't be
> "const" anymore, because it would change the member-variable m_DirtyMutex.
>
> In qt4/examples/threads/mandelbrot/renderthread.cpp line 90 I see, that
> you do not have to lock if you want to read a boolean. Is reading a
> boolean therefor atomic? -- or is even reading any member atomic?
You can mark m_DirtyMutex to be mutable, then you can do it in a const method.

Cheers
	-ian reinhart geiser

--
 [ signature omitted ] 

Message 3 in thread

Jens said:
> 	bool m_Dirty;

This should be "volatile bool", so that it is not left in registers for
too long.

> I wonder if I have to lock in the read-function dirty(), too.
> I do not want to lock in dirty() because than this function wouldn't be
> "const" anymore, because it would change the member-variable m_DirtyMutex.

I fear you even designed it wrong - if you base any decisions on dirty()
that could in fact affect this flag again, then no amount of locking
inside it will help you:

For every action which affects this variable you have to lock before the
first read/write and unlock after the last read/write. So if your
algorithm looks like:

if(x.dirty()){
 x.makeBackup();
 x.setDirty(false);
}

it will fail spectacularly. It should actually be:

x.makeBackupIfDirty();

with this method looking like:

void X::makeBackupIfDirty()
{
   QMutexLocker mlock(&mymutex);
   if(m_dirty)
     makeBackup();
   m_dirty=false;
}

(QMutexLocker sets the lock where it is instantiated and clears it on
leaving its range of visibility - ie. it will lock on entering the method
and unlock on leaving it)


    Konrad

--
 [ signature omitted ] 

Message 4 in thread

Hi Konrad, Hi Ian, Hi anybody else,

I have classes which only hold data and I have classes which interact 
with these data-classes through read- and write-functions.
Therefor I do not want to start mixing these classes.

But you are right - I still need to think about a way of locking all 
write-functions of my data-class before saving the data to disk.
I thought of making a copy of my data-classes first. And after that 
saving the data of the copy to disk using a different thread.

It could look like this:

	MyData* copy = data.createCopyIfDirtyToSave();
	if(copy) {
	   saveToDisk(copy);
	}

The createCopyIfDirtyToSave()-function sets all threads to in wait 
condition which call write-functions while the copying is done. After 
that m_Dirty becomes false and all threads waiting in a write-function 
get woken up.

Would that work?

Greetings
Jens

P.S:
I do not get, why a member must be "volatile" in my case.
-------------------------------------
class MyData : public QObject
{
Q_OBJECT
.....
public:
     MyData* createCopyIfDirtyToSave() {
	QMutexLocker locker(&m_CopyMutex);
	MyData* copy = 0;
	m_Copying = true;
	if(dirty()) {
         	MyData* copy = new MyData();
		..fill copy with all data of this class..
		m_Dirty = false;
	}
         m_Copying = false;
	m_WritingAllowed.wakeAll();
     }

     void writeDataA(int data)
     {
         if(m_Copying) m_WritingAllowed.wait();
         QWriteLock locker(&m_DataALock);
	m_DataA = data;
	setDirty(true);
     }

     int readDataA() const
     {
         QReadLock locker(&m_DataALock);
         return m_DataA;
     }

     bool dirty() const
     {
	QReadLock locker(&m_DirtyLock);
         return m_Dirty;
     };

     void setDirty(const bool d)
     {
	if(m_Copying) m_WritingAllowed.wait();
        	QWriteLock locker(&m_DirtyLock);
         m_Dirty = d;
     };

private:
     bool m_Copying;
     QMutex m_CopyMutex;
     QWaitCondition m_WritingAllowed;

     int m_DataA;
     mutable QReadWriteLock m_DataALock;

     bool m_Dirty;
     mutable QReadWriteLock m_DirtyLock;
}





Konrad Rosenbaum schrieb:
> Jens said:
>> 	bool m_Dirty;
> 
> This should be "volatile bool", so that it is not left in registers for
> too long.
> 
>> I wonder if I have to lock in the read-function dirty(), too.
>> I do not want to lock in dirty() because than this function wouldn't be
>> "const" anymore, because it would change the member-variable m_DirtyMutex.
> 
> I fear you even designed it wrong - if you base any decisions on dirty()
> that could in fact affect this flag again, then no amount of locking
> inside it will help you:
> 
> For every action which affects this variable you have to lock before the
> first read/write and unlock after the last read/write. So if your
> algorithm looks like:
> 
> if(x.dirty()){
>  x.makeBackup();
>  x.setDirty(false);
> }
> 
> it will fail spectacularly. It should actually be:
> 
> x.makeBackupIfDirty();
> 
> with this method looking like:
> 
> void X::makeBackupIfDirty()
> {
>    QMutexLocker mlock(&mymutex);
>    if(m_dirty)
>      makeBackup();
>    m_dirty=false;
> }
> 
> (QMutexLocker sets the lock where it is instantiated and clears it on
> leaving its range of visibility - ie. it will lock on entering the method
> and unlock on leaving it)
> 
> 
>     Konrad
> 
> --
> To unsubscribe - send a mail to qt-interest-request@xxxxxxxxxxxxx with "unsubscribe" in the subject or the body.
> List archive and information: http://lists.trolltech.com/qt-interest/
> 
> 

--
 [ signature omitted ]