/* * A signal handler for SIGVTALRM * Comes here when a thread runs up its time slot. This handler implements * a preemptive thread scheduler. It looks at the global ready queue, pop * the thread in the front, save the current thread context and switch context. */ void sigvtalrm_handler(int sig) { /* block the signal */ sigprocmask(SIG_BLOCK, &vtalrm, NULL); /* if no thread in the ready queue, resume execution */ if (steque_isempty(&ready_queue)) return; /* get the next runnable thread and use preemptive scheduling */ thread_t* next = (thread_t*) steque_pop(&ready_queue); while (next->state == GTTHREAD_CANCEL) { steque_enqueue(&zombie_queue, next); next = (thread_t*) steque_pop(&ready_queue); } thread_t* prev = current; steque_enqueue(&ready_queue, current); next->state = GTTHREAD_RUNNING; current = next; /* unblock the signal */ sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); swapcontext(prev->ucp, current->ucp); }
int gtthread_create(gtthread_t *thread, void *(*start_routine)(void *), void *arg) { if (init != 1) { /*printf("Error - gtthread not initialized. \n");*/ exit(-1); } else { gtthread *newThread, *current; sigset_t oldset; /* Block alarms */ sigprocmask(SIG_BLOCK, &alrm, &oldset); /* sigprocmask(SIG_BLOCK, &alrm, NULL); */ /* do malloc for new thread and init its attributes */ if((newThread = malloc(sizeof(gtthread))) != NULL) { newThread -> id = thread_num++; newThread -> finished = 0; newThread -> cancel = 0; /* store thread id */ *thread = newThread -> id; /* get thread at front of currently running queue*/ current = (gtthread *) gtthread_self(); /* getcontext() */ if ( getcontext(&newThread -> uctx) == 0) { newThread -> uctx.uc_stack.ss_sp = (char*) malloc(SIGSTKSZ); newThread -> uctx.uc_stack.ss_size = SIGSTKSZ; /* set its successor context */ newThread -> uctx.uc_link = ¤t -> uctx; } /* init join queue for the new thread */ steque_init(&newThread -> joining); makecontext(&newThread -> uctx, (void (*)(void)) run_thread, 2, start_routine, arg); /* Add new thread to back of queue */ steque_enqueue(&globalQ, newThread); steque_enqueue(¤tly_running, newThread); sigprocmask(SIG_UNBLOCK, &alrm, NULL); } else { /*printf("Thread creation malloc failed. Exiting.\n");*/ sigprocmask(SIG_UNBLOCK, &alrm, NULL); return 1;/* ? */ } return 0; } }
/* The gtthread_init() function does not have a corresponding pthread equivalent. It must be called from the main thread before any other GTThreads functions are called. It allows the caller to specify the scheduling period (quantum in micro second), and may also perform any other necessary initialization. If period is zero, then thread switching should occur only on calls to gtthread_yield(). Recall that the initial thread of the program (i.e. the one running main() ) is a thread like any other. It should have a gtthread_t that clients can retrieve by calling gtthread_self() from the initial thread, and they should be able to specify it as an argument to other GTThreads functions. The only difference in the initial thread is how it behaves when it executes a return instruction. You can find details on this difference in the man page for pthread_create. */ void gtthread_init(long period){ gtthread_int_t *mainthread; /* Malloc for this thread */ if ((mainthread = malloc(sizeof(gtthread_int_t))) != NULL){ /* Initialize queues */ steque_init(&threads); steque_init(&run_queue); /* Set up mainthread */ mainthread->id = next_id++; mainthread->cancelreq = 0; mainthread->completed = 0; steque_init(&mainthread->join_queue); /* Set up the context */ getcontext(&mainthread->context); mainthread->context.uc_stack.ss_sp = (char *) malloc(SIGSTKSZ); mainthread->context.uc_stack.ss_size = SIGSTKSZ; steque_enqueue(&threads, mainthread); steque_enqueue(&run_queue, mainthread); /* Initialize the scheduling quantum */ quantum = period; if (quantum != 0) { /* Setting up the signal mask */ sigemptyset(&vtalrm); sigaddset(&vtalrm, SIGVTALRM); /* Setting up the alarm */ timer = (struct itimerval*) malloc(sizeof(struct itimerval)); timer->it_value.tv_sec = timer->it_interval.tv_sec = 0; timer->it_value.tv_usec = timer->it_interval.tv_usec = quantum; /* Setting up the handler */ memset(&act, '\0', sizeof(act)); act.sa_handler = &alrm_handler; if (sigaction(SIGVTALRM, &act, NULL) < 0) { printf("sigaction"); } setitimer(ITIMER_VIRTUAL, timer, NULL); sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); } } // FIXME: what about the error case? }
gtthread_blk_t *get_thread(gtthread_t tID){ int queue_len, i; gtthread_blk_t *tmp, *ret; int match = 0; DEBUG_MSG("get_thread, tID: %ld\n", tID); queue_len = steque_size(&thread_queue); for(i = 0; i < queue_len; i++){ tmp = (gtthread_blk_t *)steque_pop(&thread_queue); steque_enqueue(&thread_queue, tmp); if(tmp->tID == tID){ ret = tmp; match = 1; } } if(!match) DEBUG_MSG("No Match!\n"); return ret; }
void HandleIncomingRequests() { printf("Monitoring for requests...\n"); while(_monitorIncomingRequests) { cache_status_request *request = malloc(sizeof(cache_status_request)); ssize_t msgSize = msgrcv(_requestQueueId, request, sizeof(cache_status_request), 0, 0); printf("Message size %lu\n", msgSize); if(msgSize > 0) { printf("Request recieved for [Path: %s]\n", request->Path); if(simplecache_get(request->Path) > 0) { printf("Exists in cache\n"); request->CacheStatus = IN_CACHE; steque_enqueue(_queue, request); } else { printf("Does not exist in cache\n"); request->CacheStatus = NOT_IN_CACHE; } SendCacheStatusResponse(request); } } printf("Stop handling requests...\n"); }
/* The gtthread_create() function mirrors the pthread_create() function, only default attributes are always assumed. */ int gtthread_create(gtthread_t *thread, void *(*start_routine)(void *), void *arg) { // Sets the thread id and other attributes. thread->id = g_thread_id++; thread->is_finished = 0; thread->is_joined = -1; thread->wait_tid = -1L; thread->retval = NULL; thread->context = (ucontext_t *) malloc(sizeof(ucontext_t)); ucontext_t curr_context; if(getcontext(thread->context) == -1) { perror("Error calling getcontext()"); exit(EXIT_FAILURE); } // Creating a stack for the context. thread->context->uc_stack.ss_sp = (char *) malloc(SIGSTKSZ); thread->context->uc_stack.ss_size = SIGSTKSZ; // Sets the caller thread to be the new thread's successor. thread->context->uc_link = &curr_context; // Uses the apply() helper function with start_routine and arg as arguments. makecontext(thread->context, (void *) apply, 3, (void *) start_routine, arg, thread); steque_enqueue(&g_threads_steque, thread); return 0; }
/* schedules next thread in the currently_running queue*/ void scheduleNextAndSwap(gtthread* current) { gtthread *nextThread, *runnable; nextThread = (gtthread *) steque_front(¤tly_running); if(nextThread -> cancel) { nextThread -> finished = 1; steque_pop(¤tly_running); while( !steque_isempty(&nextThread -> joining)) { runnable = (gtthread *) steque_pop(&nextThread -> joining); steque_enqueue(¤tly_running, runnable); } nextThread = (gtthread *) steque_front(¤tly_running); } /* swapcontext(old, new) */ if(swapcontext(¤t -> uctx, &nextThread -> uctx) == -1) { /*printf("swapcontext");*/ exit(EXIT_FAILURE); } }
/* The gtthread_init() function does not have a corresponding pthread equivalent. It must be called from the main thread before any other GTThreads functions are called. It allows the caller to specify the scheduling period (quantum in micro second), and may also perform any other necessary initialization. If period is zero, then thread switching should occur only on calls to gtthread_yield(). Recall that the initial thread of the program (i.e. the one running main() ) is a thread like any other. It should have a gtthread_t that clients can retrieve by calling gtthread_self() from the initial thread, and they should be able to specify it as an argument to other GTThreads functions. The only difference in the initial thread is how it behaves when it executes a return instruction. You can find details on this difference in the man page for pthread_create. */ void gtthread_init(long period){ gtthread_t main_id = tid++; q = (steque_t*)malloc(sizeof(steque_t)); steque_init(q); void* main_item; main_item = (node_t*)malloc(sizeof(node_t)); ((node_t*)main_item) -> thread_ctx = (ucontext_t*)malloc(sizeof(ucontext_t)); ((node_t*)main_item) -> id = main_id; getcontext(((node_t*)main_item) -> thread_ctx); //TODO: need to init context? steque_enqueue(q, main_item); /* Setting up the signal mask */ sigemptyset(&vtalrm); sigaddset(&vtalrm, SIGVTALRM); sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); T = (struct itimerval*) malloc(sizeof(struct itimerval)); p = period; set_alarm(); }
/* producer_routine - thread that adds the letters 'a'-'z' to the queue */ void *producer_routine(void *arg) { steque_t *queue_p = arg; pthread_t consumer_thread; int i, result = 0; intptr_t c; result = pthread_create(&consumer_thread, NULL, consumer_routine, queue_p); if (0 != result) { fprintf(stderr, "Failed to create consumer thread: %s\n", strerror(result)); exit(1); } result = pthread_detach(consumer_thread); if (0 != result) fprintf(stderr, "Failed to detach consumer thread: %s\n", strerror(result)); for (i = 0; i < 100; i++){ for (c = 'a'; c <= 'z'; ++c) { /* Add the node to the queue */ pthread_mutex_lock(&g_queue_lock); steque_enqueue(queue_p, (void*) c); pthread_mutex_unlock(&g_queue_lock); sched_yield(); } } pthread_mutex_lock(&g_num_prod_lock); --g_num_prod; pthread_mutex_unlock(&g_num_prod_lock); return (void*) 0; }
/* The gtthread_cancel() function is analogous to pthread_cancel, allowing one thread to terminate another asynchronously. */ int gtthread_cancel(gtthread_t thread) { /* if a thread cancel itself */ if (gtthread_equal(current->tid, thread)) gtthread_exit(0); sigprocmask(SIG_BLOCK, &vtalrm, NULL); thread_t* t = thread_get(thread); if (t == NULL) { sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); return -1; } if (t->state == GTTHREAD_DONE) { sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); return -1; } if (t->state == GTTHREAD_CANCEL) { sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); return -1; } else t->state = GTTHREAD_CANCEL; free(t->ucp->uc_stack.ss_sp); free(t->ucp); t->ucp = NULL; t->joining = 0; steque_enqueue(&zombie_queue, t); sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); return 0; }
void destroy_shm_seg(char* fldes, shm_data* mem) { //release everything in our shm object pthread_mutex_destroy(&mem->data_mutex); pthread_cond_destroy(&mem->proxy_cond); pthread_cond_destroy(&mem->cache_cond); mem->file_length = 0; mem->bytes_written = 0; if (munmap(mem, segment_size) < 0) { fprintf(stdout, "Error %d (%s) on proxy munmap.\n", errno, strerror(errno)); fflush(stdout); } if (shm_unlink(fldes) < 0) { fprintf(stdout, "Error %d (%s) on proxy shm_unlink.\n", errno, strerror(errno)); fflush(stdout); } pthread_mutex_lock(&fldes_mutex); while (fldes_queue_size == max_fldes_queue_size) { pthread_cond_wait(&write_cond, &fldes_mutex); } steque_enqueue(&fldes_queue, fldes); fldes_queue_size++; pthread_mutex_unlock(&fldes_mutex); pthread_cond_broadcast(&read_cond); //pthread_cond_signal(&read_cond); }
void init_proxy() { steque_init(&fldes_queue); // create segment for (int i = 0; i < segments; i++) { char str_id[16] = ""; sprintf(str_id, "%d", i); segment_id = (char*) malloc((strlen(str_id) + strlen(SHAREDMPATH) + 1) * sizeof(char)); bzero(segment_id, (strlen(str_id) + strlen(SHAREDMPATH) + 1) * sizeof(char)); strcpy(segment_id, SHAREDMPATH); strcat(segment_id, str_id); shm_unlink(segment_id); fprintf(stdout, "Created Segment ID: %s\n", segment_id); fflush(stdout); steque_enqueue(&fldes_queue, segment_id); } fldes_queue_size = steque_size(&fldes_queue); max_fldes_queue_size = steque_size(&fldes_queue); // fprintf(stdout, "init queue done.\n"); }
/* Reschedule all threads that are in finished thread's join queue */ static void reschedule_joined(gtthread_int_t *finished) { gtthread_int_t *wait; while (!steque_isempty(&finished->join_queue)) { wait = (gtthread_int_t *) steque_pop(&finished->join_queue); steque_enqueue(&run_queue, wait); } }
/* The gtthread_create() function mirrors the pthread_create() function, only default attributes are always assumed. */ int gtthread_create(gtthread_t *thread, void *(*start_routine)(void *), void *arg){ gtthread_int_t *thread_int, *self; sigset_t oldset; sigprocmask(SIG_BLOCK, &vtalrm, &oldset); /* Malloc for this new thread */ if ((thread_int = malloc(sizeof(gtthread_int_t))) != NULL){ /* Initialize thread values */ /* Block alarms */ thread_int->id = next_id++; *thread = thread_int->id; thread_int->cancelreq = 0; thread_int->completed = 0; steque_init(&thread_int->join_queue); /* Set up the context */ getcontext(&thread_int->context); thread_int->context.uc_stack.ss_sp = (char *) malloc(SIGSTKSZ); thread_int->context.uc_stack.ss_size = SIGSTKSZ; self = (gtthread_int_t *) steque_front(&run_queue); thread_int->context.uc_link = &self->context; makecontext(&thread_int->context, (void (*)(void)) start_wrapper, 2, start_routine, arg); /* Add new thread to data structures */ steque_enqueue(&threads, thread_int); steque_enqueue(&run_queue, thread_int); /* Unblock alarms */ } sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); //FIXME: error case? /* Return 0 on success */ return 0; }
int gtthread_yield(void) { gtthread *lastThread; sigset_t oldset; sigprocmask(SIG_BLOCK, &alrm, &oldset); lastThread = (gtthread *) steque_pop(¤tly_running); steque_enqueue(¤tly_running, lastThread); /* steque_cycle(¤tly_running); */ scheduleNextAndSwap(lastThread); /* swapcontext */ sigprocmask(SIG_UNBLOCK, &alrm, NULL); return 0; }
/* The gtthread_create() function mirrors the pthread_create() function, only default attributes are always assumed. */ int gtthread_create(gtthread_t *thread, void *(*start_routine)(void *), void *arg){ ucontext_t uctx; gtthread_blk_t *th_blk; DEBUG_MSG("gtthread_create\n"); //sigprocmask(SIG_BLOCK, &vtalrm, NULL); // disable alarm signal /************************* Initialize the context *************************************/ if(getcontext(&uctx) == -1) return FAIL; uctx.uc_stack.ss_sp = (char*) malloc(SIGSTKSZ); uctx.uc_stack.ss_size = SIGSTKSZ; uctx.uc_link = NULL; makecontext(&uctx, (void (*) (void))(thread_handler), 2, start_routine, arg); // modify the context /************************** Initialize the thread block *************************************/ th_blk = (gtthread_blk_t *)malloc(sizeof(gtthread_blk_t)); if(th_blk == NULL) return FAIL; th_blk->tID = thread_ID++; th_blk->state = READY; th_blk->uctx = uctx; if(th_blk->uctx.uc_stack.ss_size == SIGSTKSZ) DEBUG_MSG("Correct assign\n"); /********************************************************************************************/ steque_enqueue(&thread_queue, th_blk); // add the thread to the scheduler queue //sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); // enable alarm signal *thread = th_blk->tID; //list_thread(); gtthread_yield(); return SUCCESS; }
int gtthread_join(gtthread_t thread, void **status) { gtthread *target_thread, *callingThread; sigset_t oldset; /* Block alarms */ sigprocmask(SIG_BLOCK, &alrm, &oldset); /* find target thread in globalQ */ target_thread = getThread(thread); if(target_thread != NULL) /*target thread found */ { /* Check if it has finished */ if(target_thread -> finished) { /*If it has finished, unblock alarm and then set status and return*/ sigprocmask(SIG_UNBLOCK,&alrm, NULL); } else { /* If not finished, get the currently running thread(calling thread) and queue it to join target */ callingThread = (gtthread *) steque_pop(¤tly_running); steque_enqueue(&target_thread -> joining, callingThread); /* schedule next thread */ scheduleNextAndSwap(callingThread); /* Now unblock alarms */ sigprocmask(SIG_UNBLOCK, &alrm, NULL); } /* Set status */ if(status != NULL) { *status = target_thread -> retval; } /* successful so return 0 */ return 0; } else /* if target thread not found */ { /* unclock alarms and return */ sigprocmask(SIG_UNBLOCK, &alrm, NULL); return 1; } }
/* The gtthread_init() function does not have a corresponding pthread equivalent. It must be called from the main thread before any other GTThreads functions are called. It allows the caller to specify the scheduling period (quantum in micro second), and may also perform any other necessary initialization. If period is zero, then thread switching should occur only on calls to gtthread_yield(). Recall that the initial thread of the program (i.e. the one running main() ) is a thread like any other. It should have a gtthread_t that clients can retrieve by calling gtthread_self() from the initial thread, and they should be able to specify it as an argument to other GTThreads functions. The only difference in the initial thread is how it behaves when it executes a return instruction. You can find details on this difference in the man page for pthread_create. */ void gtthread_init(long period){ ucontext_t uctx_main; gtthread_blk_t *main_thread; /* Initialize the thread queue */ steque_init(&thread_queue); //fprintf(stderr, "gtthread_init\n"); DEBUG_MSG("gtthread_init\n"); /* Create main thread and put into queue */ /************************* Initialize the context *************************************/ if(getcontext(&uctx_main) == -1){ printf("getcontext FAIL!\n"); return; } uctx_main.uc_stack.ss_sp = (char*) malloc(SIGSTKSZ); uctx_main.uc_stack.ss_size = SIGSTKSZ; uctx_main.uc_link = NULL; //makecontext(&uctx_main, thread_handler, 0); // modify the context /************************** Initialize the thread block *************************************/ if(!(main_thread = malloc(sizeof(gtthread_blk_t)))){ printf("main_thread created failed\n"); return; } main_thread->tID = thread_ID++; main_thread->state = RUNNING; main_thread->uctx = uctx_main; /********************************************************************************************/ steque_enqueue(&thread_queue, main_thread); // add the thread to the scheduler queue sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); // enable alarm signal /* Initialize timer and alarm */ timer_init(period); }
/* Find a thread by its id */ static gtthread_int_t * find_thread(gtthread_t thread) { int queue_len; int i; gtthread_int_t *item; queue_len = steque_size(&threads); for (i = 0; i < queue_len; i++) { item = (gtthread_int_t *) steque_pop(&threads); steque_enqueue(&threads, item); if (item->id == thread) { return item; } } return NULL; }
void print_run_queue(void) { int queue_len; int i; gtthread_int_t *item; queue_len = steque_size(&run_queue); for (i = 0; i < queue_len; i++) { item = (gtthread_int_t *) steque_pop(&run_queue); steque_enqueue(&run_queue, item); printf("%d->", (int) item->id); fflush(stdout); } printf("\n"); fflush(stdout); }
ssize_t handler_get(gfcontext_t *ctx, char *path, void* arg){ steque_node_t* node = calloc(1, sizeof(steque_node_t)); request_t* request = calloc(1, sizeof(request_t)); request->ctx = ctx; request->path = strdup(path); request->arg = arg; node->item = (void*)request; pthread_mutex_lock(&req_mutex); steque_enqueue(&request_queue, node); pthread_cond_signal(&req_cond); pthread_mutex_unlock(&req_mutex); return 0; }
/* The gtthread_yield() function is analogous to pthread_yield, causing the calling thread to relinquish the cpu and place itself at the back of the schedule queue. */ void gtthread_yield(void){ gtthread_int_t *old; sigset_t oldset; /* Block alarms */ sigprocmask(SIG_BLOCK, &vtalrm, &oldset); /* Put current thread at end of run queue */ old = (gtthread_int_t *) steque_pop(&run_queue); steque_enqueue(&run_queue, old); /* Start running the new thread */ schedule_next(old); /* Unblock alarms */ sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); }
/* The gtthread_join() function is analogous to pthread_join. All gtthreads are joinable. */ int gtthread_join(gtthread_t thread, void **status){ gtthread_int_t *target, *self; sigset_t oldset; /* Block alarms */ sigprocmask(SIG_BLOCK, &vtalrm, &oldset); /* Find the thread id */ target = find_thread(thread); if (target != NULL) { /* If the target thread isn't complete, need to schedule another thread */ if (!target->completed) { // 2. Pop this thread off of the main run queue self = (gtthread_int_t *) steque_pop(&run_queue); // 3. Enqueue this thread on the target thread run queue steque_enqueue(&target->join_queue, self); // 4. Schedule the next thread, since this is no longer in the run queue schedule_next(self); /* Unblock alarms */ sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); } else { /* Unblock alarms */ sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); } // 5. Set status if (status != NULL) { *status = target->retval; } return 0; } else { /* Unblock alarms */ sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); // target thread not found return 1; } }
/* The gtthread_exit() function is analogous to pthread_exit. */ void gtthread_exit(void* retval) { /* block alarm signal */ sigprocmask(SIG_BLOCK, &vtalrm, NULL); if (steque_isempty(&ready_queue)) { sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); exit((long) retval); } /* if the main thread call gtthread_exit */ if (current->tid == 1) { while (!steque_isempty(&ready_queue)) { sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); sigvtalrm_handler(SIGVTALRM); sigprocmask(SIG_BLOCK, &vtalrm, NULL); } sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); exit((long) retval); } thread_t* prev = current; current = (thread_t*) steque_pop(&ready_queue); current->state = GTTHREAD_RUNNING; /* free up memory allocated for exit thread */ free(prev->ucp->uc_stack.ss_sp); free(prev->ucp); prev->ucp = NULL; /* mark the exit thread as DONE and add to zombie_queue */ prev->state = GTTHREAD_DONE; prev->retval = retval; prev->joining = 0; steque_enqueue(&zombie_queue, prev); /* unblock alarm signal and setcontext for next thread */ sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); setcontext(current->ucp); }
/* The gtthread_yield() function is analogous to pthread_yield, causing the calling thread to relinquish the cpu and place itself at the back of the schedule queue. */ int gtthread_yield(void) { /* block SIGVTALRM signal */ sigprocmask(SIG_BLOCK, &vtalrm, NULL); /* if no thread to yield, simply return */ if (steque_isempty(&ready_queue)) return 0; thread_t* next = (thread_t*) steque_pop(&ready_queue); thread_t* prev = current; steque_enqueue(&ready_queue, current); current = next; /* unblock the signal */ sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); swapcontext(prev->ucp, current->ucp); return 0; }
static void load_requests(char *workload_path){ FILE *file_handle; request_t *req; file_handle = fopen(workload_path, "r"); if (file_handle == NULL) { fprintf(stderr,"cannot open workload file %s", workload_path); exit(EXIT_FAILURE); } steque_init(&queue); req = (request_t*) malloc(sizeof(request_t)); while (fscanf(file_handle, "%s %s", req->inputfilename, req->outputfilename) != EOF){ steque_enqueue(&queue, req); req = (request_t*) malloc(sizeof(request_t)); } free(req); fclose(file_handle); }
void list_thread(){ int queue_len, i; gtthread_blk_t *tmp; DEBUG_MSG("----------------------------------------------\n"); queue_len = steque_size(&thread_queue); for(i = 0; i < queue_len; i++){ tmp = (gtthread_blk_t *)steque_pop(&thread_queue); DEBUG_MSG("tmp->tID: %ld, state: %d, context: %x\n", tmp->tID, tmp->state, &tmp->uctx); steque_enqueue(&thread_queue, tmp); } DEBUG_MSG("----------------------------------------------\n"); }
void gtthread_exit(void *retval) { gtthread *exiting, *runnable; sigset_t oldset; sigprocmask(SIG_BLOCK, &alrm, &oldset); exiting = (gtthread *) steque_pop(¤tly_running); exiting -> retval = retval; exiting -> finished = 1; /*reschedJoinedThreads(exiting);*//* reschedule other threads in exiting thread's join Q */ while( !steque_isempty(&exiting -> joining)) { runnable = (gtthread *) steque_pop(&exiting -> joining); steque_enqueue(¤tly_running, runnable); } scheduleNextAndSwap(exiting); sigprocmask(SIG_UNBLOCK, &alrm, NULL); }
/* The gtthread_create() function mirrors the pthread_create() function, only default attributes are always assumed. */ int gtthread_create(gtthread_t *thread, void *(*start_routine)(void *), void *arg) { /* block SIGVTALRM signal */ sigprocmask(SIG_BLOCK, &vtalrm, NULL); /* allocate heap for thread, it cannot be stored on stack */ thread_t* t = malloc(sizeof(thread_t)); *thread = t->tid = maxtid++; // need to block signal t->state = GTTHREAD_RUNNING; t->proc = start_routine; t->arg = arg; t->ucp = (ucontext_t*) malloc(sizeof(ucontext_t)); t->joining = 0; memset(t->ucp, '\0', sizeof(ucontext_t)); if (getcontext(t->ucp) == -1) { perror("getcontext"); exit(EXIT_FAILURE); } /* allocate stack for the newly created context */ /* here we allocate the stack size using the canonical */ /* size for signal stack. */ t->ucp->uc_stack.ss_sp = malloc(SIGSTKSZ); t->ucp->uc_stack.ss_size = SIGSTKSZ; t->ucp->uc_stack.ss_flags = 0; t->ucp->uc_link = NULL; makecontext(t->ucp, (void (*)(void)) gtthread_start, 2, start_routine, arg); steque_enqueue(&ready_queue, t); /* unblock the signal */ sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); return 0; }
/* The gtthread_create() function mirrors the pthread_create() function, only default attributes are always assumed. */ int gtthread_create(gtthread_t *thread, void *(*start_routine)(void *), void *arg){ //TODO init check void* new_item; *thread = tid++; new_item = (node_t*)malloc(sizeof(node_t)); ((node_t*)new_item) -> id = *thread; ((node_t*)new_item) -> thread_ctx = (ucontext_t*)malloc(sizeof(ucontext_t)); ((node_t*)new_item) -> canceled = 0; getcontext(((node_t*)new_item) -> thread_ctx); ((node_t*)new_item) -> thread_ctx -> uc_link = 0; ((node_t*)new_item) -> thread_ctx -> uc_stack.ss_sp = malloc(SIGSTKSZ); ((node_t*)new_item) -> thread_ctx -> uc_stack.ss_size = SIGSTKSZ; ((node_t*)new_item) -> thread_ctx -> uc_stack.ss_flags = 0; makecontext(((node_t*)new_item) -> thread_ctx, (void (*)(void)) wrapper, 2, start_routine, arg); steque_enqueue(q, new_item); return 0; }