├── mythread_utilities.c ├── mythread_q.h ├── mythread_self.c ├── mythread_wrapper.c ├── myatomic.h ├── futex.c ├── Makefile ├── mythread_idle.c ├── mythread_exit.c ├── futex_inline.h ├── mythread_join.c ├── mythread_test.c ├── futex.h ├── mythread_q.c ├── mythread_yield.c ├── mythread.h ├── README └── mythread_create.c /mythread_utilities.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct futex debug_futex; 7 | static int debug_futex_init_done = 0; 8 | 9 | char debug_msg[1000]; 10 | 11 | pid_t __mythread_gettid() 12 | { 13 | return (pid_t) syscall(SYS_gettid); 14 | } 15 | 16 | void __mythread_debug_futex_init() 17 | { 18 | if (debug_futex_init_done != 1) { 19 | futex_init(&debug_futex, 1); 20 | debug_futex_init_done = 0; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /mythread_q.h: -------------------------------------------------------------------------------- 1 | /* Single Author info: 2 | * sskanitk Salil S Kanitkar 3 | * Group info: 4 | * jhshah Jitesh H Shah 5 | * sskanitk Salil S Kanitkar 6 | * ajalgao Aditya A Jalgaonkar 7 | */ 8 | 9 | /* 10 | mythread_q.h : Function prototypes for thread q functions. 11 | */ 12 | 13 | extern void mythread_q_init(mythread_private_t *node); 14 | extern void mythread_q_add(mythread_private_t *node); 15 | extern void mythread_q_delete(mythread_private_t *node); 16 | extern void mythread_q_state_display(); 17 | extern mythread_private_t * mythread_q_search(unsigned long ); 18 | 19 | -------------------------------------------------------------------------------- /mythread_self.c: -------------------------------------------------------------------------------- 1 | /* Single Author info: 2 | * ajalgao Aditya A Jalgaonkar 3 | * Group info: 4 | * jhshah Jitesh H Shah 5 | * sskanitk Salil S Kanitkar 6 | * ajalgao Aditya A Jalgaonkar 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | /* Return the user exposed handle to the current thread */ 14 | mythread_t mythread_self() 15 | { 16 | pid_t tid; 17 | mythread_t self_tcb; 18 | 19 | /* Get our tid */ 20 | tid = __mythread_gettid(); 21 | self_tcb.tid = tid; 22 | 23 | /* Return by value */ 24 | return (self_tcb); 25 | } 26 | 27 | /* Return pointed to the private TCB structure */ 28 | mythread_private_t *__mythread_selfptr() 29 | { 30 | /* Search in the queue and return the pointer */ 31 | return mythread_q_search(__mythread_gettid()); 32 | } 33 | -------------------------------------------------------------------------------- /mythread_wrapper.c: -------------------------------------------------------------------------------- 1 | /* Single Author info: 2 | * ajalgao Aditya A Jalgaonkar 3 | * Group info: 4 | * jhshah Jitesh H Shah 5 | * sskanitk Salil S Kanitkar 6 | * ajalgao Aditya A Jalgaonkar 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | /* A new thread is created with this wrapper pointer. The aim is to suspend the new 14 | * thread until it is scheduled by the dispatcher. 15 | */ 16 | int mythread_wrapper(void *thread_tcb) 17 | { 18 | mythread_private_t *new_tcb; 19 | new_tcb = (mythread_private_t *) thread_tcb; 20 | 21 | DEBUG_PRINTF("Wrapper: will sleep on futex: %ld %d\n", 22 | (unsigned long)__mythread_gettid(), 23 | new_tcb->sched_futex.count); 24 | 25 | /* Suspend till explicitly woken up */ 26 | futex_down(&new_tcb->sched_futex); 27 | 28 | DEBUG_PRINTF("Wrapper: futex value: %ld %d\n", 29 | (unsigned long)new_tcb->tid, new_tcb->sched_futex.count); 30 | 31 | /* We have been woken up. Now, call the user-defined function */ 32 | new_tcb->start_func(new_tcb->args); 33 | 34 | /* Ideally we shouldn't reach here */ 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /myatomic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * myatomic.h -- support for atomic action (compare and swap) 3 | */ 4 | 5 | /* Atomically store NEWVAL in *MEM if *MEM is equal to OLDVAL and set eqz; 6 | o/w clear eqz. Always return the old *MEM value. 7 | The conditional atomic swap is performed if the return value == OLDVAL. */ 8 | 9 | #ifdef i386 10 | # define __arch_compare_and_exchange_val_32_acq(mem, newval, oldval) \ 11 | ({ __typeof (*mem) ret; \ 12 | __asm __volatile ("lock\n" "cmpxchgl %2, %1\n" \ 13 | : "=a" (ret), "=m" (*mem) \ 14 | : "r" (newval), "m" (*mem), "0" (oldval)); \ 15 | ret; }) 16 | # define compare_and_swap(mem, newval, oldval) \ 17 | __arch_compare_and_exchange_val_32_acq(mem, newval, oldval) 18 | #else 19 | # define __arch_compare_and_exchange_val_64_acq(mem, newval, oldval) \ 20 | ({ __typeof (*mem) ret; \ 21 | __asm __volatile ("lock\n" "cmpxchgq %q2, %1\n" \ 22 | : "=a" (ret), "=m" (*mem) \ 23 | : "r" ((long int) (newval)), "m" (*mem), \ 24 | "0" ((long int) (oldval))); \ 25 | ret; }) 26 | # define compare_and_swap(mem, newval, oldval) \ 27 | __arch_compare_and_exchange_val_64_acq(mem, newval, oldval) 28 | #endif 29 | -------------------------------------------------------------------------------- /futex.c: -------------------------------------------------------------------------------- 1 | #include "futex.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int sys_futex(int *uaddr, int op, int val, const struct timespec *timeout) 8 | { 9 | return syscall(SYS_futex, uaddr, op, val, timeout, NULL, 0); 10 | } 11 | 12 | /* Returns -1 on fail, 0 on wakeup, 1 on pass, 2 on didn't sleep */ 13 | int __futex_down_slow(struct futex *futx, int val, struct timespec *rel) 14 | { 15 | if (sys_futex(&futx->count, FUTEX_WAIT, val, rel) == 0) { 16 | /* <= in case someone else decremented it */ 17 | if (futx->count <= FUTEX_PASSED) { 18 | futx->count = -1; 19 | return 1; 20 | } 21 | return 0; 22 | } 23 | /* EWOULDBLOCK just means value changed before we slept: loop */ 24 | if (errno == EWOULDBLOCK) 25 | return 2; 26 | return -1; 27 | } 28 | 29 | int __futex_up_slow(struct futex *futx) 30 | { 31 | futx->count = 1; 32 | __futex_commit(); 33 | return sys_futex(&futx->count, FUTEX_WAKE, 1, NULL); 34 | } 35 | 36 | int futex_await(struct futex *futx, int signal) 37 | { 38 | return sys_futex(&futx->count, FUTEX_FD, signal, NULL); 39 | } 40 | 41 | void futex_init(struct futex *futx, int val) 42 | { 43 | futx->count = val; 44 | __futex_commit(); 45 | } 46 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Single Author info: 2 | # jhshah Jitesh H Shah 3 | # Group info: 4 | # jhshah Jitesh H Shah 5 | # sskanitk Salil S Kanitkar 6 | # ajalgao Aditya A Jalgaonkar 7 | # 8 | 9 | SRCS = futex.c mythread_q.c mythread_create.c mythread_exit.c mythread_self.c mythread_yield.c mythread_exit.c mythread_join.c mythread_utilities.c mythread_wrapper.c mythread_idle.c 10 | INC = futex.h futex_inline.h myatomic.h mythread.h mythread_q.h 11 | OBJS = $(SRCS:.c=.o) 12 | TEST_SRCS = mythread_test.c 13 | TEST_OBJS = $(TEST_SRCS:.c=.o) 14 | DEBUG=0 15 | 16 | CFLAGS = -Wall -Werror -I. -g 17 | LDFLAGS = -L. 18 | LIB = libmythread.a 19 | 20 | AR = /usr/bin/ar 21 | CC = gcc 22 | 23 | ifeq ($(DEBUG),1) 24 | EXTRA_CFLAGS += -DDEBUG 25 | endif 26 | 27 | .PHONY: all lib clean tags test a5 28 | a5: all 29 | all: lib test 30 | 31 | lib: $(LIB) 32 | 33 | libmythread.a: $(OBJS) $(INC) 34 | $(AR) rcs $(LIB) $(OBJS) 35 | 36 | %.o: %.c $(INC) 37 | $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@ 38 | 39 | clean: 40 | rm -f $(OBJS) $(TEST_OBJS) $(LIB) *~ mythread_test 41 | 42 | tags: 43 | find . -name "*.[cChH]" | xargs ctags 44 | find . -name "*.[cChH]" | etags - 45 | 46 | test: $(TEST_OBJS) $(INC) lib 47 | $(CC) -o mythread_test $(CFLAGS) $(EXTRA_CFLAGS) $(TEST_OBJS) $(LIB) 48 | -------------------------------------------------------------------------------- /mythread_idle.c: -------------------------------------------------------------------------------- 1 | /* Single Author info: 2 | * jhshah Jitesh H Shah 3 | * Group info: 4 | * jhshah Jitesh H Shah 5 | * sskanitk Salil S Kanitkar 6 | * ajalgao Aditya A Jalgaonkar 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | /* Idle thread implementation. 15 | * The thread checks whether it is the only one alive, if yes, exit() 16 | * else keep scheduling someone. 17 | */ 18 | void *mythread_idle(void *phony) 19 | { 20 | mythread_private_t *traverse_tcb; 21 | pid_t idle_tcb_tid; 22 | 23 | while (1) { 24 | DEBUG_PRINTF("I am idle\n"); 25 | traverse_tcb = __mythread_selfptr(); 26 | idle_tcb_tid = traverse_tcb->tid; 27 | traverse_tcb = traverse_tcb->next; 28 | 29 | /* See whether there is a NON-DEFUNCT process in the list. 30 | * If there is, idle doesn't need to kill the process just yet */ 31 | while (traverse_tcb->tid != idle_tcb_tid) { 32 | if (traverse_tcb->state != DEFUNCT) { 33 | break; 34 | } 35 | traverse_tcb = traverse_tcb->next; 36 | } 37 | 38 | /* Idle is the only one alive, kill the process */ 39 | if (traverse_tcb->tid == idle_tcb_tid) 40 | exit(0); 41 | 42 | /* Some thread still awaits execution, yield ourselves */ 43 | mythread_yield(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /mythread_exit.c: -------------------------------------------------------------------------------- 1 | /* Single Author info: 2 | * jhshah Jitesh H Shah 3 | * Group info: 4 | * jhshah Jitesh H Shah 5 | * sskanitk Salil S Kanitkar 6 | * ajalgao Aditya A Jalgaonkar 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | /* Calling the glibc's exit() exits the process. Directly call the syscall 16 | * instead 17 | */ 18 | static void __mythread_do_exit() 19 | { 20 | syscall(SYS_exit, 0); 21 | } 22 | 23 | /* See whether anyone is blocking on us for a join. If yes, mark that thread as READY 24 | * and kill ourselves 25 | */ 26 | void mythread_exit(void *return_val) 27 | { 28 | mythread_private_t *self_ptr; 29 | 30 | /* Get pointer to our TCB structure */ 31 | self_ptr = __mythread_selfptr(); 32 | 33 | /* Don't remove the node from the list yet. We still have to collect the return value */ 34 | self_ptr->state = DEFUNCT; 35 | self_ptr->returnValue = return_val; 36 | 37 | /* Change the state of any thread waiting on us. FIFO dispatcher will do the 38 | * needful 39 | */ 40 | if (self_ptr->blockedForJoin != NULL) 41 | self_ptr->blockedForJoin->state = READY; 42 | 43 | __mythread_dispatcher(self_ptr); 44 | 45 | /* Suicide */ 46 | __mythread_do_exit(); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /futex_inline.h: -------------------------------------------------------------------------------- 1 | #include "myatomic.h" 2 | 3 | /* Atomic dec: return new value. */ 4 | static __inline__ int __futex_down(int *counter) 5 | { 6 | int oval, val = *counter; 7 | 8 | /* Don't decrement if already negative. */ 9 | if (val < 0) 10 | return val; 11 | 12 | oval = compare_and_swap(counter, val-1, val); 13 | 14 | if (oval == val) return val-1; 15 | /* Otherwise, we have no way of knowing value. Guess -1 (if 16 | we're wrong we'll spin). */ 17 | return -1; 18 | } 19 | 20 | /* Atomic inc: return 1 if counter incremented from 0 to 1. */ 21 | static __inline__ int __futex_up(int *counter) 22 | { 23 | int oval, val = *counter; 24 | 25 | oval = compare_and_swap(counter, val+1, val); 26 | 27 | return (oval == val && oval == 0); 28 | } 29 | 30 | /* Simple atomic increment. */ 31 | static __inline__ void __atomic_inc(int *counter) 32 | { 33 | int oval, val = *counter; 34 | 35 | oval = compare_and_swap(counter, val+1, val); 36 | } 37 | 38 | /* Atomic decrement, and return 1 if result is negative. */ 39 | static __inline__ int __furwock_dec_negative(int *counter) 40 | { 41 | int oval, val = *counter; 42 | 43 | oval = compare_and_swap(counter, val-1, val); 44 | 45 | return (oval == val) && (val+1 < 0); 46 | } 47 | 48 | /* Commit the write, so it happens before we send the semaphore to 49 | anyone else */ 50 | static __inline__ void __futex_commit(void) 51 | { 52 | /* Probably overkill, but some non-Intel clones support 53 | out-of-order stores, according to 2.5.5-pre1's 54 | linux/include/asm-i386/system.h */ 55 | #ifdef __i386__ 56 | __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory"); 57 | #endif 58 | } 59 | -------------------------------------------------------------------------------- /mythread_join.c: -------------------------------------------------------------------------------- 1 | /* Single Author info: 2 | * jhshah Jitesh H Shah 3 | * Group info: 4 | * jhshah Jitesh H Shah 5 | * sskanitk Salil S Kanitkar 6 | * ajalgao Aditya A Jalgaonkar 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | /* Wait on the thread specified by "target_thread". If the thread is DEFUNCT, 14 | * just collect the return status. Else, wait for the thread to die and then 15 | * collect the return status 16 | */ 17 | int mythread_join(mythread_t target_thread, void **status) 18 | { 19 | mythread_private_t *target, *self_ptr; 20 | 21 | self_ptr = __mythread_selfptr(); 22 | DEBUG_PRINTF("Join: Got tid: %ld\n", (unsigned long)self_ptr->tid); 23 | target = mythread_q_search(target_thread.tid); 24 | 25 | /* If the thread is already dead, no need to wait. Just collect the return 26 | * value and exit 27 | */ 28 | if (target->state == DEFUNCT) { 29 | *status = target->returnValue; 30 | return 0; 31 | } 32 | 33 | DEBUG_PRINTF("Join: Checking for blocked for join\n"); 34 | /* If the thread is not dead and someone else is already waiting on it 35 | * return an error 36 | */ 37 | if (target->blockedForJoin != NULL) 38 | return -1; 39 | 40 | /* Add ourselves as waiting for join on this thread. Set our state as 41 | * BLOCKED so that we won't be scheduled again. 42 | */ 43 | target->blockedForJoin = self_ptr; 44 | DEBUG_PRINTF("Join: Setting state of %ld to %d\n", 45 | (unsigned long)self_ptr->tid, BLOCKED); 46 | self_ptr->state = BLOCKED; 47 | 48 | /* Schedule another thread */ 49 | mythread_yield(); 50 | 51 | /* Target thread died, collect return value and return */ 52 | *status = target->returnValue; 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /mythread_test.c: -------------------------------------------------------------------------------- 1 | /* Single Author info: 2 | * ajalgao Aditya A Jalgaonkar 3 | * Group info: 4 | * jhshah Jitesh H Shah 5 | * salilk Salil S Kanitkar 6 | * ajalgao Aditya A Jalgaonkar 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | /* Number of threads to start */ 16 | #define NTHREADS 8 17 | 18 | /* This function will first increment count by 50, yield. When it gets the 19 | * control back, it will increment count again and then exit 20 | */ 21 | void *thread_func(void *arg) 22 | { 23 | int *count = (int *)arg; 24 | 25 | *count = *count + 50; 26 | LOG_PRINTF("Thread %ld: Incremented count by 50 and will now yield\n", (unsigned long)mythread_self().tid); 27 | mythread_yield(); 28 | *count = *count + 50; 29 | LOG_PRINTF("Thread %ld: Incremented count by 50 and will now exit\n", (unsigned long)mythread_self().tid); 30 | mythread_exit(NULL); 31 | return NULL; 32 | } 33 | 34 | /* This is a simple demonstration of how to use the mythread library. 35 | * Start NTRHEADS number of threads, collect count value and exit 36 | */ 37 | int main() 38 | { 39 | mythread_t threads[NTHREADS]; 40 | int count[NTHREADS]; 41 | int i; 42 | char *status; 43 | 44 | for (i = 0; i < NTHREADS; i++) { 45 | count[i] = i; 46 | mythread_create(&threads[i], NULL, thread_func, &count[i]); 47 | } 48 | for (i = 0; i < NTHREADS; i++) { 49 | LOG_PRINTF("Main: Will now wait for thread %ld. Yielding..\n", (unsigned long)threads[i].tid); 50 | mythread_join(threads[i], (void **)&status); 51 | LOG_PRINTF("Main: Thread %ld exited and increment count to %d\n", (unsigned long)threads[i].tid, count[i]); 52 | } 53 | LOG_PRINTF("Main: All threads completed execution. Will now exit..\n"); 54 | mythread_exit(NULL); 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /futex.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Userspace Semaphores 3 | */ 4 | #ifndef _FUTEX_H 5 | #define _FUTEX_H 6 | #include /* size_t */ 7 | #include 8 | #include 9 | 10 | /* You should never touch this structure directly! */ 11 | struct futex 12 | { 13 | int count; 14 | }; 15 | 16 | #include "futex_inline.h" 17 | #define FUTEX_PASSED (-(1024 * 1024 * 1024)) 18 | 19 | /* Down and up operations */ 20 | struct timespec; 21 | extern int sys_futex(int *, int, int, const struct timespec *); 22 | extern int __futex_down_slow(struct futex *, int, struct timespec *); 23 | extern int __futex_up_slow(struct futex *); 24 | 25 | static inline int futex_down_timeout(struct futex *futx, struct timespec *rel) 26 | { 27 | int val, woken = 0; 28 | 29 | /* Returns new value */ 30 | while ((val = __futex_down(&futx->count)) != 0) { 31 | switch (__futex_down_slow(futx, val, rel)) { 32 | case -1: return -1; /* error */ 33 | case 1: 34 | return 0; /* passed */ 35 | case 0: woken = 1; break; /* slept */ 36 | } 37 | } 38 | /* If we were woken, someone else might be sleeping too: set to -1 */ 39 | if (woken) { 40 | futx->count = -1; 41 | } 42 | return 0; 43 | } 44 | 45 | /* If __futex_down decrements from 1 to 0, we have it. Otherwise sleep. */ 46 | static inline int futex_down(struct futex *futx) 47 | { 48 | return futex_down_timeout(futx, NULL); 49 | } 50 | 51 | static inline int futex_trydown(struct futex *futx) 52 | { 53 | return (__futex_down(&futx->count) == 0 ? 0: -1); 54 | } 55 | 56 | /* If __futex_up increments count from 0 -> 1, none was waiting. 57 | Otherwise, set to 1 and tell kernel to wake them up. */ 58 | static inline int futex_up(struct futex *futx) 59 | { 60 | if (!__futex_up(&futx->count)) 61 | return __futex_up_slow(futx); 62 | return 0; 63 | } 64 | 65 | static inline int futex_up_fair(struct futex *futx) 66 | { 67 | /* Someone waiting? */ 68 | if (!__futex_up(&futx->count)) { 69 | futx->count = FUTEX_PASSED; 70 | __futex_commit(); 71 | /* If we wake one, they'll see it's a direct pass. */ 72 | if (sys_futex(&futx->count, FUTEX_WAKE, 1, NULL) == 1) 73 | return 0; 74 | /* Otherwise do normal slow case */ 75 | return __futex_up_slow(futx); 76 | } 77 | return 0; 78 | } 79 | 80 | /* If signal non-zero, set up that signal to come to us for FD ready. 81 | Returns fd. */ 82 | int futex_await(struct futex *futx, int signal); 83 | 84 | void futex_init(struct futex *sem, int val); 85 | #endif /* _FUTEX_H */ 86 | -------------------------------------------------------------------------------- /mythread_q.c: -------------------------------------------------------------------------------- 1 | /* Single Author info: 2 | * sskanitk Salil S Kanitkar 3 | * Group info: 4 | * jhshah Jitesh H Shah 5 | * sskanitk Salil S Kanitkar 6 | * ajalgao Aditya A Jalgaonkar 7 | */ 8 | 9 | /* We use a Queue Data structure for managing the tcb corresponding to each thread created. 10 | The queue is implemented using a double ended linked list. 11 | */ 12 | #include 13 | 14 | /* The global extern pointer defined in mythread.h which points to the head node in 15 | Queue of the Thread Control Blocks. 16 | */ 17 | mythread_private_t *mythread_q_head; 18 | 19 | /* This function initializes the Queue with a single node. 20 | */ 21 | void mythread_q_init(mythread_private_t * node) 22 | { 23 | 24 | node->prev = node; 25 | node->next = node; 26 | 27 | mythread_q_head = node; 28 | 29 | } 30 | 31 | /* This function adds a node to the Queue, at the end of the Queue. 32 | This is equivalent to Enque operation. 33 | */ 34 | void mythread_q_add(mythread_private_t * node) 35 | { 36 | 37 | if (mythread_q_head == NULL) { 38 | //Q is not initiazed yet. Create it. 39 | mythread_q_init(node); 40 | return; 41 | } 42 | //Insert the node at the end of Q 43 | node->next = mythread_q_head; 44 | node->prev = mythread_q_head->prev; 45 | mythread_q_head->prev->next = node; 46 | mythread_q_head->prev = node; 47 | 48 | return; 49 | 50 | } 51 | 52 | /* This function deleted a specified(passed as a parameter) node from the Queue. 53 | */ 54 | void mythread_q_delete(mythread_private_t * node) 55 | { 56 | 57 | mythread_private_t *p; 58 | if (node == mythread_q_head && node->next == mythread_q_head) { 59 | //There is only a single node and it is being deleted 60 | printf("The Q is now Empty!\n"); 61 | mythread_q_head = NULL; 62 | } 63 | 64 | if (node == mythread_q_head) 65 | mythread_q_head = node->next; 66 | 67 | p = node->prev; 68 | 69 | p->next = node->next; 70 | node->next->prev = p; 71 | 72 | return; 73 | 74 | } 75 | 76 | /* This function iterates over the ntire Queue and prints out the state(see mythread.h to refer to various states) 77 | of all the tcb members. 78 | */ 79 | void mythread_q_state_display() 80 | { 81 | 82 | if (mythread_q_head != NULL) { 83 | 84 | //display the Q - for debug purposes 85 | printf("\n The Q contents are -> \n"); 86 | mythread_private_t *p; 87 | p = mythread_q_head; 88 | do { //traverse to the last node in Q 89 | printf(" %d\n", p->state); 90 | p = p->next; 91 | } while (p != mythread_q_head); 92 | 93 | } 94 | 95 | } 96 | 97 | /* This function iterates over the Queue and prints out the state of the specified thread. 98 | */ 99 | mythread_private_t *mythread_q_search(unsigned long new_tid) 100 | { 101 | 102 | mythread_private_t *p; 103 | if (mythread_q_head != NULL) { 104 | 105 | p = mythread_q_head; 106 | do { //traverse to the last node in Q 107 | if (p->tid == new_tid) 108 | return p; 109 | p = p->next; 110 | } while (p != mythread_q_head); 111 | 112 | } 113 | return NULL; 114 | 115 | } 116 | -------------------------------------------------------------------------------- /mythread_yield.c: -------------------------------------------------------------------------------- 1 | /* Single Author info: 2 | * jhshah Jitesh H Shah 3 | * Group info: 4 | * jhshah Jitesh H Shah 5 | * sskanitk Salil S Kanitkar 6 | * ajalgao Aditya A Jalgaonkar 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /* Global futex. Please see the mythread_yield() function for more info */ 16 | struct futex gfutex; 17 | 18 | /* This function is called from inside yield. It finds a next suitable thread 19 | * which is in the READY state and wakes it up for execution. 20 | * 21 | * It starts looping from the next thread of the current one and hence, FIFO 22 | * is ensured. 23 | */ 24 | int __mythread_dispatcher(mythread_private_t * node) 25 | { 26 | mythread_private_t *ptr = node->next; 27 | /* Loop till we find a thread in READY state. This loop is guanrateed 28 | * to end since idle thread is ALWAYS READY. 29 | */ 30 | while (ptr->state != READY) 31 | ptr = ptr->next; 32 | 33 | /* No other thread is READY. Nothing to do */ 34 | if (ptr == node) 35 | return -1; 36 | else { 37 | DEBUG_PRINTF("Dispatcher: Wake-up:%ld Sleep:%ld %d %d\n", 38 | (unsigned long)ptr->tid, (unsigned long)node->tid, 39 | ptr->sched_futex.count, ptr->state); 40 | 41 | /* Wake up the target thread. This won't cause any races because 42 | * it is protected by a global futex. 43 | */ 44 | futex_up(&ptr->sched_futex); 45 | 46 | DEBUG_PRINTF("Dispatcher: Woken up:%ld, to %d\n", 47 | (unsigned long)ptr->tid, ptr->sched_futex.count); 48 | return 0; 49 | } 50 | } 51 | 52 | /* Yield: Yield the processor to another thread. Dispatcher selects the next 53 | * appropriate thread and wakes it up. Then current thread sleeps. 54 | */ 55 | int mythread_yield() 56 | { 57 | mythread_private_t *self; 58 | int retval; 59 | 60 | self = __mythread_selfptr(); 61 | 62 | /* Take the global futex to avoid races in yield. There was a condition 63 | * when after a wake-up the target thread races ahead and entered yield 64 | * before the first thread finised. This caused deadlocks and ugly races 65 | * when the original thread hadn't slept yet, but was tried to woken up. 66 | * So, protect yield by a global futex and make sure the current thread 67 | * atleast reduces its futex value to 0, before another one starts. 68 | */ 69 | futex_down(&gfutex); 70 | 71 | retval = __mythread_dispatcher(self); 72 | /* Only one thread. Nothing to do */ 73 | if (retval == -1) { 74 | futex_up(&gfutex); 75 | return 0; 76 | } 77 | 78 | DEBUG_PRINTF("Yield: Might sleep on first down %ld %d\n", 79 | (unsigned long)self->tid, self->sched_futex.count); 80 | /* The "if" condition was to fix a couple of race conditions. The purpose 81 | * of two futex down's is to make the process sleep on the second. But 82 | * sometimes the value of the futex is already 0, so do a conditional 83 | * down. This, alongwith the global futex, seems to alleviate maximum 84 | * races in yield. 85 | */ 86 | if (self->sched_futex.count > 0) 87 | futex_down(&self->sched_futex); 88 | 89 | futex_up(&gfutex); 90 | 91 | DEBUG_PRINTF("Yield: Might sleep on second down %ld %d\n", 92 | (unsigned long)self->tid, self->sched_futex.count); 93 | /* Sleep till another process wakes us up */ 94 | futex_down(&self->sched_futex); 95 | 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /mythread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mythread.h -- interface of user threads library 3 | */ 4 | 5 | /* Single Author info: 6 | * ajalgao Aditya A Jalgaonkar 7 | * Group info: 8 | * jhshah Jitesh H Shah 9 | * sskanitk Salil S Kanitkar 10 | * ajalgao Aditya A Jalgaonkar 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #ifndef MYTHREAD_H 20 | #define MYTHREAD_H 21 | 22 | #define FALSE 0 23 | #define TRUE 1 24 | 25 | #define RUNNING 0 26 | #define READY 1 /* Ready to be scheduled */ 27 | #define BLOCKED 2 /* Waiting on Join */ 28 | #define DEFUNCT 3 /* Dead */ 29 | 30 | typedef struct mythread_attr { 31 | unsigned long stackSize; /* Stack size to be used by this thread. Default is SIGSTKSZ */ 32 | }mythread_attr_t; 33 | 34 | /* Thread Handle exposed to the user */ 35 | typedef struct mythread { 36 | pid_t tid; /* The thread-id of the thread */ 37 | }mythread_t; 38 | 39 | /* The Actual Thread Control Block structure */ 40 | typedef struct mythread_private { 41 | 42 | pid_t tid; /* The thread-id of the thread */ 43 | int state; /* the state in which the corresponding thread will be. */ 44 | void * (*start_func) (void *); /* The func pointer to the thread function to be executed. */ 45 | void *args; /* The arguments to be passed to the thread function. */ 46 | void *returnValue; /* The return value that thread returns. */ 47 | struct mythread_private *blockedForJoin; /* Thread blocking on this thread */ 48 | struct futex sched_futex; /* Futex used by the dispatcher to schedule this thread */ 49 | struct mythread_private *prev, *next; 50 | 51 | }mythread_private_t; 52 | 53 | extern mythread_private_t *mythread_q_head; /* The pointer pointing to head node of the TCB queue */ 54 | /* add your code here */ 55 | 56 | /* 57 | * mythread_self - thread id of running thread 58 | */ 59 | mythread_t mythread_self(void); 60 | 61 | /* 62 | * mythread_create - prepares context of new_thread_ID as start_func(arg), 63 | * attr is ignored right now. 64 | * Threads are activated (run) according to the number of available LWPs 65 | * or are marked as ready. 66 | */ 67 | int mythread_create(mythread_t *new_thread_ID, 68 | mythread_attr_t *attr, 69 | void * (*start_func)(void *), 70 | void *arg); 71 | 72 | /* 73 | * mythread_yield - switch from running thread to the next ready one 74 | */ 75 | int mythread_yield(void); 76 | 77 | /* 78 | * mythread_join - suspend calling thread if target_thread has not finished, 79 | * enqueue on the join Q of the target thread, then dispatch ready thread; 80 | * once target_thread finishes, it activates the calling thread / marks it 81 | * as ready. 82 | */ 83 | int mythread_join(mythread_t target_thread, void **status); 84 | 85 | /* 86 | * mythread_exit - exit thread, awakes joiners on return 87 | * from thread_func and dequeue itself from run Q before dispatching run->next 88 | */ 89 | void mythread_exit(void *retval); 90 | 91 | /* Private functions */ 92 | 93 | /* Get the tid of the current thread */ 94 | pid_t __mythread_gettid(); 95 | 96 | /* Get the pointer to the private structure of this thread */ 97 | mythread_private_t *__mythread_selfptr(); 98 | 99 | /* Dispatcher - The thread scheduler */ 100 | int __mythread_dispatcher(mythread_private_t *); 101 | void __mythread_debug_futex_init(); 102 | 103 | /* Debugging stuff */ 104 | extern char debug_msg[1000]; 105 | extern struct futex debug_futex; 106 | 107 | #ifdef DEBUG 108 | #define DEBUG_PRINTF(...) __mythread_debug_futex_init(); \ 109 | futex_down(&debug_futex); \ 110 | sprintf(debug_msg, __VA_ARGS__); \ 111 | write(1, debug_msg, strlen(debug_msg)); \ 112 | futex_up(&debug_futex); 113 | #else 114 | #define DEBUG_PRINTF(...) do {} while(0); 115 | #endif 116 | 117 | #define ERROR_PRINTF(...) __mythread_debug_futex_init(); \ 118 | futex_down(&debug_futex); \ 119 | sprintf(debug_msg, __VA_ARGS__); \ 120 | write(1, debug_msg, strlen(debug_msg)); \ 121 | futex_up(&debug_futex); 122 | 123 | #define LOG_PRINTF(...) __mythread_debug_futex_init(); \ 124 | futex_down(&debug_futex); \ 125 | sprintf(debug_msg, __VA_ARGS__); \ 126 | write(1, debug_msg, strlen(debug_msg)); \ 127 | futex_up(&debug_futex); 128 | 129 | #endif /* MYTHREAD_H */ 130 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | What is mythread_lib? 2 | --------------------- 3 | Implementation of a non-preemptive user-level thread library - mythread. 4 | 5 | 6 | Aim/Assumptions and interface: 7 | ----------------------------- 8 | Provide a statically linked user-level thread library which provides following 9 | functions: 10 | 1. mythread_create() 11 | 2. mythread_self() 12 | 3. mythread_join() 13 | 4. mythread_yield() 14 | 5. mythread_exit() 15 | The methods have the same signature as their counterparts in pthread 16 | (POSIX-thread) library. We also implement a simple FIFO (First In 17 | First Out) based scheduling policy with one running thread (a single 18 | processor) using a doubly linked queue of threads, including an idle thread. 19 | 20 | 21 | Members: 22 | ------- 23 | Jitesh Shah - jhshah 24 | Salil Kanitkar - sskanitk 25 | Aditya Jalgaonkar - ajalgao 26 | 27 | 28 | How to Build: 29 | ------------ 30 | Our Makefile provides a bunch of targets; each one builds a particular part 31 | of the project. 32 | Refer to the following commands for building -> 33 | 34 | Compiling: 35 | $ make 36 | # This is equivalent to "make all" and "make a5" which can also be alternatively used. 37 | # It compiles all the code files, creates the corresponding object files and builds 38 | # the library file "libmythread.a". It also builds the mythread_test file - a sample 39 | # test program provided - which tests out all the mythread_ functions. 40 | 41 | Library Compilation: 42 | $ make lib 43 | 44 | Cleaning: 45 | $ make clean 46 | 47 | Test file: 48 | $ make test 49 | 50 | Tags: 51 | $ make tags 52 | # This target will create tags for both emacs and vim. We do not advertize any editor :-) 53 | 54 | Display archive contents: 55 | $ ar t libmythread.a 56 | 57 | 58 | How to use the Library: 59 | ---------------------- 60 | The mythread_test.c file provides a simple demonstration of how to use this 61 | mythread library. 62 | 63 | It creates some predefined number of threads using mythread_create(), passes 64 | a different interger variable initialized to some value to all the created 65 | threads. The main thread then does a 'mythread_join' on all of these threads. 66 | The thread function that each thread invokes increments the count by 50, and 67 | then voluntarily yields the processor (which incidentally is the only way to 68 | relinquish control by a thread). Next time when the thread gets scheduled, 69 | it increments the count again by 50 and does a 'mythread_exit()'. The main 70 | thread, collects all the counts and exists out. 71 | 72 | Basic Workflow: 73 | -------------- 74 | We use a user-level binary semaphore implementation called 'futex' - provided 75 | by the instrustor. A futex_down() by a thread causes the value of the futex to 76 | count down. If the new value is -1, thread will also block. We use this 77 | abstraction to implement scheduling and yielding of the processor by a thread. 78 | 79 | 80 | mythread_create() 81 | ---------------- 82 | We execute clone system call to create a new process with shared address space. 83 | The (optional) argument passed to mythread_create specifies the stack size to 84 | be used by the created thread. We invoke a mythread_wrapper function upon 85 | completion of clone since we dont want the newly created thread to be scheduled 86 | right-away. This wrapper does a down on the futex corresponsding to this thread 87 | making the thread sleep. 88 | 89 | 90 | mythread_yield() and dispatcher: 91 | ------------------------------- 92 | When a thread wishes to relinquish control of the CPU, it calls the 93 | mythread_yield(). Yield() will call a dispatcher() - which is our function for 94 | scheduling. It searches for the next thread in the Queue to be shceduled, and 95 | does a futex_up() on that thread causing it to wake up. Also, it will perform 96 | a futext_down() on itself causing itself to go sleep. 97 | 98 | 99 | mythread_join(): 100 | --------------- 101 | Our Thread Control Block(tcb) contains a blockedForJoin parameter which 102 | specifies a thread that has done a join on this thread. We set the state of 103 | the thread that has done a join on this thread to be DEFUNCT. This means that 104 | when this thread exits, it will wake up the thread that was waiting on this 105 | thread completion via join. If more than one thread try to do a mythread_join() 106 | on the same target thread, only one will succeed and all others will return error. 107 | 108 | 109 | Idle thread: 110 | ----------- 111 | We have implemented an idle thread which infinitely loopes over and repeatedly 112 | yields the processor. When all the other threads have exited out, idle thread 113 | also exist out - ending the program execution. 114 | 115 | 116 | Data Structures: 117 | --------------- 118 | As stated previously, we use a Double ended Queue for the imeplemntation of 119 | FIFO scheduling of threads. The binary sempahore abstraction futex is used 120 | for yielding the thread execution. 121 | 122 | 123 | References: 124 | --------- 125 | 1) man pages of all the POSIX thread related functions. 126 | 2) A cursory study of the pthread library code. 127 | 3) "A Library Implementation of POSIX Threads under UNIX" by Frank Mueller 128 | in Proceedings of the USENIX Conference, Jan 1993, pages 29-41. 129 | 3) Futex Documentation. 130 | -------------------------------------------------------------------------------- /mythread_create.c: -------------------------------------------------------------------------------- 1 | /* Single Author info: 2 | * sskanitk Salil S Kanitkar 3 | * Group info: 4 | * jhshah Jitesh H Shah 5 | * sskanitk Salil S Kanitkar 6 | * ajalgao Aditya A Jalgaonkar 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | /* We are explicitly including these files, since the flags required to be passed 14 | * clone() system call pick up the standard signal values from these header fiiles. 15 | */ 16 | #include 17 | #include 18 | #include 19 | 20 | /* We include the header files defined by us which are required for the create operations. 21 | */ 22 | #include 23 | #include 24 | #include 25 | 26 | /* To be able to use getttid(), we define a function for ourselves that 27 | directly references the system call. 28 | */ 29 | #include 30 | #include 31 | 32 | #define CLONE_SIGNAL (CLONE_SIGHAND | CLONE_THREAD) 33 | 34 | int mythread_wrapper(void *); 35 | void *mythread_idle(void *); 36 | 37 | /* The global extern pointer defined in mythread.h which points to the head node in 38 | Queue of the Thread Control Blocks. 39 | */ 40 | mythread_private_t *mythread_q_head; 41 | 42 | /* The global pointer which points to the tcb of the main thread. 43 | */ 44 | mythread_private_t *main_tcb; 45 | 46 | /* This structure is used to be able to refer to the Idle thread tcb. 47 | */ 48 | mythread_t idle_u_tcb; 49 | 50 | /* This is a global futex which we use when dispatcher operation needs to be 51 | performed which in turn is invoked by the yield call. 52 | The global futex is required to avoid the race conditions which may be 53 | invoked by mutliple threads during yield. 54 | */ 55 | extern struct futex gfutex; 56 | 57 | /* When the first mythread_create call is invoked, we create the tcb corresponding 58 | to main and idle threads. The following function adds the tcb for main thread 59 | in front of the queue. 60 | */ 61 | static int __mythread_add_main_tcb() 62 | { 63 | DEBUG_PRINTF("add_main_tcb: Creating node for Main thread \n"); 64 | main_tcb = (mythread_private_t *) malloc(sizeof(mythread_private_t)); 65 | if (main_tcb == NULL) { 66 | ERROR_PRINTF("add_main_tcb: Error allocating memory for main node\n"); 67 | return -ENOMEM; 68 | } 69 | 70 | main_tcb->start_func = NULL; 71 | main_tcb->args = NULL; 72 | main_tcb->state = READY; 73 | main_tcb->returnValue = NULL; 74 | main_tcb->blockedForJoin = NULL; 75 | 76 | /* Get the main's tid and put it in its corresponding tcb. */ 77 | main_tcb->tid = __mythread_gettid(); 78 | 79 | /* Initialize futex to zero */ 80 | futex_init(&main_tcb->sched_futex, 1); 81 | 82 | /* Put it in the Queue of thread blocks */ 83 | mythread_q_add(main_tcb); 84 | return 0; 85 | } 86 | 87 | /* The mythread_create() function. 88 | This creates a shared process context by using the clone system call. 89 | We pass the pointer to a wrapper function ( See mythread_wrapper.c ) which in turn 90 | sets up the thread environment and then calls the thread function. 91 | The mythread_attr_t argument can optionally specify the stack size to be used 92 | the newly created thread. 93 | */ 94 | int mythread_create(mythread_t * new_thread_ID, 95 | mythread_attr_t * attr, 96 | void *(*start_func) (void *), void *arg) 97 | { 98 | 99 | /* pointer to the stack used by the child process to be created by clone later */ 100 | char *child_stack; 101 | 102 | unsigned long stackSize; 103 | mythread_private_t *new_node; 104 | pid_t tid; 105 | int retval; 106 | 107 | /* Flags to be passed to clone system call. 108 | This flags variable is picked up from pthread source code - with CLONE_PTRACE removed. 109 | */ 110 | int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL 111 | | CLONE_PARENT_SETTID 112 | | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM); 113 | 114 | if (mythread_q_head == NULL) { 115 | /* This is the very first mythread_create call. Set up the Q first with tcb nodes for main thread. */ 116 | retval = __mythread_add_main_tcb(); 117 | if (retval != 0) 118 | return retval; 119 | 120 | /* Initialise the global futex */ 121 | futex_init(&gfutex, 1); 122 | 123 | /* Now create the node for Idle thread with a recursive call to mythread_create(). */ 124 | DEBUG_PRINTF("create: creating node for Idle thread \n"); 125 | mythread_create(&idle_u_tcb, NULL, mythread_idle, NULL); 126 | } 127 | 128 | /* This particular piece of code was added as a result of a weird bug encountered in the __futex_down(). 129 | * In 2.6.35 (our kernel version), all threads can access main thread's stack, but 130 | * on the OS machine, this stack is somehow private to main thread only. 131 | */ 132 | new_node = (mythread_private_t *) malloc(sizeof(mythread_private_t)); 133 | if (new_node == NULL) { 134 | ERROR_PRINTF("Cannot allocate memory for node\n"); 135 | return -ENOMEM; 136 | } 137 | 138 | /* If Stack-size argument is not provided, use the SIGSTKSZ as the default stack size 139 | * Otherwise, extract the stacksize argument. 140 | */ 141 | if (attr == NULL) 142 | stackSize = SIGSTKSZ; 143 | else 144 | stackSize = attr->stackSize; 145 | 146 | /* posix_memalign aligns the allocated memory at a 64-bit boundry. */ 147 | if (posix_memalign((void **)&child_stack, 8, stackSize)) { 148 | ERROR_PRINTF("posix_memalign failed! \n"); 149 | return -ENOMEM; 150 | } 151 | 152 | /* We leave space for one invocation at the base of the stack */ 153 | child_stack = child_stack + stackSize - sizeof(sigset_t); 154 | 155 | /* Save the thread_fun pointer and the pointer to arguments in the TCB. */ 156 | new_node->start_func = start_func; 157 | new_node->args = arg; 158 | /* Set the state as READY - READY in Q, waiting to be scheduled. */ 159 | new_node->state = READY; 160 | 161 | new_node->returnValue = NULL; 162 | new_node->blockedForJoin = NULL; 163 | /* Initialize the tcb's sched_futex to zero. */ 164 | futex_init(&new_node->sched_futex, 0); 165 | 166 | /* Put it in the Q of thread blocks */ 167 | mythread_q_add(new_node); 168 | 169 | /* Call clone with pointer to wrapper function. TCB will be passed as arg to wrapper function. */ 170 | if ((tid = 171 | clone(mythread_wrapper, (char *)child_stack, clone_flags, 172 | new_node)) == -1) { 173 | printf("clone failed! \n"); 174 | printf("ERROR: %s \n", strerror(errno)); 175 | return (-errno); 176 | } 177 | /* Save the tid returned by clone system call in the tcb. */ 178 | new_thread_ID->tid = tid; 179 | new_node->tid = tid; 180 | 181 | DEBUG_PRINTF("create: Finished initialising new thread: %ld\n", 182 | (unsigned long)new_thread_ID->tid); 183 | return 0; 184 | } 185 | --------------------------------------------------------------------------------