[Interest] QueuedConnection, QByteArray, reserve, capacity

Thiago Macieira thiago.macieira at intel.com
Wed May 31 18:10:09 CEST 2017


On Wednesday, 31 May 2017 02:15:47 PDT qtlist at wadefalk.se wrote:
> 	First of all, i think reserve() seems not to be reliable for keeping the
> promise of the container's size and memory buffer integrity even under
> normal simple conditions. After having reserved a certain size and then
> modifying the actual length of the QByteArray using resize() to a size LESS
> or EQUAL to the size of the capacity() would unpredictably (not always)
> have the array to reallocate its data and again reducing the capacity. This
> is in my world a breakage of the semantics in the container behavior.
> Expecting the QByteArray::data() not to change the actual memory location
> is then completely false. One of the expectations of the reserve() call is
> to guarantee the container not to reallocate itself unless absolutely
> needed.

It you shrink too much, then it releases the memory. (where "too much" may be 
"less than half", I'd have to check the sources)

> 	But one thing about this that is even worse. I discovered having a set
> capacity of a QByteArray to something and then passing that QByteArray as
> const reference to a signal over a QueuedConnection actually modified the
> capacity() ! (and indeed reallocated the array). The violation of rules in
> this case I feel is even worse and I suspect the moc code does something
> strange to make this happen. I have read the moc code is supposed to take a
> copy of the array and pass that copy to the other thread (this is nice of
> course), but how can that result in the original container in the signal
> emitting thread being reallocated and having its capacity modified? Here we
> are even talking about breakage of const promise.

The signal-slot system treats const references the same as passing by value. 
So when you have a queued connection, the system will actually create a copy 
behind your back and queue that to the other thread. Please note that this 
needs to happen, since the slot is called asynchronously from the emission: 
this is the only way to guarantee that the reference that the slot gets is 
valid, with no data race issues.

That is also why you cannot pass non-const references via queued connections. 
You can pass pointers (the pointer is copied and the pointed object is your 
problem).

I *think* we wrote the optimisation that a BlockingQueuedConnection does not 
copy, but again I'd have to check the sources.

> 	If I'm right this is totally unexpected behavior and it should be filed as
> a serious bug I think. Here is a simple example.

Not a bug. This is expected and required behaviour.

> 
> 	 #ifndef ASYNCARRAY_H #define ASYNCARRAY_H #include  class ASyncArray :
> public QObject { Q_OBJECT public: explicit ASyncArray(QObject *parent =
> nullptr); public slots: void testSlot(const QByteArray& array); signals:
> void testSignal(const QByteArray& array); public slots: }; #endif //
> ASYNCARRAY_H #include  #include "asyncarray.h"
> ASyncArray::ASyncArray(QObject *parent) : QObject(parent) { connect(this,
> &ASyncArray::testSignal, this, &ASyncArray::testSlot,
> Qt::QueuedConnection); QByteArray testArray; testArray.reserve(100);
> testArray.resize(50); memset(testArray.data(), 0xaa, testArray.capacity());
> qDebug()

Line breaks anywhere, please? Anyway, not relevant.
-- 
Thiago Macieira - thiago.macieira (AT) intel.com
  Software Architect - Intel Open Source Technology Center




More information about the Interest mailing list