# # Threads for MIPS. Student scaffolding. bsy, created. # # void thr_init(thread_state, thr_func, arg, stack, stack_size) # # void thr_yield(void) # # void thr_go(void) # # void thr_set_pri(thread_state, pri) # # void lock_init(lockptr) # # void lock_acquire(lockptr) # # void lock_release(lockptr) # # User's main code may call thr_init several times to initialize several # threads, then call thr_go to go multithreaded. Threads run, and yields # occur in a round-robin fashion, with the thread priority specifying how # often a thread is run when encountered, until all the threads complete. # A thread running (after thr_go) may also called thr_init to create and # start new threads. # # # Thread state -- see header.mips # .data num_threads: .word 0 max_threads: .word 0 thread_tbl: .space 1024 # 256 threads possible cur_thread: .word 0 .text # # thr_init(thread_state, thr_func, arg, stack, stack_size) LEAF # .globl thr_init thr_init: [ stuff ] jr $ra # # void thr_go(void) -- the main thread is a thread so that thr_yield # will always have some thread to run. This thread simply checks # the number of live threads and yields if there are others. # # frame pointer not saved, since no local variables # .data main_thread_data: .space THR_STATE_SIZE .text .globl thr_go thr_go: subu $sp, $sp, 4 sw $ra, 4($sp) lw $t0, num_threads sw $t0, cur_thread addu $t1, $t0, 1 sw $t1, num_threads # number left alive sw $t1, max_threads # wrap-around counting sll $t1, $t0, 2 la $t1, thread_tbl($t1) la $t2, main_thread_data sw $t2, 0($t1) li $t3, RUNNABLE sw $t3, STATE($t2) thr_go_loop: jal thr_yield lw $t0, num_threads bgt $t0, 1, thr_go_loop sw $zero, num_threads sw $zero, max_threads lw $ra, 4($sp) # we are alone! addu $sp, $sp, 4 jr $ra # # void thr_yield(void) # # First, we save the register set of the current thread, then we # find next initialized or runnable thread, and context switch to it # (i.e., load its register set). This means is that if thread is in # the INIT state, we must first initialize the thread's registers so # its $RA points to our code to handle thread completion, load the # basic thread registers (so it looks like a regular jal call), mark # the thread as RUNNABLE, and jump to the thread function; if the thread # is already running (i.e., marked as RUNNABLE), we just do a simple # context switch. # .text .globl thr_yield thr_yield: # save current context [ stuff ] sw $s0, S0OFF($t1) sw $s1, S1OFF($t1) sw $s2, S2OFF($t1) sw $s3, S3OFF($t1) sw $s4, S4OFF($t1) sw $s5, S5OFF($t1) sw $s6, S6OFF($t1) sw $s7, S7OFF($t1) sw $gp, GPOFF($t1) sw $ra, RAOFF($t1) sw $fp, FPOFF($t1) sw $sp, SPOFF($t1) [ stuff ] lw $s0, S0OFF($t1) # load new reg set lw $s1, S1OFF($t1) lw $s2, S2OFF($t1) lw $s3, S3OFF($t1) lw $s4, S4OFF($t1) lw $s5, S5OFF($t1) lw $s6, S6OFF($t1) lw $s7, S7OFF($t1) lw $gp, GPOFF($t1) lw $ra, RAOFF($t1) lw $fp, FPOFF($t1) lw $sp, SPOFF($t1) jr $ra # # void thr_set_pri(thread_state, priority) # .text .globl thr_set_pri thr_set_pri: [ stuff ] jr $ra # # void lock_init(lockptr) LEAF # .text .globl lock_init lock_init: [ stuff ] jr $ra # # void lock_acquire(lockptr) # .text .globl lock_acquire lock_acquire: [ stuff ] jr $ra # # void lock_release(lockptr) LEAF # .text .globl lock_release lock_release: [ stuff ] jr $ra