I've got a project that is sort of a virtual operating system.
In this project, the Kernel class is responsible for creating Process classes. The process class consists of Thread classes and the Thread class will consist of a CPU class. The CPU class is actually a CPU emulator and the Thread is therefore an "emulated" thread. So when the CPU class encounters an interrupt instruction, it needs to be handled by the kernel because it is usually a system call. The Kernel class cannot actually see the CPU class directly, it is embedded in the Thread class, which is embedded in the Process class.
The approach I'm using now uses a InterruptHandler class, which handles the system calls and breakpoints. Here's what the code looks like.
class CPU final { std::shared_ptr<MemoryBus> memoryBus; std::shared_ptr<InterruptHandler> interruptHandler; std::uint32_t regs[32]; public: void Run(unsigned int steps) { for (decltype(steps) i = 0; i < steps; i++) RunInst(); } protected: // This function will call the interrupt // handler if it encounters an interrupt instruction. void RunInst(); }; class Thread final { std::shared_ptr<CPU> cpu; std::shared_ptr<InterruptHandler> interruptHandler; public: Thread() : cpu(new CPU) { } void Run(unsigned int steps) { cpu->Run(steps); } }; class Process final { std::vector<std::shared_ptr<Threads>> threads; std::shared_ptr<MemoryMap> memoryMap; std::shared_ptr<InterruptHandler> interruptHandler; public: void Run(unsigned int steps) { for (auto &t : threads) t->Run(steps); } }; class InterruptHandler { std::shared_ptr<FS> fs; public: void HandleInterrupt(CPU &cpu, int interrupt_type); void HandleSyscall(CPU &cpu, int syscall_type); void HandleWrite(CPU &cpu, int fd, const void *buf, unsigned int len); // More system calls follow }; class Kernel final { std::vector<std::shared_ptr<Process>> processes; std::vector<InterruptHandler> interruptHandler; public: void Run(unsigned int steps) { for (auto &p : processes) p->Run(steps); } void AddProcess(const std::string &path) { // opens process, assigns interrupt handler processes.emplace_back(process); } }; The problem here is that the interrupt handler class has to be passed to just about every sub class of the Process class, including the Process class. Also, the InterruptClass seems like it's going to end up containing a lot of the same data as the Kernel class. I don't like this because I feel like it violates the principle of "don't repeat yourself."
I know there's a better way of designing this but I haven't really figured it out yet. Is the Proxy design pattern applicable to this scenario? What about using signals and slots, similar to Qt and also described here?
I've left out the URL of this project, because I don't want to come off as self promoting. If the code I provided is too incomplete, I'll reference files in the project for more info.