You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
122 lines
4.4 KiB
C++
122 lines
4.4 KiB
C++
/*! \file
|
|
* \brief \ref Guard synchronizes access to epilogue level
|
|
*/
|
|
|
|
#pragma once
|
|
#include "../object/bbuffer.h"
|
|
#include "../object/key.h"
|
|
#include "../types.h"
|
|
#include "epilogues.h"
|
|
|
|
//! \brief The epilogue vault contains the protected data for the epilogue level
|
|
struct Vault {
|
|
Vault();
|
|
// no copy
|
|
Vault(const Vault&) = delete;
|
|
Vault& operator=(const Vault&) = delete;
|
|
};
|
|
|
|
/*! \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!<br>
|
|
* *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
|