Implementing our own fibers 117 This is where we start running our runtime. It will continually call t_yield() until it returns false, which means that there is no more work to do and we can exit the process: pub fn run(&mut self)-> ! { while self. t_yield() {} std::process::exit(0); } Note yield is a reserved word in Rust, so we can't name our function that. If that was not the case, it would be my preferred name for it over the slightly more cryptic t_yield. This is the return function that we call when a thread is finished. return is another reserved keyword in Rust, so we name this t_return(). Make a note that the user of our threads does not call this; we set up our stack so this is called when the task is done: fn t_return(&mut self) { if self. current != 0 { self. threads[self. current]. state = State::Available; self. t_yield(); } } If the calling thread is the base_thread, we won't do anything. Our runtime will call t_yield for us on the base thread. If it's called from a spawned thread, we know it's finished since all threads will have a guard function on top of their stack (which we'll show further down), and the only place where this function is called is on our guard function. We set its state to Available, letting the runtime know it's ready to be assigned a new task, and then immediately call t_yield, which will schedule a new thread to be run. So, finally, we get to the heart of our runtime: the t_yield function. The first part of this function is our scheduler. We simply go through all the threads and see if any are in the Ready state, which indicates that it has a task it is ready to make progress. This could be a database call that has returned in a real-world application. If no thread is Ready, we're all done. This is an extremely simple scheduler using only a round-robin algorithm. A real scheduler might have a much more sophisticated way of deciding what task to run next. If we find a thread that's ready to be run, we change the state of the current thread from Running to Ready.