/*! \file * \brief \ref Guard synchronizes access to epilogue level */ //#pragma once #include "sync/bellringer.h" #include "../object/bbuffer.h" #include "../object/key.h" #include "../types.h" #include "epilogues.h" #include "../device/textstream.h" #include "../thread/scheduler.h" //! \brief The epilogue vault contains the protected data for the epilogue level struct Vault { Vault(); TextStream kout = TextStream(0, 80, 0, 10, true); Scheduler sch = Scheduler(); // no copy Vault(const Vault&) = delete; Vault& operator=(const Vault&) = delete; uint8_t counter; Bellringer bellringer; }; /*! \brief Lock guard that provides access to the epilogue \ref Vault * * This object automatically unlocks the \ref Guard when it goes out of scope. */ class Guarded { public: //! This constructor should only be used by the \ref Guard explicit Guarded(Vault& vault) : _vault(vault) {} //! Leave the critical section ~Guarded(); //! Access the epilogue vault Vault& vault() { return _vault; } const Vault& vault() const { return _vault; } // no copy Guarded(const Guarded&) = delete; Guarded& operator=(const Guarded&) = delete; private: Vault& _vault; }; /*! \brief Synchronizes the kernel with interrupts using the Prologue/Epilogue * Model \ingroup interrupts * * The Guard is used to synchronize between "normal" core activities (currently * just the text output, later system calls) and interrupt handling routines. * For this purpose, \ref Guard has to contain one ore more \ref BBuffer * "queues", in which \ref Epilogue functions can be added. This is necessary if * the critical section is occupied at the time when an interrupt occurs, and * the * \ref Epilogue cannot be executed immediately. The queued epilogues are * processed when leaving the critical section. * * **Hints:** * - The epilogue queue is a central data structure, whose consistency * must be ensured. The implementation provided by the \ref BBuffer is not * entirely safe against concurrency. You need to disable * interrupts during operations on the buffer. * - In \MPStuBS, you need a separate epilogue queue for each core, * in which each processor serializes *its* epilogues. However, epilogues * on different cores could then be executed in parallel, since the * critical section is managed separately on a per-core base. This must be * prevented by using a global \ref Ticketlock to avoid concurrent * execution of epilogues -- there must never be more than one epilogue * active on the whole system at the same time!
* *Please note:* This [giant lock](https://en.wikipedia.org/wiki/Giant_lock) * (synchronizing all cores) should not be confused with the (core-specific) * flag variable that marks only the entry to the epilogue level on the * corresponding core! * - Interrupts should be disabled for as short as possible. Due to this * reason, the prologue/epilogue model allows epilogues to be interrupted * by prologues. This means that interrupts should be * \ref Core::Interrupt::enable "enabled" again before the epilogue is * executed (this includes notifying the APIC about the * \ref LAPIC::endOfInterrupt() "End-Of-Interrupt") */ namespace Guard { /*! \brief Entering the critical section from level 0. * * Entering the critical section has to be handled differently depending on * the system: In a single-core system it is sufficient to mark the entry * by just setting a flag variable (since only one control flow can enter * the critical section at the same time). However, as soon as there are * multiple cores, this is no longer the case. If a core wants to enter the * critical section while *another* core is already in there, it should * (actively) wait in this method until the critical area is released again. * * \todo(13) Implement Method */ Guarded enter(); /*! \brief Leaving the critical section. * * Leaves the critical section and processes all remaining (enqueued) epilogues. * This may only be called while in level 1/2 after calling \ref enter(). * * Note: Usually, this method is called by the destructor of the \ref * Guarded. * * \todo(13) Implement Method */ void leave(); /*! \brief A prologue wants its epilogue to be processed (entering from level * 1). * * This method is called by the interrupt handlers. * Whether this is done immediately or the epilogue just enqueued to the * epilogue queue depends on whether the critical section on *this* Core is * accessible or not. * * \todo(13) Implement Method */ void relay(Epilogue handler); /*! \brief Access the epilogue vault without taking the lock. * Beware race conditions! */ const Vault& unsafeConstAccess(); } // namespace Guard