Subj : Re: segmentation fault while using setjmp To : comp.os.linux.development.system,comp.os.linux,comp.os.linux.questions From : Iwo Mergler Date : Tue Jun 29 2004 02:32 pm naveen wrote: > Hello *, > > Here I'm trying to switch control between three threads in a cycle - > parent, child1, child2 in that order. > But after the first cycle of execution i get a segmentation fault. I > guess the problem is with the stack allocated for each thread. I tried > increasing the stack size and i still get the segmentation fault, but > this time after completing a few more cycles. > Can anybody explain this: > > #include > #include > #include > #include > > sigjmp_buf context[3]; > int crt = 0; > > void parent() > { > while(1) > fprintf(stderr, "Parent process\n"); > } > > void child(int *i) > { > while(1) > fprintf(stderr, "Child process %d\n", *i); > } > > /* create child context and return to parent */ > void create(int i) > { > if (sigsetjmp(context[i], 1)) { > child(&crt); > } else { > char stack[1024]; > siglongjmp(context[0], -1); > } > } > > /* context switch */ > void run_thread() > { > int next = (crt + 1) % 3; > > if (!sigsetjmp(context[crt], 1)) { > crt = next; > siglongjmp(context[crt], -1); > } > } > > void handler(int sig) > { > signal(SIGALRM, handler); > alarm(1); > > run_thread(); > } > > main() > { > signal(SIGALRM, handler); > alarm(1); > > if (!sigsetjmp(context[0], 1)) { > create(1); > } > if (!sigsetjmp(context[0], 1)) { > create(2); > } > parent(); > } Naveen, you are sharing *one* stack between your tasks. That doesn't work. See below for an example of how to recursively fragment the stack to make it work, at least most of the time. The code at the end of the e-mail is a proof of concept preemptive task switcher. Do not use it for anything important! Also, listen to the other posters - it's a bad idea to use setjmp/longjmp for this. There are masses of multithreading libraries available... Your application has normally a single stack. The top of the stack is marked by the processor's stack pointer. Memory, which is beyond the top is free, from the OS's point of view. This means that if you abandon your current stackframe with a call to longjump, the OS may use the newly freed stack memory memory for other purposes. When you jump back into that stack frame, the contents can be random. The effect is similar to using malloc'ed memory after you free it. It works most of the time, but if the system is under heavy load it may fail. The failure with a corrupted stack is likely to be more spectacular though. Kind regards, Iwo ------------- #include #include #include #include #include #include #include #include #include #include #define ever (;;) int mSleep(unsigned long msec); void Task1(void *Arg) { for ever { printf(" Task1\n"); mSleep(4); } } void Task2(void *Arg) { for ever { printf(" Task2\n"); mSleep(1); } } void TaskIdle(void *Arg) { struct timespec TS,rem; int retval; for ever { printf(" Idle sleep\n"); TS.tv_sec=0; TS.tv_nsec=1000*1000*500; rem.tv_sec=rem.tv_nsec=0; retval=nanosleep(&TS,&rem); /* printf(" Idle wakeup (%s) %ld.%03ld\n",strerror(errno),rem.tv_sec,rem.tv_nsec/1000000); */ } } /* ========= OS ================ */ void Debug(const char *fmt,...) { va_list ap; va_start(ap, fmt); vfprintf(stderr,fmt,ap); fflush(stderr); } #define MAX_TASKS 4 enum Taskmode { DEAD , INIT , RUN , SLEEP }; typedef struct { int Number; /* Task number, index to array */ int Mode; /* dead,init,run,sleep */ unsigned long WakeupTime; /* System time for wakeup in SLEEP mode */ void (*Address)(void*); /* Start address of task */ jmp_buf env; /* Task context storage */ } TaskBlock; TaskBlock T[MAX_TASKS]; TaskBlock *CT=NULL; /* Current Task */ int NumTasks=0; unsigned long SystemTime=0; /* Number of slices since start */ int retval=0; struct itimerval TimerValue; void Schedule(void) { static int OldTask=0,NewTask=0; static int i; Debug("Scheduler here\n"); OldTask = CT->Number; /* Find new task to run, idle is always runnable */ for (i=NumTasks-1; i>=0; i--) { if (T[i].Mode == RUN) /* Task is ready to run, go */ { NewTask=i; break; } else if (T[i].Mode == SLEEP) /* Task is asleep, handle this */ { if (SystemTime >= T[i].WakeupTime) /* If timer is expired, run */ { NewTask=i; T[i].Mode = RUN; break; } } else if (T[i].Mode == INIT) /* Task is ready to run, go */ { NewTask=i; /* Task is still in the GrowStack context */ break; } else { Debug("[%d] Invalid task mode (%d/%d)\n",__LINE__,i,T[i].Mode); exit(-1); } } Debug("Sched: T%d -> T%d\n",OldTask,NewTask); /* Store old task context */ retval=setjmp(T[OldTask].env); if (retval) { /* Target of longjump */ return; } else { /* Immediately after setjmp */ /* run new task */ Debug("Sched: Lauching Task %d\n",NewTask); CT=&(T[NewTask]); longjmp(CT->env,1); } Debug("[%d] Fatal error, code should never reach this point\n",__LINE__); } /* Time slicer. This is the signal handler for SIGALRM */ void Slicer(int sig) { SystemTime++; /* no check for wraparound */ Debug("Slicer: SIGALRM, slicing the Time, T=%ld\n",SystemTime); Schedule(); /* reset timer */ retval = setitimer(ITIMER_REAL,&TimerValue,NULL); if (retval) { Debug("[%d] settimer failed, %s\n",__LINE__,strerror(errno)); exit(-1); } } /* Creates Substacks recursively */ void GrowStack(int i) { volatile unsigned char StackStorage[32768]; /* Stack size per 'process' */ /* Init stack area, keep compiler from warning about unused var */ memset((void*)StackStorage,0,sizeof StackStorage); Debug("GrowStack: Setting environment for Task %d\n",i); if (T[i].Mode != INIT) { Debug("[%d] Warning, task %d is not in INIT state\n",__LINE__,i); } retval=setjmp(T[i].env); /* Store context */ if (retval) { /* We are the target of a longjump */ /* Sanity checks */ i=CT->Number; /* The i on the stack gets messed up */ Debug("GrowStack: received longjmp for task %d\n",i); if (T[i].Mode != INIT) { Debug("[%d] Task %d not in init mode\n",__LINE__,i); exit(-1); } if (T[i].Address == NULL) { Debug("[%d] Task %d has uninitialised start address\n",__LINE__,i); exit(-1); } /* Launch the task */ T[i].Mode = RUN; T[i].Address(NULL); /* Never return here */ Debug("[%d] Task %d returned unexpectedly\n",__LINE__,i); exit(-1); } else { /* This is the aftermath of setjump */ if (iNumber,slices,SystemTime+slices); CT->Mode=SLEEP; CT->WakeupTime=SystemTime+slices; Schedule(); return 0; } int main(void) { int i; struct sigaction sigact; /* Reset tasks */ for (i=0; i