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
 |