Ошибка сегментации после перестановки текста в обработчике аварий
По сути, я пытаюсь симулировать многопоточность в одном потоке с переключением контекста. Я устанавливаю будильник на каждые 10 микросекунд и переключаю контекст с одного потока на другой. Проблема в том, что примерно один из пяти запусков завершается с ошибкой сегмента сразу после того, как аварийный сигнал завершает swapcontext, по крайней мере, там я его отследил с помощью gdb.
Вот мои исходные файлы main.c
#include "umt.h"
void f()
{
int x = 10;
printf("starting thread\n");
while(x)
{
printf("thread %d\n", x);
sleep(1);
x--;
}
}
int main()
{
int x = 0, y, z;
umt_init();
y = umt_thread_create(f);
printf("starting main\n");
if(y == 0)
{
printf("Problems with creating thread\n");
return;
}
x = 10;
z = 1;
while(x)
{
printf("main\n");
x--;
}
umt_thread_join(y);
printf("done waiting\n");
return 0;
}
UMT.h
#include <sys/time.h>
#include <stdio.h>
#include <signal.h>
#include <ucontext.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
typedef struct _umt_thread
{
int thread_id;
ucontext_t context;
void (*handler)(void);
int hasFinished;
}umt_thread, *pumt_thread;
void umt_init();
int umt_thread_create(void (*handler)(void));
void umt_thread_join(int thr);
и umt.c
#include "umt.h"
#define MAIN_CONTEXT 0
#define STACK_SIZE 1638400
int currentThread;
char threadpool[15];
pumt_thread threads;
void signal_thread_finish();
void thread_handler()
{
threads[currentThread].handler();
signal_thread_finish();
}
void thread_scheduler();
void signal_thread_finish()
{
threads[currentThread].hasFinished = TRUE;
threadpool[currentThread] = 0;
thread_scheduler();
}
void thread_scheduler()
{
int nextThread = 0, curThread = 0;
int x = 0;
ucontext_t *con1, *con2;
nextThread = currentThread + 1;
while(1)
{
if(nextThread == 15)
nextThread = 0;
if(nextThread == currentThread)
break;
if(threadpool[nextThread] == 1)
break;
nextThread++;
}
if(nextThread == currentThread)
return;
curThread = currentThread;
currentThread = nextThread;
con1 = &(threads[curThread].context);
con2 = &(threads[nextThread].context);
x = swapcontext(con1, con2);
}
void umt_init()
{
ucontext_t context;
struct itimerval mytimer;
int i;
stack_t new_stack;
getcontext(&context);
threads = (pumt_thread)malloc(sizeof(umt_thread) * 15);
threads[MAIN_CONTEXT].thread_id = MAIN_CONTEXT;
threads[MAIN_CONTEXT].context = context;
threadpool[MAIN_CONTEXT] = 1;
for(i = 1;i<15;i++)
{
threadpool[i] = 0;
}
currentThread = 0;
new_stack.ss_sp = (char*)malloc(STACK_SIZE);
new_stack.ss_size = STACK_SIZE;
new_stack.ss_flags = 0;
i = sigaltstack(&new_stack, NULL);
if(i != 0)
{
printf("problems assigning new stack for signaling\n");
}
signal(SIGALRM, thread_scheduler);
mytimer.it_interval.tv_sec = 0;
mytimer.it_interval.tv_usec = 10;
mytimer.it_value.tv_sec = 0;
mytimer.it_value.tv_usec = 5;
setitimer(ITIMER_REAL, &mytimer, 0);
}
int umt_thread_create(void (*handler)(void))
{
ucontext_t context;
int i, pos;
for(i = 1;i<15;i++)
{
if(threadpool[i] == 0)
{
pos = i;
break;
}
}
if(i == 15)
{
printf("No empty space in the threadpool\n");
return -1;
}
if(getcontext(&context) == -1)
{
printf("Problems getting context\n");
return 0;
}
context.uc_link = 0;//&(threads[MAIN_CONTEXT].context);
context.uc_stack.ss_sp = (char*)malloc(STACK_SIZE);
if(context.uc_stack.ss_sp == NULL)
{
printf("Problems with allocating stack\n");
}
context.uc_stack.ss_size = STACK_SIZE;
context.uc_stack.ss_flags = 0;
makecontext(&context, thread_handler, 0);
threads[pos].thread_id = pos;
threads[pos].context = context;
threads[pos].handler = handler;
threads[pos].hasFinished = FALSE;
threadpool[pos] = 1;
printf("Created thread on pos %d\n", pos);
return pos;
}
void umt_thread_join(int tid)
{
while(!threads[tid].hasFinished)
{
}
}
Я пробовал много комбинаций и пытался отследить по инструкции, но не смог прийти к заключению или идее относительно того, что может вызвать эту ошибку сегмента. Спасибо
1 ответ
Я вижу несколько проблем (некоторые из них связаны с segfault + некоторые другие комментарии)
Ваш планировщик (thread_scheduler) должен находиться в критической секции, например, вы должны блокировать любые сигналы тревоги (или игнорировать их) так, чтобы обработка пула потоков выполнялась таким образом, чтобы не повредить его. вы можете использовать либо sigprocmask, либо переменную логической переменной, которая отключит сигнал тревоги (обратите внимание, что это не то же самое, что мьютекс пользовательских потоков, а только внутренняя синхронизация с логикой планирования)
ваши часы тикают слишком быстро ИМХО, это в микросекундах, а не в миллисекундах, поэтому 1000 микросекунд для tv_usec могут иметь больше смысла для целей тестирования.
малый размер стека также может вызвать ошибку сегмента, но кажется, что ваш стек достаточно большой.
ps существует лучший способ обработки объединения, в настоящее время вы тратите на него много циклов ЦП, почему бы просто не избежать переключения на поток, вызвавший соединение, до тех пор, пока ожидающий поток не завершится?