c context switching segfault

1

I'm required to simulate threads using context switching and interrupts. I am occasionally (1/25 runs) getting a segfault in my program. I am not really sure what the cause of it is. I suspect it has to do with the manner in which I do the context switching. I've tried determining the reason for the segfault with gdb and valgrind with no success.

GDB results:

0x0000007b in ?? ()
(gdb) bt
#0  0x0000007b in ?? ()
(gdb)

Valgrind results (not complete):

    ==6343== Conditional jump or move depends on uninitialised value(s)
      8 ==6343==    at 0x808F8CA: __linkin_atfork (in my-test)
      9 ==6343==    by 0x809046B: _dl_non_dynamic_init (in my-test)
     10 ==6343==    by 0x8090C31: __libc_init_first (in my-test)
     11 ==6343==    by 0x805EEB0: (below main) (in my-test)
     12 ==6343==  Uninitialised value was created
     13 ==6343==    at 0x80900E2: _dl_sysinfo_int80 (in my-test)
     14 ==6343==    by 0x80C056F: brk (in my-test)
     15 ==6343==    by 0x808D6C9: sbrk (in my-test)
     16 ==6343==    by 0x805F1CB: __libc_setup_tls (in my-test)
     17 ==6343==    by 0x805F3C6: __pthread_initialize_minimal (in my-test)
     18 ==6343==    by 0x805EE5C: (below main) (in my-test)
     19 ==6343==
     20 ==6343== Conditional jump or move depends on uninitialised value(s)
     21 ==6343==    at 0x806C015: malloc (in my-test)
     22 ==6343==    by 0x809046B: _dl_non_dynamic_init (in my-test)
     23 ==6343==    by 0x8090C31: __libc_init_first (in my-test)
     24 ==6343==    by 0x805EEB0: (below main) (in my-test)
     25 ==6343==  Uninitialised value was created
     26 ==6343==    at 0x80900E2: _dl_sysinfo_int80 (in my-test)
     27 ==6343==    by 0x80C056F: brk (in my-test)
     28 ==6343==    by 0x808D6C9: sbrk (in my-test)
     29 ==6343==    by 0x805F1CB: __libc_setup_tls (in my-test)
     30 ==6343==    by 0x805F3C6: __pthread_initialize_minimal (in my-test)
     31 ==6343==    by 0x805EE5C: (below main) (in my-test)
...    
    ==6343== Warning: client switching stacks?  SP change: 0xbee18ea0 --> 0x8135e08
    281 ==6343==          to suppress, use: --max-stackframe=1228001128 or greater
    282 ==6343== Conditional jump or move depends on uninitialised value(s)
    283 ==6343==    at 0x806C015: malloc (in my-test)
    284 ==6343==    by 0x804E5D4: mem_resize_fn (in my-test)
    285 ==6343==    by 0x804A5D1: expand (iin my-test)
    286 ==6343==    by 0x804B0C4: list_insert (in my-test)
    287 ==6343==    by 0x804BE7E: list_append_int (in my-test)
    288 ==6343==    by 0x8049B4D: handler (in my-test)
    289 ==6343==    by 0x8060B42: makecontext (in my-test)
    290 ==6343==    by 0x8049AB0: main (in my-test)
    291 ==6343==  Uninitialised value was created
    292 ==6343==    at 0x80900E2: _dl_sysinfo_int80 (in my-test)
    293 ==6343==    by 0x80C056F: brk (in my-test)
    294 ==6343==    by 0x808D6C9: sbrk (in my-test)
    295 ==6343==    by 0x805F1CB: __libc_setup_tls (in my-test)
    296 ==6343==    by 0x805F3C6: __pthread_initialize_minimal (in my-test)
    297 ==6343==    by 0x805EE5C: (below main) (in my-test)
 ...   


==6343== Warning: client switching stacks?  SP change: 0x813da60 --> 0xbee18ea0
385 ==6343==          to suppress, use: --max-stackframe=1228032960 or greater
386 ==6343== Use of uninitialised value of size 4
387 ==6343==    at 0x804A1E9: scheduler (in my-test)
388 ==6343==    by 0x809F617: ??? (in my-test)
389 ==6343==    by 0x809F617: ??? (in my-test)
390 ==6343==  Uninitialised value was created by a stack allocation
391 ==6343==    at 0x8060BD8: swapcontext (in my-test)
...
 ==6343== Use of uninitialised value of size 4
434 ==6343==    at 0x805F644: sigprocmask (in my-test)
435 ==6343==    by 0x1FFF: ???
436 ==6343==    by 0x8049B4D: handler (in my-test)
437 ==6343==    by 0x8060B42: makecontext (in my-test)
438 ==6343==    by 0x8049AB0: main (in my-test)
439 ==6343==  Uninitialised value was created by a stack allocation
440 ==6343==    at 0x8060BD8: swapcontext (in my-test)
441 ==6343==
442 ==6343== Jump to the invalid address stated on the next line
443 ==6343==    at 0x2000: ???
444 ==6343==    by 0x8049B4D: handler (in my-test)
445 ==6343==    by 0x8060B42: makecontext (in my-test)
446 ==6343==    by 0x8049AB0: main (in my-test)
447 ==6343==  Address 0x2000 is not stack'd, malloc'd or (recently) free'd
448 ==6343==
449 ==6343==
450 ==6343== Process terminating with default action of signal 11 (SIGSEGV)
451 ==6343==  Bad permissions for mapped region at address 0x2000
452 ==6343==    at 0x2000: ???
453 ==6343==    by 0x8049B4D: handler (in my-test)
454 ==6343==    by 0x8060B42: makecontext (in my-test)
455 ==6343==    by 0x8049AB0: main (in my-test)
456 ==6343==
457 ==6343== HEAP SUMMARY:
458 ==6343==     in use at exit: 0 bytes in 0 blocks
459 ==6343==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
460 ==6343==
461 ==6343== All heap blocks were freed -- no leaks are possible
462 ==6343==
463 ==6343== For counts of detected and suppressed errors, rerun with: -v
464 ==6343== ERROR SUMMARY: 114 errors from 38 contexts (suppressed: 0 from 0)

my-test.c:

/* Includes */
#include <stdio.h>      /* Input/Output */
#include <stdlib.h>     /* General Utilities */
#include <math.h>

#include "mythreads.h"

/* prototype for thread routine */
void handler ( );

/* global vars */

/* semaphores are declared global so they can be accessed 
   in main() and in thread routine,
   here, the semaphore is used as a mutex */

int counter_mutex;

/* shared variables */
int counter; 
double result = 0.0;

int main()
{
    int thread_num = 10;
    int j;
    char* thread_names[] = {
        "thread 0",
        "thread 1",
        "thread 2",
        "thread 3",
        "thread 4",
        "thread 5",
        "thread 6",
        "thread 7",
        "thread 8",
        "thread 9"
    };

    /* Initialize MyThreads library. */
    mythread_init();

    /* 250 ms */
    set_quantum_size(500);

    counter_mutex = create_semaphore(1);

     for(j=0; j<thread_num; j++)
     {
        mythread_create(thread_names[j], (void *) &handler, 6004);
     }

    /* Print threads informations before run */
    //mythread_state();

     /* When this function returns, all threads should have exited. */
     runthreads();

    destroy_semaphore(counter_mutex);

    // /* Print threads informations after run */
    mythread_state();

     printf("The counter is %d\n", counter);
    printf("The result is %f\n", result);

   if (counter == 50 &&
     (result - 151402.656521) < 0.000001)
       printf(">>> Thread library PASSED the Test 1\n");

    exit(0);
}


void handler ()
{
    int i;
    for(i=0; i < 5; i++)
    {
        /* If you remove this protection, you should be able to see different
         * out of every time you run this program.  With this protection, you
         * should always be able to see result to be 151402.656521 */
        semaphore_wait(counter_mutex);       /* down semaphore */

        /* START CRITICAL REGION */
        int j;
        for (j = 0; j < 1000; j++) {
            result = result + sin(counter) * tan(counter);
        }
        counter++;
        /* END CRITICAL REGION */    

        semaphore_signal(counter_mutex);       /* up semaphore */
    }
    mythread_exit(); /* exit thread */
}

mythreads.h

#ifndef __MY_THREADS_H__
#define __MY_THREADS_H__

#include <signal.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/timeb.h>
#include <ucontext.h>
#include <slack/std.h>
#include <slack/list.h>

#define THREAD_NAME_LEN 32
#define THREAD_MAX 128
#define SEMAPHORE_MAX 128
#define THREAD_STACK_SIZE 4096
#define QUANTUM_N_SIZE 60000

enum ThreadState {
  NOTCREATED,
  RUNNING,
  RUNNABLE,
  BLOCKED,
  EXIT
};

typedef struct ControlBlock_t {
  ucontext_t context;
  char thread_name[THREAD_NAME_LEN];
  int thread_id;
  enum ThreadState state;
  struct timespec start;
  struct timespec end;
  double run_time;
  void *stack;
} ControlBlock;

typedef struct Semaphore_t {
  List * thread_queue;
  int value;
  int initial;
  int active;
} Semaphore;

int mythreads_init();
int mythread_create(char *threadname, void (*threadfunc)(), int stacksize);
void mythread_exit();
int mythread_id();
void runthreads();
void set_quantum_size(int quantum);
int create_semaphore(int value);
void semaphore_wait(int semaphore);
void semaphore_signal(int semaphore);
void destroy_semaphore(int semaphore);
void mythread_state();
void evict_thread();
void scheduler();

#endif /* __MY_THREADS_H__ */

mythreads.c

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <slack/std.h>
#include <slack/list.h>
#include "mythreads.h"

static ControlBlock tcbTable[THREAD_MAX];
static Semaphore semTable[SEMAPHORE_MAX];
static List * runqueue;
static ControlBlock * current_thread;
static int threadId = 0;
static int runningThreadId = 0;
static ucontext_t uctx_main;
static int quantum_size;
struct itimerval timer;
static int current_threads;
static int my_threads_init;
static int current_semaphores;
static int done = 0;
static int totalThreadsCreated = 0;
static int totalThreadsExited = 0;
long long int switch_ctr;
int i=0;

/*
This function initializes all the globa data structures for the thread system
*/
int mythread_init(){
  int i=0;

  //Initialize the run queue.
  runqueue = list_create(NULL);

  //Initialize the thread control table.
  for(i = 0;i < THREAD_MAX;i++){
    tcbTable[i].state = NOTCREATED;
  }

  //Initialize the semaphore table.
  for(i = 0;i < SEMAPHORE_MAX;i++){
    semTable[i].active = 0;
  }
}

int mythread_create(char *threadName, void(*threadfunc)(), int stacksize){
  //Set basic information about the thread.
  strcpy(tcbTable[threadId].thread_name, threadName);
  tcbTable[threadId].thread_id = threadId;
  tcbTable[threadId].state = RUNNABLE;

  //Set the context information about the thread.
  getcontext(&tcbTable[threadId].context); //Save the current context.
  tcbTable[threadId].context.uc_link = &uctx_main; //The context that will be resumed when the current context exits.
  tcbTable[threadId].stack = malloc(stacksize);
  tcbTable[threadId].context.uc_stack.ss_sp = tcbTable[threadId].stack; //Allocate the stack and make it point to the beginning of the stack.
  tcbTable[threadId].context.uc_stack.ss_size = stacksize; //Set the signal stack size.
  makecontext(&tcbTable[threadId].context,threadfunc,0); //Creates the context with the information set above.

  //Add thread to the runqueue.
  runqueue = list_append_int(runqueue,threadId);
  totalThreadsCreated++;

  //If an error occured, return -1. Otherwise, return the  thread id.
  if(!tcbTable[threadId].context.uc_stack.ss_sp){
    //We failed to allocate memory for the stack
    printf("Failed to allocate memory for the stack\n");
    fflush(stdout);
    return -1;
  }else{
    int tmp = threadId;
    threadId++;
    return tmp;
  }
}

/*
This function is called at the end of the function that was 
invoked by the thread.
*/
void mythread_exit(){
  sigset_t x;
  sigemptyset(&x);
  sigaddset(&x, SIGALRM);
  sigprocmask(SIG_BLOCK, &x, NULL);
  printf("Setting state of thread %d to EXIT.\n",runningThreadId);
  fflush(stdout);
  tcbTable[runningThreadId].state = EXIT; //Set the state of the thread to EXIT.
  totalThreadsExited++;
  if(totalThreadsCreated == totalThreadsExited){
    done = 1;
  }
  sigprocmask(SIG_UNBLOCK, &x, NULL);
}

/*
Starts running one of the threads in the runqueue.
*/
void runthreads(){
   /* Block Signals */
    sigset_t x;
    sigemptyset(&x);
    sigaddset(&x, SIGALRM);
    sigprocmask(SIG_BLOCK, &x, NULL);

    struct itimerval timer;
    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = quantum_size;
    timer.it_value.tv_sec = 0;
    timer.it_value.tv_usec = quantum_size;
    setitimer(ITIMER_REAL, &timer, 0);

    sigset(SIGALRM, &scheduler);
    runningThreadId = list_shift_int(runqueue);


    tcbTable[runningThreadId].state = RUNNING;

    //begin_time(running_thread);

    /* Unblock signal */
    sigprocmask(SIG_UNBLOCK, &x, NULL);

    if(swapcontext(&uctx_main, &tcbTable[runningThreadId].context) == -1) {
        perror("swapcontext error");
        exit(1);
    }

    while(!list_empty(semTable[0].thread_queue) || (totalThreadsExited<totalThreadsCreated));

    sigprocmask(SIG_BLOCK, &x, NULL);
    for(i = 0; i < 10 ; i++) {
        tcbTable[i].state = EXIT;
    }
    printf("Back in main\n");
    fflush(stdout);
}

void set_quantum_size(int size){
  quantum_size = size;
}

/*
  Called when the ALRM signal fires
*/
void scheduler(){
    switch_ctr++;
   if (list_empty(runqueue) && tcbTable[runningThreadId].state == EXIT) {
        printf("returning to main");
        fflush(stdout);
        setcontext(&uctx_main);
    }

    if (!list_empty(runqueue) ) {
      sigset_t x;
      sigemptyset(&x);
      sigaddset(&x, SIGALRM);
      sigprocmask(SIG_BLOCK, &x, NULL);

      int old_running_thread = runningThreadId;
      runningThreadId = list_shift_int(runqueue);

      if (tcbTable[old_running_thread].state == RUNNABLE || tcbTable[old_running_thread].state == RUNNING) {
          runqueue = list_append_int(runqueue, old_running_thread);
      }

      sigprocmask(SIG_UNBLOCK, &x, NULL);

      if (swapcontext(&tcbTable[old_running_thread].context, &tcbTable[runningThreadId].context) == -1) {
          printf("swapcontext error");
          fflush(stdout);
      }
    }
}

int create_semaphore(int val){
  if (current_semaphores == SEMAPHORE_MAX){
    //We already have the maximum number of semphores allowed 
    //so we cannot create new ones.
    return -1;
  }else{
    semTable[current_semaphores].initial = val;
    semTable[current_semaphores].value = val;
    semTable[current_semaphores].thread_queue = list_create(NULL);
    int tmp = current_semaphores;
    current_semaphores++;
    return tmp;
  }
}

/*
  Called by the handler() funcion in my-test.c
*/
void semaphore_wait(int semaphore){
    sigset_t x;
    sigemptyset(&x);
    sigaddset(&x, SIGALRM);
    sigprocmask(SIG_BLOCK, &x, NULL);

    long long int old_switch_ctr = switch_ctr; 
    (semTable[semaphore]).value--;

    if((semTable[semaphore]).value<0) {
        (tcbTable[runningThreadId]).state = BLOCKED;
        (semTable[semaphore]).thread_queue = list_append_int((semTable[semaphore]).thread_queue,runningThreadId);
    }

    sigprocmask(SIG_UNBLOCK,&x,NULL);
    while(old_switch_ctr==switch_ctr);
}

/*
  Called by the handler() funcion in my-test.c
*/
void semaphore_signal(int semaphore){
    // Block Signals
    sigset_t x;
    sigemptyset (&x);
    sigaddset(&x, SIGALRM);
    sigprocmask(SIG_BLOCK, &x, NULL);

    /* Increase Semaphore value */
    semTable[semaphore].value = semTable[semaphore].value + 1;

    if (semTable[semaphore].value < 1) {
        int should_run;
        should_run = list_shift_int(semTable[semaphore].thread_queue);
        tcbTable[should_run].state = RUNNABLE;
        runqueue = list_append_int(runqueue, should_run);
    } 

    // Unblock Signals
    sigprocmask(SIG_UNBLOCK, &x, NULL);
    scheduler();
}

void destroy_semaphore(int semaphore){
  if(!list_empty(semTable[semaphore].thread_queue)){
    fprintf(stderr, "There are threads waiting on this semaphore. Thus, it cannot be destroyed\n");
    return;
  }
}

void mythread_state()
{
    printf("\nTHREADNAME\tTHREAD\tTHREAD STATE\tCPU TIME(ns)\n"); 
    fflush(stdout);
    int i;
    for(i = 0; (tcbTable[i].state != NOTCREATED) && (i < THREAD_MAX); ++i) {
        char * state_i;
        switch(tcbTable[i].state) {
            case 0:
                state_i = "NOTCREATED";
                break;
            case 1:
                state_i = "RUNNING";
                break;
            case 2:
                state_i = "RUNNABLE";
                break;
            case 3:
                state_i = "RUNNABLE";
                break;
            case 4:
                state_i = "EXIT";
                break;
            default:
                state_i = "UNDEFINED";
                break;
        }
        printf("\n%s\t%d\t%s\t\t%la\n", tcbTable[i].thread_name, i, state_i, tcbTable[i].run_time); 
    }
}
c
concurrency
asked on Stack Overflow Mar 15, 2013 by lbj-ub • edited Mar 15, 2013 by Jacob Parker

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0