First off, let me apologize for the length of the code "sample", I was trying to provide the smallest executable example I could.
I should mention, the queue is of the form:
When run is called, the first element is successfully removed from the queue which is then referenced by the global pointer Curr_Thread
. I check the form of the queue before and after, everything is where its supposed to be.
Swapcontext works and control is passed to func_1()
, but this is where the problem lies. As soon as it enters func_1()
, the queue gets mangled somehow, meaning, the head pointer still points to the "dummy" element as it did before the switch (with the next and previous pointers pointing where they should), but everything after the dummy element now points to some garbage element with random next and previous pointer addresses. When func_1
calls yield()
, the call to AddTcb()
inside crashes because it can never find the end of the queue to add Curr_Thread
.
Before entry into func_1()
from swapcontext
:
Immediately after entry into func_1()
from swapcontext
Why is the structure of my queue suddenly changed after calling swapcontext
? Why is it changing with no other interaction?
Thank you.
#include <ucontext.h>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include <ctype.h>
#include <stdlib.h>
#define MAX_QUEUE 100
TCB_t *ReadyQ;
TCB_t *Curr_Thread;
int global_thread_id = 0;
typedef struct TCB_t {
struct TCB_t *next;
struct TCB_t *prev;
ucontext_t context;
int thread_id;
} TCB_t;
void start_thread(void (*function) (void));
void run();
void yield();
void print_id(TCB_t *tcb);
void func_1();
void (*f1)();
TCB_t* newItem();
TCB_t* newTcb(TCB_t* head);
void AddTcb(TCB_t* head_node, TCB_t* new_node);
TCB_t* DelTcb(TCB_t* head);
void init_TCB (TCB_t *tcb, void *function, void *stackP, int stack_size)
{
memset(tcb, '\0', sizeof(TCB_t));
getcontext(&tcb->context);
tcb->context.uc_stack.ss_sp = stackP;
tcb->context.uc_stack.ss_size = (size_t) stack_size;
tcb->thread_id = global_thread_id ++;
makecontext(&tcb->context, function, 0);// context is now cooked
}
void start_thread(void (*function) (void)){
void *stack; //generic stack pointer
TCB_t *new_tcb; //new TCB
stack = malloc(STACK_SIZE);
new_tcb = (TCB_t*) malloc(sizeof(TCB_t));
init_TCB(new_tcb, function, stack, sizeof(stack));
AddTcb(ReadyQ, new_tcb);
}
void run(){
Curr_Thread = DelTcb(ReadyQ);
ucontext_t parent;
getcontext(&parent); //get the current running context
swapcontext(&parent, &(Curr_Thread->context)); //switch it to the next q element
}
void yield(){
TCB_t *prev_thread;
AddTcb(ReadyQ, Curr_Thread);
prev_thread = Curr_Thread;
Curr_Thread = DelTcb(ReadyQ);
//swap the context from the previous thread to the thread pointed to by Curr_Thread
swapcontext(&(prev_thread->context), &(Curr_Thread->context));
}
struct TCB_t* newItem(){
TCB_t* new_tcb; //create new node on heap
new_tcb = (TCB_t*) malloc(sizeof(TCB_t));
return new_tcb; //return the new node
}
TCB_t* newQueue(){
TCB_t *dummy = newItem(); //create dummy node
TCB_t *head = newItem();
dummy->next = NULL; //set dummy elements to NULL
dummy->prev = NULL;
head->next = dummy; //point head at dummy
head->prev = NULL;
return head; //return head
}
//Add new item to queue
void AddTcb(TCB_t* head_tcb_node, TCB_t* new_tcb_node){
TCB_t* tmp, *dummy;
dummy = head_tcb_node->next; //tmp is header node
if(dummy->next == NULL){
dummy->next = new_tcb_node;
dummy->prev = new_tcb_node;
new_tcb_node->next = dummy;
new_tcb_node->prev = dummy;
}else{
tmp = dummy->next;
while(tmp->next != dummy){
tmp = tmp->next;
}
new_tcb_node->next = tmp->next;
new_tcb_node->prev = tmp;
tmp->next = new_tcb_node;
dummy->prev = new_tcb_node;
}
}
//Remove and return first queue element
TCB_t* DelTcb(TCB_t *head){
TCB_t *dummy, *pop, *tmp;
dummy = head->next;
if (dummy->next == NULL){
pop = NULL;
}else{
pop = dummy->next;
if(pop->next == dummy){
dummy->next = NULL;
dummy->prev = NULL;
}else{
tmp = pop->next;
tmp->prev = dummy;
dummy->next = tmp;
}
pop->next = pop->prev = NULL;
}
return pop;
}
void func_1(){
int local_1 = 0;
while(1){
//print_id(ReadyQ);
printf("\n");
printf("Global int: %d\t", gbl_num);
printf("Local int, function 1: %d\n\n", local_1);
gbl_num++;
local_1++;
yield();
sleep(1);
}
}
int main(){
ReadyQ = newQueue();
f1 = func_1;
start_thread(f1);
run();
return 0;
}