1

Basically what I am trying to do is simulate multithreading on a single thread with context switching. I set up an alarm for every 10 microseconds, and I switch the context from one to another thread. The problem is that about one in 5 runs ends up with a seg fault right after the alarm finishes the swapcontext, at least that is where I traced it with gdb.

Here are my source files 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);

and 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)
    {
    }
}

I tried a lot of combinations and tried tracing by instruction but could not arrive to a conclusion or idea as to what might cause this seg fault. Thanks

Cristi M
  • 446
  • 4
  • 13

1 Answers1

0

Few issues I see (some are related to segfault + some other comments)

  1. You scheduler (thread_scheduler) should be in a critical section, e.g. you should block any alarm signals (or ignore them) so that the handing of the threadpool is done in a way that doesn't corrupt it. you can either use sigprocmask or a volatile boolean variable that will silence the alarm (note this is not the same as the user threads mutex, just an internal synchronization to your scheduling logic)

  2. your clock ticks way too fast IMHO, this is in micro seconds, not milliseconds, so 1000 microseconds for tv_usec might make more sense for testing purposes.

  3. small stack sizes might also cause a seg fault but it seems your stack is big enough.

p.s. there is a better way to handle join, you currently waste lot's of CPU cycles on it, why not simply avoid switching to a thread that called join, untill the thread that it's waiting for has terminated?

Eran Medan
  • 44,555
  • 61
  • 184
  • 276