Неверный размер записи 4 с классом матрицы (с использованием valgrind) - PullRequest
1 голос
/ 16 августа 2011

У меня есть простой матричный класс, который я создал для использования с opengl (es 2.0, afaik нет встроенной версии для этой конкретной версии opengl).

Это в основном не более чемвектор, размер которого в конструкторе содержит 16 чисел с плавающей запятой (я выбрал вектор из всего лишь массива с 16 поплавками для имеющегося у него оператора =) и некоторые вспомогательные функции, которые полезны в контексте opengl.В любом случае, одна из этих вспомогательных структур загружает единичную матрицу в вектор:

void Matrix::loadIdentity()
{
    matrix.resize(16, 0);
    for(int i = 0; i < 16; i++)
        matrix[i] = 0.0f;
    for(int i = 0; i < 4; i++) {
        matrix[i * 4 + i] = 1.0f;
    }
}

Где матрица:

std::vector<float>

Строка, которая говорит:

matrix.resize(16, 0);

предназначен для того, чтобы убедиться, что размер на самом деле составляет 16 операций с плавающей запятой, хотя это также сделано в конструкторе, и я не верю, что в конечном счете это понадобится, но сейчас, чтобы устранить одну возможную ошибку, чтобы упростить жизньдля отладки.

Valgrind говорит мне, что эта функция, в частности эта строка:

matrix[i] = 0.0f;

вызывает неправильный размер записи 4, что подозрительно звучит так, как будто я как-то вызываю матрицу[16] = 0,0f ;.Однако я должен перейти только от 0 до 15. Интересно отметить, что valgrind также говорит, что рассматриваемая память, в которую записывается, была выделена деструктором матрицы и, более конкретно, деструктором вектора.

Address 0x150f8280 is 0 bytes inside a block of size 64 free'd  1: operator delete(void*) in /build/buildd/valgrind-3.6.1/coregrind/m_replacemalloc/vg_replace_malloc.c:387
  2: __gnu_cxx::new_allocator&lt;float&gt;::deallocate(float*, unsigned long) in <a href="file:///usr/include/c++/4.5/ext/new_allocator.h:95" >/usr/include/c++/4.5/ext/new_allocator.h:95</a>
  3: std::_Vector_base&lt;float, std::allocator&lt;float&gt; &gt;::_M_deallocate(float*, unsigned long) in <a href="file:///usr/include/c++/4.5/bits/stl_vector.h:146" >/usr/include/c++/4.5/bits/stl_vector.h:146</a>
  4: std::_Vector_base&lt;float, std::allocator&lt;float&gt; &gt;::~_Vector_base() in <a href="file:///usr/include/c++/4.5/bits/stl_vector.h:132" >/usr/include/c++/4.5/bits/stl_vector.h:132</a>
  5: std::vector&lt;float, std::allocator&lt;float&gt; &gt;::~vector() in <a href="file:///usr/include/c++/4.5/bits/stl_vector.h:314" >/usr/include/c++/4.5/bits/stl_vector.h:314</a>
  6: Matrix::~Matrix() in <a href="file:///home/leif/MarbleMachine/core/matrix.h:6" >core/matrix.h:6</a>

Это кажется маловероятным, если это просто какой-то индекс из-за ошибки диапазона, поскольку, на самом деле, векторный объект - это прежде всего указатель на то, где массив находится в памяти (ну, есть другие вещитоже, но я пытаюсь подчеркнуть, что сам векторный объект (где будет храниться деструктор), кажется маловероятным, что он будет непрерывным в памяти с тем, где хранится фактический массив.Зачем деструктору требовать память, если все дело в том, чтобы высвободить память, которую использует объект, не приведет ли это просто к утечке памяти? Кстати, у класса матрицы нет явного деструктора, поэтому только неявный

Наконец, я знаю, что предыдущие ошибки в valgrind могут вызыватьБудущие ошибки, но единственными предыдущими ошибками являются недопустимый размер чтения и использование неинициализированных переменных внутри существующих библиотек (OpenGL и SDL), поэтому я считаю крайне маловероятным, что они вызывают повреждение кучи до того, как это произойдет.

Спасибо.

Ответы [ 2 ]

1 голос
/ 16 августа 2011

Из фрагмента отчета Valgrind, который вы опубликовали, довольно ясно видно, что вы получаете доступ к висячему хранилищу для вектора.

Такой доступ может легко возникнуть, если один поток выполняет итерацию по вектору, в то время как другой поток изменил его размер. Например, это небезопасно:

for (vector<float>::iterator it = matrix.begin(); it != matrix.end(); ++it)
  *it = 0;

в другой теме:

matrix.resize(32, 0);

Если у вас есть мутаторы, которые могут изменять размер вектора, вы должны сериализовать всех читателей по отношению к ним, например,

pthread_mutex_lock(&matrix_lock);
for (vector<float>::iterator it = matrix.begin(); it != matrix.end(); ++it)
  *it = 0;
pthread_mutex_unlock(&matrix_lock);

В другой теме:

pthread_mutex_lock(&matrix_lock);
matrix.resize(32, 0);
pthread_mutex_unlock(&matrix_lock);

То, что вы описали в своем ответе:

1 Графический поток получает matrix.end() вне блокировки [что уже является проблемой].
...
6 графических резьбовых замков
7 Графический поток получает matrix.begin() и начинает перебирать

, по-видимому, не согласуется с отчетом об ошибках Valgrind, потому что это привело бы к доступу за концом массива, тогда как Valgrind сообщает о доступе в начале (теперь удаленного / висячего массива).

Итог: вызов begin(), end(), итерация должна происходить в пределах блокировки, защищающей контейнер; в противном случае поведение вашей программы будет неопределенным.

0 голосов
/ 16 августа 2011

Спасибо за помощь, думаю, я наконец понял это. На самом деле оказалось, что я не дал вам достаточно информации для решения проблемы, поэтому прежде чем дать вам решение, которое я нашел, позвольте мне дать вам больше информации.

Приложение представляло собой многопоточное приложение, в каждом потоке у которого есть часть, в которой оно перебирает цикл объектов (тех же объектов), в которых содержится матрица. В одном случае физический движок, а в другом - рендеринг объектов. Я блокировал данные в обоих потоках, когда это было необходимо, но оказалось, что перед блокировкой я получил конец итератора.

Итак, я думаю, что это происходило так:

  1. Графический поток определил конечный итератор массива вершин.
  2. Физический поток заблокировал данные.
  3. Прошел физический поток и из-за столкновения добавил в него новые предметы.
  4. Новый размер массива будет больше, чем его емкость, поэтому его емкость удваивается, что приводит к перемещению всех данных и аннулированию всех итераторов. (Я знаю, что они всегда становятся недействительными при добавлении данных, но это не имеет побочных эффектов, если только местоположение данных не меняется, аааик).
  5. Поток физических данных разблокирован.
  6. Графический поток заблокировал данные.
  7. Графический поток нашел начальный итератор с правильным расположением, но поскольку цикл for сравнил его с итератором, найденным в элементе № 1, он уходит из конца массива, переворачивая недопустимые данные (связанные с настройкой матрицы элемента для рисунок).

Спасибо всем за помощь.

...