// vim: set noet ts=4 sw=4: /*! \file * \brief \ref Thread abstraction required for multithreading */ /*! \defgroup thread Multithreading * \brief The Multithreading Subsystem * * The group Multithreading contains all elements that form the foundation * of CPU multiplexing. This module's objective is to provide the abstraction * thread that provides a virtualized CPU for the user's applications. */ #pragma once #include "../arch/context.h" #include "../object/queue.h" #include "../types.h" #include "../sync/semaphore.h" #include "../memory/pagetable.h" /// Stack size for each thread constexpr uint32_t STACK_SIZE = 3472; struct IpcStruct { uintptr_t ptr; size_t size; int pid; bool is_answer; IpcStruct* queue_link = nullptr; }; /*! \brief The Thread is an object used by the scheduler. * \ingroup thread */ class Thread { private: /*! \brief pointer to the next element of the readylist */ Thread* queue_link; bool isKernel; void* start; friend class Queue; friend class Semaphore; /*! \brief Memory reserved for this threads stack */ // alignas(16) char reserved_stack_space_user[STACK_SIZE]; alignas(16) char reserved_stack_space_isr[STACK_SIZE]; /*! \brief Context of the thread, used for saving and restoring the register * values when context switching. */ /*! \brief The thread's entry point. * * For the first activation of a thread, we need a "return address" * pointing to a function that will take care of calling C++ virtual * methods (e.g. \ref action()), based on the thread object pointer. * For this purpose, we use this `kickoff()` function. * * \note As this function is never actually called, but only executed by * returning from the co-routine's initial stack, it may never * return. Otherwise garbage values from the stack will be interpreted as * return address and the system might crash. * * \param param1 Thread to be started * \param param2 Second parameter (will be used later) * \param param3 Third parameter (will be used later) * */ static void kickoffUsermode (Thread *object); void map_app(void* code_paddr, uint16_t code_page_num, void* stack_vaddr, uint16_t stack_page_num); public: static void kickoff(uintptr_t param1, uintptr_t param2, uintptr_t param3); struct{ void* user; void* isr; } StackPointer; void* operator new ( size_t count )noexcept; Context context; four_lvl_paging_t* paging_tree; void* code_paddr; int code_pagenum; Semaphore ipc_sem; Queue ipc_queue; /*! \brief Unique thread id */ const int id; /*! \brief Marker for a dying thread */ volatile bool kill_flag; // Naively moving or copying esp. the (user) stack of a thread would be a // bad idea: Thread(const Thread&) = delete; Thread(Thread&&) = delete; Thread& operator=(const Thread&) = delete; Thread& operator=(Thread&&) = delete; /*! \brief Constructor * Initializes the context using \ref prepareContext with the thread's * stack space. * */ explicit Thread(); explicit Thread (bool kernel, void* start_addr, void * codeframe=0, int code_frame_num=1); /*! \brief Activates the first thread on this CPU. * * Calling the method starts the first thread on the calling CPU. * From then on, \ref Thread::resume() must be used for all subsequent * context switches. * */ void go(); void load_paging(Thread* t); /*! \brief Switches from the currently running thread to the `next` one. * * The values currently present in the callee-saved registers will be * stored in this threads context-structure, the corresponding values * belonging to `next` thread will be loaded. * \param next Pointer to the next thread. * */ void resume(Thread* next); /*! \brief Method that contains the thread's program code. * * Derived classes are meant to override this method to provide * meaningful code to be run in this thread. */ virtual void action(); // XXX: why is this not always pure virtual? // // // };