├── part2 ├── README.md ├── xchg_test.c ├── Makefile ├── mymutex.h ├── mymutex.c ├── user.c ├── mypthread.h └── mypthread.c /part2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i/THREADIT/master/part2 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | THREADIT 2 | ======== 3 | 4 | User thread library similar to PThreads 5 | -------------------------------------------------------------------------------- /xchg_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mymutex.h" 3 | 4 | int main(void) { 5 | uint a = 0; 6 | xchg(&a, 1); 7 | printf("%d\n", a); 8 | 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | FLAGS= -Wall -Werror 2 | EXTRA= -Wextra 3 | DEBUG= -g 4 | 5 | all: 6 | gcc $(FLAGS) mypthread.c mymutex.c user.c -o part2 -g 7 | 8 | debug: all 9 | 10 | clean: 11 | rm part2 12 | -------------------------------------------------------------------------------- /mymutex.h: -------------------------------------------------------------------------------- 1 | #ifndef MYMUTEX_H 2 | #define MYMUTEX_H 3 | #include "mypthread.h" 4 | 5 | #define FREE 0 6 | #define LOCKED 1 7 | 8 | typedef unsigned int uint; 9 | 10 | typedef struct mypthread_mutex { 11 | uint state; 12 | int ready; 13 | } mypthread_mutex_t; 14 | 15 | int mypthread_mutex_init(mypthread_mutex_t *); 16 | int mypthread_mutex_lock(mypthread_mutex_t *); 17 | int mypthread_mutex_trylock(mypthread_mutex_t *); 18 | int mypthread_mutex_unlock(mypthread_mutex_t *); 19 | int mypthread_mutex_destroy(mypthread_mutex_t *); 20 | 21 | static inline unsigned int xchg(uint *addr, uint newval) { 22 | uint result; 23 | asm volatile("lock; xchgl %0, %1" : 24 | "+m" (*addr), "=a" (result) : 25 | "1" (newval) : 26 | "cc"); 27 | return result; 28 | } 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /mymutex.c: -------------------------------------------------------------------------------- 1 | #include "mymutex.h" 2 | 3 | 4 | int mypthread_mutex_init(mypthread_mutex_t *mutex) { 5 | if (mutex->ready) { 6 | return -1; 7 | } 8 | 9 | mutex->ready = 1; 10 | mutex->state = FREE; 11 | return 0; 12 | } 13 | 14 | int mypthread_mutex_lock(mypthread_mutex_t *mutex) { 15 | if (mutex->ready) { 16 | while (xchg(&mutex->state, 1) != 0) ; 17 | return 0; 18 | } 19 | return -1; 20 | } 21 | 22 | int mypthread_mutex_trylock(mypthread_mutex_t *mutex) { 23 | if (mutex->ready) 24 | return xchg(&mutex->state, 1); 25 | return -1; 26 | } 27 | 28 | int mypthread_mutex_unlock(mypthread_mutex_t *mutex) { 29 | if (mutex->ready) 30 | return !xchg(&mutex->state, 0); 31 | return -1; 32 | } 33 | 34 | int mypthread_mutex_destroy(mypthread_mutex_t *mutex) { 35 | if (mutex->ready) { 36 | mutex->ready = 0; 37 | return 0; 38 | } 39 | return -1; 40 | } 41 | -------------------------------------------------------------------------------- /user.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "mypthread.h" 6 | #include "mymutex.h" 7 | 8 | static mypthread_t t1, t2; 9 | static mypthread_mutex_t mutex; 10 | static int *a; 11 | 12 | void *bar(void *arg) { 13 | if (mypthread_mutex_trylock(&mutex)) { 14 | printf("bar couldn't get lock\n"); 15 | } else { 16 | printf("bar got lock\n"); 17 | } 18 | 19 | return NULL; 20 | } 21 | 22 | void *foo(void *arg) { 23 | if (mypthread_mutex_lock(&mutex)) { 24 | printf("foo couldn't get lock\n"); 25 | } else { 26 | printf("foo got lock\n"); 27 | *a = 15; 28 | sleep(1); 29 | } 30 | 31 | return NULL; 32 | } 33 | 34 | int main() { 35 | a = malloc(sizeof(int)); 36 | 37 | mypthread_mutex_init(&mutex); 38 | 39 | mypthread_create(&t1, foo, NULL); 40 | mypthread_create(&t2, bar, NULL); 41 | 42 | 43 | mypthread_join(t1, NULL); 44 | 45 | free(a); 46 | return 0; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /mypthread.h: -------------------------------------------------------------------------------- 1 | #ifndef MYPTHREAD_H 2 | #define MYPTHREAD_H 3 | #include 4 | #include 5 | 6 | #define MAX_THREADS 1024 7 | #define STACK_SZ 0x4000 8 | 9 | int tableup; // Whether or not thread table has been initialized 10 | int pos; // Current position in thread table 11 | enum status { UNUSED, BLOCKED, WAITING, RUNNABLE, TERMINATED }; 12 | 13 | /* 14 | * pthread type 15 | */ 16 | typedef struct mypthread { 17 | void*(*fn)(void*); 18 | void *arg; 19 | enum status state; 20 | ucontext_t ctx; 21 | void *retval; 22 | int id; 23 | int join_id; 24 | } mypthread_t; 25 | 26 | mypthread_t main_thread; 27 | mypthread_t *ttable[MAX_THREADS]; 28 | 29 | /* 30 | * creates a new thread 31 | */ 32 | int mypthread_create(mypthread_t *thread, 33 | void *(*start_routine) (void *), void *arg); 34 | 35 | /* 36 | * terminates the calling thread 37 | */ 38 | void mypthread_exit(void *retval); 39 | 40 | /* 41 | * yield the processor causing the calling thread to relinquish the CPU 42 | * returns 0 on success; returns error number on failure 43 | */ 44 | int mypthread_yield(void); 45 | 46 | /* 47 | * join with a terminated thread. 48 | */ 49 | int mypthread_join(mypthread_t thread, void **retval); 50 | 51 | /* 52 | * set up ttable 53 | */ 54 | void ttable_init(); 55 | void scheduler(); 56 | mypthread_t *next_thread(void); 57 | void start_timer(void); 58 | void print_thread_table(void); 59 | void start(mypthread_t*); 60 | void handler(int); 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /mypthread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "mypthread.h" 8 | 9 | #define start_t void(*)(void) 10 | 11 | // create pthread, add it to thread table 12 | int mypthread_create(mypthread_t *thread, void *(*start_routine) (void *), void *arg) 13 | { 14 | int i, first = 0; 15 | 16 | if (!tableup) { 17 | ttable_init(); 18 | start_timer(); 19 | first = 1; 20 | } 21 | 22 | // set up context 23 | getcontext(&thread->ctx); 24 | thread->ctx.uc_stack.ss_sp = calloc(STACK_SZ, 1); 25 | thread->ctx.uc_stack.ss_size = STACK_SZ; 26 | thread->ctx.uc_link = NULL; 27 | makecontext(&thread->ctx, (start_t)start, 1, thread); 28 | 29 | thread->arg = arg; 30 | thread->fn = start_routine; 31 | thread->state = RUNNABLE; 32 | thread->join_id = 0; 33 | thread->retval = NULL; 34 | 35 | for (i = 1; i < MAX_THREADS; i++) { 36 | if (ttable[i] == NULL || ttable[i]->state == UNUSED) { 37 | ttable[i] = thread; 38 | thread->id = i; 39 | if (first) { 40 | // set up main() thread 41 | ttable[0]->state = RUNNABLE; 42 | ttable[0]->id = 0; 43 | ttable[0]->retval = NULL; 44 | ttable[0]->join_id = 0; 45 | getcontext(&ttable[0]->ctx); 46 | } 47 | return 0; 48 | } 49 | } 50 | 51 | return -1; 52 | } 53 | 54 | void mypthread_exit(void *retval) 55 | { 56 | mypthread_t *curr = ttable[pos]; 57 | curr->retval = retval; 58 | curr->state = TERMINATED; 59 | mypthread_yield(); 60 | } 61 | 62 | // give up control of processor 63 | int mypthread_yield(void) 64 | { 65 | mypthread_t *old, *new; 66 | old = ttable[pos]; 67 | new = next_thread(); 68 | // printf("Swapping out thread %d with %d\n", old->id, new->id); 69 | pos = new->id; 70 | 71 | return swapcontext(&old->ctx, &new->ctx); 72 | } 73 | 74 | // wait for thread thr to exit 75 | int mypthread_join(mypthread_t thr, void **retval) 76 | { 77 | mypthread_t *curr = ttable[pos]; 78 | mypthread_t *joinee = ttable[thr.id]; 79 | curr->join_id = thr.id; 80 | curr->state = WAITING; 81 | 82 | while (ttable[thr.id]->state != TERMINATED) { 83 | mypthread_yield(); 84 | } 85 | curr->state = RUNNABLE; 86 | 87 | if (retval) { 88 | *retval = joinee->retval; 89 | } 90 | 91 | return 0; 92 | } 93 | 94 | // set up the thread table 95 | void ttable_init(void) 96 | { 97 | int i; 98 | tableup = 1; 99 | for (i = 0; i < MAX_THREADS; i++) { 100 | ttable[i] = NULL; 101 | } 102 | ttable[0] = &main_thread; 103 | } 104 | 105 | void start_timer(void) 106 | { 107 | struct sigaction sa; 108 | struct itimerval timer; 109 | 110 | sa.sa_handler = handler; 111 | sigaction(SIGVTALRM, &sa, NULL); 112 | 113 | timer.it_interval.tv_sec = 1; 114 | timer.it_interval.tv_usec = 0; 115 | timer.it_value.tv_sec = 1; 116 | timer.it_value.tv_usec = 0; 117 | setitimer(ITIMER_VIRTUAL, &timer, NULL); 118 | } 119 | 120 | void handler(int sig) { 121 | mypthread_yield(); 122 | } 123 | 124 | // get next runnable thread 125 | mypthread_t *next_thread(void) 126 | { 127 | int i = pos + 1; 128 | for (;; i++) { 129 | i = i % MAX_THREADS; 130 | if (ttable[i] != NULL) { 131 | if (ttable[i]->state == RUNNABLE) { 132 | return ttable[i]; 133 | } 134 | if (ttable[i]->state == WAITING && 135 | ttable[ttable[i]->join_id]->state == TERMINATED) { 136 | return ttable[i]; 137 | } 138 | } 139 | } 140 | } 141 | 142 | void print_thread_table() 143 | { 144 | int i; 145 | for (i = 0; i < MAX_THREADS; i++) { 146 | if (ttable[i] != NULL) { 147 | printf("{ id: %d, state: ", i); 148 | switch (ttable[i]->state) { 149 | case UNUSED: 150 | printf("UNUSED"); 151 | break; 152 | case RUNNABLE: 153 | printf("RUNNABLE"); 154 | break; 155 | case WAITING: 156 | printf("WAITING[%d]", ttable[i]->join_id); 157 | break; 158 | case BLOCKED: 159 | printf("BLOCKED"); 160 | break; 161 | case TERMINATED: 162 | printf("TERMINATED"); 163 | break; 164 | } 165 | if (ttable[i]->state == TERMINATED) 166 | printf(", retval: %p", ttable[i]->retval); 167 | printf(" }\n"); 168 | } 169 | } 170 | } 171 | 172 | void start(mypthread_t *t) { 173 | t->retval = t->fn(t->arg); 174 | t->state = TERMINATED; 175 | free(t->ctx.uc_stack.ss_sp); 176 | mypthread_yield(); 177 | } 178 | --------------------------------------------------------------------------------