This commit is contained in:
Niklas Gollenstede
2025-04-14 11:20:52 +02:00
commit 5a2e32aaeb
126 changed files with 16742 additions and 0 deletions

29
sync/bellringer.cc Normal file
View File

@@ -0,0 +1,29 @@
#include "bellringer.h"
#include "../interrupt/guard.h"
#include "../thread/thread.h"
struct Bell {
// link pointer to the next bell in the bellringers bell list
Bell *queue_link[1] = {nullptr};
Thread *thread;
size_t counter;
};
Bell **Bellringer::bell_link(Bell &obj, unsigned link_index) {
return &obj.queue_link[link_index];
}
// check: Checks whether bells are running out of time and rings them if
// necessary
void Bellringer::check(Vault &vault) { (void)vault; }
// job: Give a bell to the bellringer & ring it when the specified time ran out.
void Bellringer::sleep(Vault &vault, unsigned int ms) {
(void)vault;
(void)ms;
}
// Are there bells in the queue?
bool Bellringer::bellPending() const { return false; }

69
sync/bellringer.h Normal file
View File

@@ -0,0 +1,69 @@
/*! \file
* \brief \ref Bellringer that manages and activates time-triggered activities.
*/
#pragma once
#include "../object/queue.h"
#include "../types.h"
struct Vault;
struct Bell;
/*! \brief Manages and activates time-triggered activities.
* \ingroup ipc
*
* The Bellringer is regularly activated and checks whether any of the bells
* should ring. The bells are stored in a Queue<Bell> that is managed by the
* Bellringer. A clever implementation avoids iterating through the whole list
* for every iteration by keeping the bells sorted and storing delta times. This
* approach leads to a complexity of O(1) for the method called by the timer
* interrupt in case no bells need to be rung.
*/
class Bellringer {
// Prevent copies and assignments
Bellringer(const Bellringer&) = delete;
Bellringer& operator=(const Bellringer&) = delete;
/*! \brief List of bells currently managed.
*
* This list contains non-expired bells enqueued by job().
* These bells will be checked on every call to check().
*
* All elements that should be inserted into a Queue instance
* are required to be derived from Queue<Bell>::Node.
*/
Queue<Bell> bells;
//! Link pointer for bells
static Bell** bell_link(Bell& obj, unsigned link_index);
public:
// constructor
Bellringer() : bells(0, bell_link) {}
/*! \brief Checks whether there are bells to be rung.
*
* Every call to check elapses a tick. Once such a tick reduces a bells
* remaining time to zero, the bell will be rung.
*
* \todo(16) Implement Method
*/
void check(Vault& vault);
/*! \brief Passes a `bell` to the bellringer to be rung after `ms`
* milliseconds.
* \param bell Bell that should be rung after `ms` milliseconds
* \param ms number of milliseconds that should be waited before
* ringing the bell
*
* \todo(16) Implement Method
*/
void sleep(Vault& vault, unsigned int ms);
/*! \brief Checks whether there are enqueued bells.
* \return true if there are enqueued bells, false otherwise
*
* \todo(16) Implement Method
*/
bool bellPending() const;
};

16
sync/semaphore.cc Normal file
View File

@@ -0,0 +1,16 @@
#include "./semaphore.h"
#include "../interrupt/guard.h"
#include "../thread/thread.h"
Semaphore::Semaphore(unsigned c) { (void)c; }
Thread **Semaphore::thread_link(Thread &obj, unsigned link_index) {
(void)obj;
(void)link_index;
return nullptr;
}
void Semaphore::p(Vault &vault) { (void)vault; }
void Semaphore::v(Vault &vault) { (void)vault; }

57
sync/semaphore.h Normal file
View File

@@ -0,0 +1,57 @@
#pragma once
#include "../object/queue.h"
#include "../types.h"
/*! \file
* \brief \ref Semaphore for synchronization of threads.
*/
/*!
* \defgroup ipc Inter-Process Communication
* \brief Communication between threads
*/
// Forward declarations to break cyclic includes
struct Vault;
class Thread;
/*! \brief Semaphore used for synchronization of threads.
* \ingroup ipc
*
* The class Semaphore implements the concept of counting semaphores.
* The waiting list is provided by the base class Waitingroom.
*/
class Semaphore {
// Prevent copies and assignments
Semaphore(const Semaphore&) = delete;
Semaphore& operator=(const Semaphore&) = delete;
static Thread** thread_link(Thread& obj, unsigned link_index);
public:
/*! \brief Constructor; initialized the counter with provided value `c`
* \param c Initial counter value
*
* \todo(16) Implement Constructor
*/
explicit Semaphore(unsigned c = 0);
/*! \brief Wait for access to the critical area.
*
* Enter/Wait operation: If the counter is greater than 0, then it is
* decremented by one. Otherwise the calling thread will be enqueued
* into the Waitingroom and marked as blocked.
*
* \todo(16) Implement Method
*/
void p(Vault& vault);
/*! \brief Leave the critical area.
*
* Leave operation: If there are threads in the Waitingroom, wake the
* first one; otherwise increment the counter by one.
*
* \todo(16) Implement Method
*/
void v(Vault& vault);
};

60
sync/spinlock.h Normal file
View File

@@ -0,0 +1,60 @@
/*! \file
* \brief Contains the class Spinlock
*/
#pragma once
#include "../types.h"
/*! \brief Using Spinlocks, it is possible to serialize blocks of code
* that might otherwise run in parallel on multiple CPU cores,
* or be interleaved due to interrupts or scheduling.
*
* \ingroup sync
*
* Synchronization is implemented using a lock variable. Once a thread enters
* the critical area, it sets the lock variable (to a non-zero value); when
* this thread leaves the critical area, it resets the lock variable to zero.
* Threads trying to enter an already locked critical area, actively wait,
* continuously checking until the critical area is free again.
*
* Use the following two GCC intrinsics
* - `bool __atomic_test_and_set(void *ptr, int memorder)`
* - `void __atomic_clear (bool *ptr, int memorder)`
*
* These intrinsics are translated into atomic, architecture-specific
* CPU instructions.
*
* If you want that things just work, choose __ATOMIC_SEQ_CST as memorder.
* This is not the most efficient memory order but works reasonably well.
*
* <a
* href="https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html">Atomic
* Builtins in GCC manual</a>
*/
class Spinlock {
// Prevent copies and assignments
Spinlock(const Spinlock& copy) = delete;
Spinlock& operator=(const Spinlock&) = delete;
public:
/*! \brief Constructor; Initializes as unlocked.
*
* \todo(12) Complete Constructor (for \MPStuBS, or use \ref Ticketlock)
*
*/
consteval Spinlock() {}
/*! \brief Enters the critical area. In case the area is already locked,
* \ref lock() will actively wait until the area can be entered.
*
* \see \ref Core::pause()
* \todo(12) Implement Method (for \MPStuBS, or use \ref Ticketlock)
*/
void lock() {}
/*! \brief Unblocks the critical area.
*
* \todo(12) Implement Method (for \MPStuBS, or use \ref Ticketlock)
*/
void unlock() {}
};

52
sync/ticketlock.h Normal file
View File

@@ -0,0 +1,52 @@
/*! \file
* \brief Contains the class Ticketlock
*/
#pragma once
/*! \brief Using Ticketlocks, it is possible to serialize blocks of code
* that might otherwise run in parallel on multiple CPU cores,
* or be interleaved due to interrupts or scheduling.
*
* \ingroup sync
*
* Synchronization is implemented using a lock and a ticket variable.
* Once a thread tries to enter the critical area, it obtains a ticket by
* atomically incrementing the ticket variable and waiting until the lock
* counter reaches this ticket, if it is not there already.
* When a thread leaves the critical area, it increments the lock variable by
* one and thereby allows the next thread to enter the critical area.
*
* If you want that things just work, choose __ATOMIC_SEQ_CST as memorder.
* This is not the most efficient memory order but works reasonably well.
*
* <a
* href="https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html">Atomic
* Builtins in GCC manual</a>
*/
class Ticketlock {
// Prevent copies and assignments
Ticketlock(const Ticketlock& copy) = delete;
Ticketlock& operator=(const Ticketlock&) = delete;
public:
/*! \brief Constructor
*
* \todo(12) Complete Constructor (for \MPStuBS)
*/
consteval Ticketlock() {}
/*! \brief Enters the critical area. In case the area is already locked,
* \ref lock() will actively wait until the area can be entered.
*
* \see \ref Core::pause()
* \todo(12) Implement Method (for \MPStuBS)
*/
void lock() {}
/*! \brief Unblocks the critical area.
*
* \todo(12) Implement Method (for \MPStuBS)
*/
void unlock() {}
};