Конечно, здесь есть некоторые утечки памяти, но более насущная проблема - это множественные случаи чтения / записи после окончания выделенной памяти и двойного освобождения.
Давайте рассмотрим ихвремя, начинающееся сверху:
==4725== Invalid write of size 8
==4725== at 0x109505: createPartitions (counter.c:215)
==4725== by 0x109550: createThreads (counter.c:238)
==4725== by 0x10973A: run (counter.c:312)
==4725== by 0x10985C: main (counter.c:367)
==4725== Address 0x4a61698 is 0 bytes after a block of size 8 alloc'd
==4725== at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725== by 0x10948F: createPartitions (counter.c:195)
==4725== by 0x109550: createThreads (counter.c:238)
==4725== by 0x10973A: run (counter.c:312)
==4725== by 0x10985C: main (counter.c:367)
==4725==
Это жалоба на запись того, что было выделено.Вот код, который выполняет как распределение, так и недопустимый доступ:
wrapper *partitions = (wrapper *)malloc(sizeof(wrapper));
for (int i = 0; i < numberOfThreads; i++)
{
if (i < numberOfThreads - remainder)
{
partitions[i] = createNormalPartition(last, quotient);
last += quotient;
continue;
}
wrapper temp = createOverloadedPartition(last, quotient);
partitions[i] = temp;
last += quotient + 1;
}
Вы выделяете пространство для одного экземпляра wrapper
, но записываете его так, как если бы оно быломассив numberOfThreads
экземпляров.Вам нужно выделить место для этого множества экземпляров:
wrapper *partitions = malloc(sizeof(wrapper) * numberOfThreads);
Следующий:
==4725== Invalid write of size 8
==4725== at 0x48812C8: pthread_create@@GLIBC_2.2.5 (in /usr/lib/libpthread-2.28.so)
==4725== by 0x1095A8: createThreads (counter.c:248)
==4725== by 0x10973A: run (counter.c:312)
==4725== by 0x10985C: main (counter.c:367)
==4725== Address 0x4a61648 is 0 bytes after a block of size 8 alloc'd
==4725== at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725== by 0x10972A: run (counter.c:305)
==4725== by 0x10985C: main (counter.c:367)
==4725==
==4725== Invalid read of size 8
==4725== at 0x1095BD: createThreads (counter.c:249)
==4725== by 0x10973A: run (counter.c:312)
==4725== by 0x10985C: main (counter.c:367)
==4725== Address 0x4a61648 is 0 bytes after a block of size 8 alloc'd
==4725== at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725== by 0x10972A: run (counter.c:305)
==4725== by 0x10985C: main (counter.c:367)
==4725==
Другая недопустимая запись вместе с недопустимым чтением.Вот распределение в run
:
pthread_t *threads = (pthread_t *)malloc(sizeof(pthread_t *));
И чтение / запись в createThreads
:
for (int i = 0; i < numberOfThreads; i++)
{
pthread_create(&threads[i], NULL, sum_runner, (void *)&partitions[i]);
printf(" Created thread %lu\n", threads[i]);
}
Как и раньше, вы выделяете пространство для сингла экземпляр, а не массив.Кроме того, вы выделяете место для pthread_t *
вместо pthread_t
, который, вероятно, будет слишком маленьким.Измените выделение, чтобы освободить место для массива, и используйте тип объекта, а не тип указателя:
pthread_t *threads = malloc(sizeof(pthread_t) * numberOfThreads);
Далее:
==4725== Invalid read of size 4
==4725== at 0x1091F1: sum_runner (counter.c:52)
==4725== by 0x4880A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==4725== by 0x4995B22: clone (in /usr/lib/libc-2.28.so)
==4725== Address 0x4a61698 is 0 bytes after a block of size 8 alloc'd
==4725== at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725== by 0x10948F: createPartitions (counter.c:195)
==4725== by 0x109550: createThreads (counter.c:238)
==4725== by 0x10973A: run (counter.c:312)
==4725== by 0x10985C: main (counter.c:367)
==4725==
==4725== Invalid read of size 4
==4725== at 0x1091FA: sum_runner (counter.c:53)
==4725== by 0x4880A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==4725== by 0x4995B22: clone (in /usr/lib/libc-2.28.so)
==4725== Address 0x4a6169c is 4 bytes after a block of size 8 alloc'd
==4725== at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725== by 0x10948F: createPartitions (counter.c:195)
==4725== by 0x109550: createThreads (counter.c:238)
==4725== by 0x10973A: run (counter.c:312)
==4725== by 0x10985C: main (counter.c:367)
==4725==
Пара недопустимых операций чтения, происходящих из одного и того жеразмещение и доступ в аджентных линиях.Это то же недействительное распределение из первого сообщения, которое мы уже рассмотрели.Следующий:
==4725== Thread 1:
==4725== Invalid read of size 8
==4725== at 0x10966B: startThreads (counter.c:278)
==4725== by 0x109746: run (counter.c:318)
==4725== by 0x10985C: main (counter.c:367)
==4725== Address 0x4a61648 is 0 bytes after a block of size 8 alloc'd
==4725== at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725== by 0x10972A: run (counter.c:305)
==4725== by 0x10985C: main (counter.c:367)
==4725==
Starting thread 98969344
==4725== Invalid read of size 8
==4725== at 0x109696: startThreads (counter.c:279)
==4725== by 0x109746: run (counter.c:318)
==4725== by 0x10985C: main (counter.c:367)
==4725== Address 0x4a61648 is 0 bytes after a block of size 8 alloc'd
==4725== at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725== by 0x10972A: run (counter.c:305)
==4725== by 0x10985C: main (counter.c:367)
==4725==
Пара недопустимых операций чтения, указывающих на то же неправильное распределение из второго сообщения.Об этом тоже уже говорилось.Далее:
==4725== Invalid write of size 4
==4725== at 0x109777: run (counter.c:331)
==4725== by 0x10985C: main (counter.c:367)
==4725== Address 0x4a62508 is 0 bytes after a block of size 8 alloc'd
==4725== at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725== by 0x109768: run (counter.c:330)
==4725== by 0x10985C: main (counter.c:367)
==4725==
==4725== Thread 2:
==4725== Invalid read of size 4
==4725== at 0x10926E: results_sum_runner (counter.c:87)
==4725== by 0x4880A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==4725== by 0x4995B22: clone (in /usr/lib/libc-2.28.so)
==4725== Address 0x4a62508 is 0 bytes after a block of size 8 alloc'd
==4725== at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725== by 0x109768: run (counter.c:330)
==4725== by 0x10985C: main (counter.c:367)
Недопустимое чтение и запись, происходящие из одного и того же размещения.Вот распределение в run
:
resultsWrapper *resultData = (resultsWrapper *)malloc(sizeof(resultsWrapper *));
Чтение в results_sum_runner
:
resultsWrapper *results = (resultsWrapper *)args;
int size = results->size;
И запись в run
:
resultData->size = numberOfThreads;
Здесь вы выделяете пространство для указателя на resultsWrapper
, а не экземпляра из resultsWrapper
.Исправьте распределение следующим образом:
resultsWrapper *resultData = malloc(sizeof(resultsWrapper));
Следующее:
==4725== Invalid free() / delete / delete[] / realloc()
==4725== at 0x48389AB: free (vg_replace_malloc.c:530)
==4725== by 0x1097CE: run (counter.c:346)
==4725== by 0x10985C: main (counter.c:367)
==4725== Address 0x4a62500 is 0 bytes inside a block of size 8 free'd
==4725== at 0x48389AB: free (vg_replace_malloc.c:530)
==4725== by 0x1092DB: results_sum_runner (counter.c:101)
==4725== by 0x4880A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==4725== by 0x4995B22: clone (in /usr/lib/libc-2.28.so)
==4725== Block was alloc'd at
==4725== at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725== by 0x109768: run (counter.c:330)
==4725== by 0x10985C: main (counter.c:367)
==4725==
Вот двойное освобождение.Выделение и второй free
находятся в run
:
resultsWrapper *resultData = (resultsWrapper *)malloc(sizeof(resultsWrapper *));
resultData->size = numberOfThreads;
resultData->array = results;
pthread_create(&summingThread, NULL, results_sum_runner, (void *)resultData);
pthread_join(summingThread, (void *)&finalResult);
free(threads);
free(resultData);
free(results);
И вот первое free
в results_sum_runner
:
void *results_sum_runner(void *args)
{
resultsWrapper *results = (resultsWrapper *)args;
int size = results->size;
int *array = results->array;
int *sum = (int *)malloc(sizeof(int *));
*sum = 0;
for (int i = 0; i < size; i++)
{
*sum += array[i];
}
free(results);
free(array);
pthread_exit((void *)sum);
}
Здесь вы выделяете память дляresultData и передача его в функцию потока results_sum_runner
.Этот поток освобождает память, но затем - и вызывающий поток.Удалите либо free(resultData)
в run
или free(results)
в results_sum_runner
.
Последний:
==4725== Invalid free() / delete / delete[] / realloc()
==4725== at 0x48389AB: free (vg_replace_malloc.c:530)
==4725== by 0x1097DA: run (counter.c:347)
==4725== by 0x10985C: main (counter.c:367)
==4725== Address 0x4a61b20 is 0 bytes inside a block of size 24 free'd
==4725== at 0x48389AB: free (vg_replace_malloc.c:530)
==4725== by 0x1092E7: results_sum_runner (counter.c:102)
==4725== by 0x4880A9C: start_thread (in /usr/lib/libpthread-2.28.so)
==4725== by 0x4995B22: clone (in /usr/lib/libc-2.28.so)
==4725== Block was alloc'd at
==4725== at 0x483777F: malloc (vg_replace_malloc.c:299)
==4725== by 0x109638: startThreads (counter.c:269)
==4725== by 0x109746: run (counter.c:318)
==4725== by 0x10985C: main (counter.c:367)
==4725==
Еще один двойной аналог, похожий на последний.free(results)
в run
и free(array)
в results_sum_runner
относятся к одной и той же памяти, поэтому избавьтесь от одного из них.
Теперь, если мы скомпилируем эти изменения и снова запустим под valgrind, мы получимЕще одна проблема:
==24305== Invalid read of size 4
==24305== at 0x40090E: sum_runner (x1.c:52)
==24305== by 0x4E416B9: start_thread (pthread_create.c:333)
==24305== Address 0x54216a8 is 8 bytes inside a block of size 24 free'd
==24305== at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24305== by 0x400CEF: createThreads (x1.c:251)
==24305== by 0x400E3C: run (x1.c:312)
==24305== by 0x400F5C: main (x1.c:367)
==24305== Block was alloc'd at
==24305== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24305== by 0x400B98: createPartitions (x1.c:195)
==24305== by 0x400C57: createThreads (x1.c:238)
==24305== by 0x400E3C: run (x1.c:312)
==24305== by 0x400F5C: main (x1.c:367)
==24305==
==24305== Invalid read of size 4
==24305== at 0x400917: sum_runner (x1.c:53)
==24305== by 0x4E416B9: start_thread (pthread_create.c:333)
==24305== Address 0x54216ac is 12 bytes inside a block of size 24 free'd
==24305== at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24305== by 0x400CEF: createThreads (x1.c:251)
==24305== by 0x400E3C: run (x1.c:312)
==24305== by 0x400F5C: main (x1.c:367)
==24305== Block was alloc'd at
==24305== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24305== by 0x400B98: createPartitions (x1.c:195)
==24305== by 0x400C57: createThreads (x1.c:238)
==24305== by 0x400E3C: run (x1.c:312)
==24305== by 0x400F5C: main (x1.c:367)
==24305==
Здесь у нас есть пара операций чтения из уже освобожденного блока памяти.Чтения происходят в sum_runner
:
wrapper *data = (wrapper *)args;
int currentIndex = data->startIndex;
int endIndex = data->endIndex;
, а в createThreads
:
for (int i = 0; i < numberOfThreads; i++)
{
pthread_create(&threads[i], NULL, sum_runner, (void *)&partitions[i]);
printf(" Created thread %lu\n", threads[i]);
}
free(partitions);
Судя по вашим комментариям в другом месте кода, вы, кажется, под впечатлениемэтот вызов pthread_join
запускает поток.Это не вариант.Вызов pthread_create
фактически запускает поток, в то время как pthread_join
говорит вызывающему потоку дождаться завершения данного потока.Таким образом, вы в конечном итоге освобождаете эту память до того, как поток сможет ее использовать.
Так что удалите эту free
и измените createThreads
, чтобы вернуть этот указатель.Затем в run
(откуда вызывается createThreads
) и сделайте здесь бесплатное после вызова startThreads
(который действительно должен быть переименован во что-то вроде waitForThreadsToFinish
):
wrapper *createThreads(pthread_t *threads)
{
wrapper *partitions = createPartitions();
printf("Creating threads......\n");
for (int i = 0; i < numberOfThreads; i++)
{
pthread_create(&threads[i], NULL, sum_runner, (void *)&partitions[i]);
printf(" Created thread %lu\n", threads[i]);
}
// remove free
printf("Finished creating threads......\n");
return partitions;
}
...
int run()
{
...
wrapper *partitions = createThreads(threads);
results = startThreads(threads);
free(partitions);
Иэто должно заботиться о недопустимых доступах.
Есть также несколько мест, где вы выделяете пространство для int *
(или массива int *
), где вы должны выделить место для одного или нескольких int
.Это не вызывает проблем в вашей системе, потому что int *
по крайней мере такой же большой, как int
, но вы все равно должны использовать правильный тип независимо от того.
Также обратите внимание, что мои предложенные изменения удалилиприведение к возвращаемому значению malloc
.См. Я приведу результат malloc? для более подробной информации.
Есть еще несколько утечек памяти, которые вы можете найти, если передадите --leak-check=full
в valgrind, но я оставлю это в качестве упражнения для читателя.