Implementing our own fibers 115 Note The registers we save in our Thread Context struct are the registers that are marked as callee saved in Figure 5. 1. We need to save these since the ABI states that the callee (which will be our switch function from the perspective of the OS) needs to restore them before the caller is resumed. Next up is how we initialize the data to a newly created thread: impl Thread { fn new()-> Self { Thread { stack: vec![0_u8; DEFAULT_STACK_SIZE], ctx: Thread Context::default(), state: State::Available, } } } This is pretty easy. A new thread starts in the Available state, indicating it is ready to be assigned a task. One thing I want to point out here is that we allocate our stack here. That is not needed and is not an optimal use of our resources since we allocate memory for threads we might need instead of allocating on first use. However, this lowers the complexity in the parts of our code that have a more important focus than allocating memory for our stack. Note Once a stack is allocated it must not move! No push() on the vector or any other methods that might trigger a reallocation. If the stack is reallocated, any pointers that we hold to it are invalidated. It's worth mentioning that Vec<T> has a method called into_boxed_slice(), which returns a reference to an allocated slice Box<[T]>. Slices can't grow, so if we store that instead, we can avoid the reallocation problem. There are several other ways to make this safer, but we'll not focus on those in this example. Implementing the runtime The first thing we need to do is to initialize a new runtime to a base state. The next code segments all belong to the impl Runtime block, and I'll make sure to let you know when the block ends since it can be hard to spot the closing bracket when we divide it up as much as we do here.