[Development] What am I not seeing in the QByteArray::capacity test failure?

Thiago Macieira thiago.macieira at intel.com
Wed Apr 23 02:45:08 CEST 2014


  FAIL!  : tst_QByteArray::reserve() 'qba.constData() != data' returned FALSE. 
  tst_qbytearray.cpp(1871) : failure location

http://testresults.qt-project.org/ci/QtBase_stable_Integration/build_03694/win32-msvc2010_Windows_7/log.txt.gz

Code around the location is:

  1858      qba.resize(capacity);
[...]
  1863      QCOMPARE(qba.capacity(), capacity);
[...]
  1866      copy = qba;
  1867      qba.reserve(capacity * 2);
  1868      QCOMPARE(qba.size(), capacity);
  1869      QCOMPARE(qba.capacity(), capacity * 2);
  1870      QCOMPARE(copy.capacity(), capacity);
  1871      QVERIFY(qba.constData() != data);

On line 1866, we make a copy, so qba's refcount should now be 2.

When we do reserve(capacity * 2) on line 1867, the byte array is supposed to 
detach and reallocate to a larger size:

   462  inline void QByteArray::reserve(int asize)
   463  {
   464      if (d->ref.isShared() || uint(asize) + 1u > d->alloc) {
   465          reallocData(qMax(uint(size()), uint(asize)) + 1u, d-
>detachFlags() | Data::CapacityReserved);
   466      } else {
   467          // cannot set unconditionally, since d could be the 
shared_null or
   468          // otherwise static
   469          d->capacityReserved = true;
   470      }
   471  }

We know it did not detach, since the test failed because the pointer value is 
still the same. It could be for one of two reasons:
 1) both conditions of the if on line 464 are false
 2) one of them is true, but reallocData() did not actually detach

We know that asize == capacity*2 is larger than d->alloc because of the test 
on line 1863 above: capacity() returns d->alloc - 1 and we know qba.capacity() 
== capacity == 100. That rules out condition #1: one of the branches of the if 
is true.

Note that we also expect d->ref.isShared() to also be true.

If it's case #2, then reallocData did not detach:

  1495  void QByteArray::reallocData(uint alloc, Data::AllocationOptions 
options)
  1496  {
  1497      if (d->ref.isShared() || IS_RAW_DATA(d)) {
  1498          Data *x = Data::allocate(alloc, options);
  1499          Q_CHECK_PTR(x);
  1500          x->size = qMin(int(alloc) - 1, d->size);
  1501          ::memcpy(x->data(), d->data(), x->size);
  1502          x->data()[x->size] = '\0';
  1503          if (!d->ref.deref())
  1504              Data::deallocate(d);
  1505          d = x;
  1506      } else {
  1507          if (options & Data::Grow)
  1508              alloc = qAllocMore(alloc, sizeof(Data));
  1509          Data *x = static_cast<Data *>(::realloc(d, sizeof(Data) + 
alloc));
  1510          Q_CHECK_PTR(x);
  1511          x->alloc = alloc;
  1512          x->capacityReserved = (options & Data::CapacityReserved) ? 1 : 
0;
  1513          d = x;
  1514      }
  1515  }

Now, for the test to be failing, we must have hit the realloc on line 1509. 
That leads me to conclude that d->ref.isShared() actually returned false, 
despite there being an implicitly shared copy.

Could there be a bug in operator=?

   937  QByteArray &QByteArray::operator=(const QByteArray & other)
   938  {
   939      other.d->ref.ref();
   940      if (!d->ref.deref())
   941          Data::deallocate(d);
   942      d = other.d;
   943      return *this;
   944  }

Note that due to construction of the testcase, at this point 
this->d == other.d. In any case, we do increment the refcount, so it should 
have gone up to 2 or 3.

QByteArray does have a move-assignment operator:

   202      inline QByteArray &operator=(QByteArray &&other)
   203      { qSwap(d, other.d); return *this; }

But even if MSVC 2010 did erroneously call this operator, the refcount already 
was 2.

Am I missing something here? Because I can't explain the issue. And I can't 
reproduce it on Linux either.

-- 
Thiago Macieira - thiago.macieira (AT) intel.com
  Software Architect - Intel Open Source Technology Center




More information about the Development mailing list