rendered paste body/* * Implementation for CS325 thread library * Copyright 2008 Anthony Martinez */#define _GNU_SOURCE#include "libthread.h"#include <assert.h>#include <sched.h>#include <stdio.h>#include <stdlib.h>#include <stdbool.h>#include <string.h>#include <search.h>static unsigned int _libthread_maxtid = 0;static unsigned int _libthread_curtid = 0;static queue_head_t _runqueue = {NULL};static queue_head_t _waitqueue = {NULL};static volatile thread_state_t _libthread_global_context;static unsigned int _libthread_save_context = 1;void _enqueue(queue_head_t *q, thread_state_t *new) { ensure_queue(q); if(q->head && q->tail) { /* This is not the first element */ insque(q->tail, new); q->tail = new; } else { /* This is the first thing in the queue, so we have to handle head and tail. */ q->head = new; q->tail = new; q->head->prev = q->head->next = NULL; }}thread_state_t *_dequeue(queue_head_t *q) { thread_state_t *r = NULL; ensure_queue(q); if(q->head) { r = q->head; q->head = r->next; if(q->head == NULL) q->tail = NULL; remque(r); } return r;}thread_state_t *_peek(queue_head_t *q) { ensure_queue(q); return q->head;}unsigned int thread_create(void *function) { unsigned int thread_id = _libthread_maxtid++; thread_state_t *newthread = new_thread_state(); Dprintf("%s: Got function %p\n", __FUNCTION__, function); newthread->esp = (unsigned int*)malloc(THREAD_STACK_SIZE); Dprintf("%s: new thread stack from %#x to %#x\n", __FUNCTION__, newthread->esp, newthread->esp+THREAD_STACK_SIZE);#ifdef DEBUG memset(newthread->esp, 0xF1, THREAD_STACK_SIZE);#endif newthread->esp += (int)THREAD_STACK_SIZE-4; /* sgd */ __asm__ __volatile__ ("movl 4(%%ebp), %0" : "=a" (*newthread->esp)); Dprintf("%s: returning to %#x\n", __FUNCTION__, *newthread->esp); newthread->ebp = newthread->esp; newthread->eip = (unsigned int*)function; newthread->id = thread_id; _enqueue(&_runqueue, newthread); return thread_id;}void begin_threads(void) { thread_state_t *cur_thread = NULL; Dprintf("%s called\n", __FUNCTION__); while(1) { cur_thread = _peek(&_runqueue); if(cur_thread) { _libthread_save_context = 0; thread_switch(cur_thread->id); } else { exit(0); } }}void thread_suspend(void) { __asm__ __volatile__ ("movl %%eax, %0;" "movl %%ebx, %1;" "movl %%ecx, %2;" "movl %%edx, %3;" "movl %%esi, %4;" "movl %%edi, %5;" : "=m"(_libthread_global_context.eax), /* 0 */ "=m"(_libthread_global_context.ebx), /* 1 */ "=m"(_libthread_global_context.ecx), /* 2 */ "=m"(_libthread_global_context.edx), /* 3 */ "=m"(_libthread_global_context.esi), /* 4 */ "=m"(_libthread_global_context.edi) /* 5 */ ); thread_state_t *new_context = new_thread_state(); thread_state_t *next_thread = _peek(&_runqueue); Dprintf("%s called\n", __FUNCTION__); memcpy(new_context, (thread_state_t *volatile)&_libthread_global_context, sizeof(thread_state_t)); new_context->id = _libthread_curtid; __asm__ __volatile__ ("movl %%ebp, %0" : "=a" (new_context->esp)); new_context->esp += 2; __asm__ __volatile__ ("movl (%%ebp), %0;" "movl 4(%%ebp), %1;" : "=a" (new_context->ebp), "=c" (new_context->eip)); _enqueue(&_waitqueue, new_context); if(next_thread) { _libthread_save_context = 0; thread_switch(next_thread->id); } else { fprintf(stderr, "%s: got null thread from run queue: H: %p, t:%p\n", __FUNCTION__, _runqueue.head, _runqueue.tail);#ifndef DEBUG exit(-2);#endif } }void thread_switch(unsigned int id) { __asm__ __volatile__ ("movl %%eax, %0;" "movl %%ebx, %1;" "movl %%ecx, %2;" "movl %%edx, %3;" "movl %%esi, %4;" "movl %%edi, %5;" : "=m"(_libthread_global_context.eax), /* 0 */ "=m"(_libthread_global_context.ebx), /* 1 */ "=m"(_libthread_global_context.ecx), /* 2 */ "=m"(_libthread_global_context.edx), /* 3 */ "=m"(_libthread_global_context.esi), /* 4 */ "=m"(_libthread_global_context.edi) /* 5 */ ); thread_state_t *inspection = _peek(&_runqueue); thread_state_t *new_context = new_thread_state(); thread_state_t found; Dprintf("%s: got tid %i, %ssaving context\n", __FUNCTION__, id, _libthread_save_context == 0 ? "not " : ""); if(_libthread_save_context == 0) goto just_switch; memcpy(new_context, (thread_state_t *volatile)&_libthread_global_context, sizeof(thread_state_t)); __asm__ __volatile__ ("movl %%ebp, %0" : "=m" (new_context->esp)); new_context->esp += 2; __asm__ __volatile__ ("movl (%%ebp), %0;" "movl 4(%%ebp), %1;" : "=a" (new_context->ebp), "=c" (new_context->eip)); _enqueue(&_runqueue, new_context); just_switch: /* Our next switch should save context */ _libthread_save_context = 1; while(inspection != NULL) { if(inspection->id == id) { _libthread_curtid = id; memcpy(&found, inspection, sizeof(thread_state_t)); /* Dprintf("thread_switch: \neax %#8.x\necx %#8.x\nedx %#8.x\nebx %#8.x\nesp %#8.x\nebp %#8.x\nesi %#8.x\nedi %#8.x\neip %#8.x\n", found.eax, found.ecx, found.edx, found.ebx, found.esp, found.ebp, found.esi, found.edi, found.eip); */ if(inspection == _runqueue.head) { _runqueue.head = inspection->next; } if(inspection == _runqueue.tail) { _runqueue.tail = inspection->prev; } remque(inspection);#ifdef DEBUG memset(inspection, 0x81, sizeof(thread_state_t));#endif free(inspection); found.esp--; /* Make room for RA */ *found.esp = (unsigned int) found.eip; __asm__ __volatile__ ( "movl %0, %%eax;" "movl %1, %%ebx;" "movl %2, %%ecx;" "movl %3, %%edx;" "movl %4, %%esi;" "movl %5, %%edi;" "movl %6, %%esp;" "movl %7, %%ebp;" "ret" : "=m" (found.eax), /* 0 */ "=m" (found.ebx), /* 1 */ "=m" (found.ecx), /* 2 */ "=m" (found.edx), /* 3 */ "=m" (found.esi), /* 4 */ "=m" (found.edi), /* 5 */ "=mV" (found.esp), /* 6 */ "=mV" (found.ebp) /* 7 */ ); }#ifdef DEBUG if(inspection->next && inspection->next->prev != inspection) { fprintf(stderr, "%s: queue anomaly\n", __FUNCTION__); abort(); }#endif inspection = inspection->next; }}void thread_wakeup(unsigned int id) { thread_state_t *inspection = _peek(&_waitqueue); int tried_runq = 0; Dprintf("%s: got tid %i\n", __FUNCTION__, id); retry: while(inspection != NULL) { if(inspection->id == id) { Dprintf("%s: found in %squeue: thread %i context at %p\n", __FUNCTION__, (tried_runq ? "run" : "wait" ), id, (void*)inspection); if(!tried_runq) { if(inspection == _waitqueue.head) { _waitqueue.head = inspection->next; } if(inspection == _waitqueue.tail) { _waitqueue.tail = inspection->prev; } remque(inspection); _enqueue(&_runqueue, inspection); } return; }#ifdef DEBUG if(inspection->next && inspection->next->prev != inspection) { fprintf(stderr, "%s: queue anomaly\n", __FUNCTION__); abort(); }#endif inspection = inspection->next; } /* Didn't find our tid on waitq, try runq */ if(!tried_runq) { inspection = _peek(&_runqueue); tried_runq = 1; goto retry; } fprintf(stderr, "%s: was asked to wakeup a nonexistent thread %i\n", __FUNCTION__, id);}