Я реализую свою собственную версию malloc, которая очень похожа на glibc malloc, так как в нем поддерживается многопоточность путем создания арен, которые являются областью памяти, с которой поток может работать, не рискуя конкурировать с другим потоком..
Моя структура данных выглядит следующим образом:
typedef struct s_arena {
pthread_mutex_t mutex;
t_pool *main_pool;
} t_arena;
typedef struct s_arena_data {
_Atomic int arena_count;
t_arena arenas[M_ARENA_MAX];
} t_arena_data;
t_arena_data - глобальная переменная, которая содержит число созданных арен, начиная с 0 для первого вызова и заканчивая M_ARENA_MAX(который я в настоящее время определяю как 8) и массив, содержащий все мои арены.
Арена содержит только мьютекс, который инициализируется с помощью pthread_mutex_init (), и указатель на пул памяти.Пул памяти не важен для этой темы, так как условие гонки возникает до его достижения.
Как работает моя программа: когда каждый поток входит в malloc, он пытается pthread_try_lock мьютекс первой арены.Если это так, все хорошо, и он переходит к распределению, которое я здесь не описал.Если этого не произойдет, может произойти несколько вещей.
Если следующая запись в массиве пуста и M_ARENA_MAX не достигнут, новый мьютекс будет заблокирован, чтобы создать новую арену и добавить ее вмассив.Мьютекс является глобальным, что означает, что никакие два потока не могут создавать арену одновременно.
Если этот мьютекс заблокирован, то поток вернется к арене [0] и продолжит поиск открытого мьютекса.
Теперь я почти уверен, что условие гонки происходит из-за переменной arena_count.Благодаря инструкциям отладки printf я заметил, что в любое время функция segfaults, M_ARENA_MAX не была достигнута.Если это так, программа не будет аварийно завершать работу.Поэтому я подозреваю, что один поток может считывать значение arena_count непосредственно перед тем, как другой поток увеличивает его, и к тому моменту, когда он завершает чтение, поток, который увеличивает его, освобождает new_arena_mutex, и первый поток начинает создавать арену сневерный индекс.
Это моя первая многопоточная программа, поэтому я прошу прощения, если мое объяснение или код не ясны, но я тратил последние 4 часа на эту проблему, и хотя я думаю, что сузилпроблема, я действительно не знаю, как ее решить.
Вот часть кода, которая неисправна:
current_arena = &arena_data.arenas[0];
int arena_index = 0;
while (pthread_mutex_trylock(¤t_arena->mutex) != 0) {
printf("THREAD %p READS[0] ARENA COUNT AT %d\n", (void *)pthread_self(), arena_data.arena_count);
if (arena_index == arena_data.arena_count - 1) {
printf("THREAD %p READS[1] ARENA COUNT AT %d\n", (void *)pthread_self(), arena_data.arena_count);
if (pthread_mutex_trylock(&new_arena_mutex) != 0 || arena_data.arena_count == M_ARENA_MAX) {
current_arena = &arena_data.arenas[(arena_index = 0)];
continue;
}
creator = true;
break;
}
current_arena = &arena_data.arenas[arena_index++];
}
/* All arenas are occupied by other threads but M_ARENA_MAX isn't reached. Let's just create a new one. */
if (creator == true) {
printf("THREAD %p READS[2] ARENA COUNT AT %d\n", (void *)pthread_self(), arena_data.arena_count);
current_pool = create_new_pool(MAIN_POOL, chunk_type, size, pagesize, &new_arena_mutex);
if (current_pool == MAP_FAILED) return NULL;
++arena_data.arena_count;
arena_data.arenas[arena_index + 1] = (t_arena){ .main_pool = current_pool };
pthread_mutex_init(&arena_data.arenas[arena_index + 1].mutex, NULL);
pthread_mutex_lock(&arena_data.arenas[arena_index + 1].mutex);
pthread_mutex_unlock(&new_arena_mutex);
return user_area((t_alloc_chunk *)current_pool->chunk, size, &arena_data.arenas[arena_index + 1].mutex);
}
Вот одна из инструкций printf, и та, котораяУтешает мою теорию, что есть условие гонки:
THREAD 0x7f9c3b216700 READS[1] ARENA COUNT AT 4
THREAD 0x7f9c3b216700 READS[2] ARENA COUNT AT 5
Значение должно быть равным, но это не так.