diff --git a/c_os/kernel/threads/Scheduler.cc b/c_os/kernel/threads/Scheduler.cc index e5f6e83..dec2922 100644 --- a/c_os/kernel/threads/Scheduler.cc +++ b/c_os/kernel/threads/Scheduler.cc @@ -6,6 +6,18 @@ * Beschreibung: Implementierung eines einfachen Zeitscheiben-Schedulers. * * Rechenbereite Threads werden in 'readQueue' verwaltet. * * * + * Der Scheduler wird mit 'schedule' gestartet. Neue Threads* + * können mit 'ready' hinzugefügt werden. Ein Thread muss * + * die CPU freiwillig mit 'yield' abgeben, damit andere auch* + * rechnen koennen. Ein Thread kann sich selbst mit 'exit' * + * terminieren. Ein Thread kann einen anderen Thread mit * + * 'kill' beenden. Ein erzwungener Threadwechsel erfolgt * + * mit der Funktion 'preempt', welche von der Timer-ISR * + * aufgerufen wird. * + * * + * Zusaetzlich gibt es nun fuer die Semaphore zwei neue * + * Funktionen 'block' und 'deblock'. * + * * * Autor: Michael, Schoettner, HHU, 23.11.2018 * *****************************************************************************/ @@ -148,3 +160,34 @@ void Scheduler::preempt() { this->readyQueue.enqueue(this->get_active()); this->dispatch(next); } + +/***************************************************************************** + * Methode: Scheduler::block * + *---------------------------------------------------------------------------* + * Beschreibung: Aufrufer ist blockiert. Es soll auf den naechsten Thread * + * umgeschaltet werden. Der Aufrufer soll nicht in die * + * readyQueue eingefuegt werden und wird extern verwaltet. * + * Wird bei uns nur fuer Semaphore verwendet. Jede Semaphore* + * hat eine Warteschlange wo der Thread dann verwaltet wird.* + * Die Methode kehrt nicht zurueck, sondern schaltet um. * + *****************************************************************************/ +void Scheduler::block() { + + /* hier muss Code eingefuegt werden */ +} + +/***************************************************************************** + * Methode: Scheduler::deblock * + *---------------------------------------------------------------------------* + * Beschreibung: Thread 'that' deblockieren. 'that' wird nur in die * + * readyQueue eingefuegt und dann zurueckgekehrt. In der * + * einfachsten Form entspricht diese Funktion exakt 'ready' * + * Man koennte alternativ aber den deblockierten Thread auch* + * am Anfang der readyQueue einfuegen, um ihn zu beorzugen. * + * * + * Parameter: that: Thread der deblockiert werden soll. * + *****************************************************************************/ +void Scheduler::deblock(Thread& that) { + + /* hier muss Code eingefuegt werden */ +} diff --git a/c_os/kernel/threads/Scheduler.h b/c_os/kernel/threads/Scheduler.h index 543210c..e3ac30f 100644 --- a/c_os/kernel/threads/Scheduler.h +++ b/c_os/kernel/threads/Scheduler.h @@ -32,7 +32,7 @@ public: // Scheduler initialisiert? // Zeitgeber-Unterbrechung kommt evt. bevor der Scheduler fertig // intiialisiert wurde! - bool isInitialized() { return initialized; } + bool isInitialized() const { return initialized; } // ruft nur der Idle-Thread (erster Thread der vom Scheduler gestartet wird) void setInitialized() { initialized = true; } @@ -54,6 +54,9 @@ public: // Thread umschalten; wird aus der ISR des PITs gerufen void preempt(); + + void block(); + void deblock(Thread& that); }; #endif diff --git a/c_os/lib/Semaphore.h b/c_os/lib/Semaphore.h new file mode 100755 index 0000000..2fb682e --- /dev/null +++ b/c_os/lib/Semaphore.h @@ -0,0 +1,39 @@ +/***************************************************************************** + * * + * S E M A P H O R E * + * * + *---------------------------------------------------------------------------* + * Beschreibung: Implementierung von Sempahor-Objekten. * + * * + * Autor: Michael Schoettner, 2.9.2016 * + *****************************************************************************/ + +#ifndef __Semaphore_include__ +#define __Semaphore_include__ + +#include "lib/Queue.h" +#include "lib/SpinLock.h" + +class Semaphore { + +private: + Semaphore(const Semaphore& copy) = delete; // Verhindere Kopieren + + // Queue fuer wartende Threads. + Queue waitQueue; + SpinLock lock; + + int counter; + +public: + // Konstruktor: Initialisieren des Semaphorzaehlers + Semaphore(int c) : counter(c) {} + + // 'Passieren': Warten auf das Freiwerden eines kritischen Abschnitts. + void p(); + + // 'Vreigeben': Freigeben des kritischen Abschnitts. + void v(); +}; + +#endif diff --git a/c_os/lib/SpinLock.cc b/c_os/lib/SpinLock.cc new file mode 100644 index 0000000..6f2d5f5 --- /dev/null +++ b/c_os/lib/SpinLock.cc @@ -0,0 +1,63 @@ +/***************************************************************************** + * * + * S P I N L O C K * + * * + *---------------------------------------------------------------------------* + * Beschreibung: Implementierung eines Spinlocks mithilfe der cmpxchg * + * Instruktion. * + * * + * Autor: Michael Schoettner, 2.2.2018 * + *****************************************************************************/ + +#include "lib/SpinLock.h" +#include "kernel/Globals.h" + +/***************************************************************************** + * Methode: CAS * + *---------------------------------------------------------------------------* + * Parameter: *ptr Adresse der Variable des Locks * + * old Wert gegen den verglichen wird * + * _new Wert der gesetzt werden soll * + * * + * Beschreibung: Semantik der Funktion CAS = Cmompare & Swap: * + * if old == *ptr then * + * *ptr := _new * + * return prev * + *****************************************************************************/ +static inline unsigned long CAS(unsigned long* ptr, unsigned long old, unsigned long _new) { + unsigned long prev; + + /* + AT&T/UNIX assembly syntax + + The 'volatile' keyword after 'asm' indicates that the instruction + has important side-effects. GCC will not delete a volatile asm if + sit is reachable. + */ + asm volatile("lock;" // prevent race conditions with other cores + "cmpxchg %1, %2;" // %1 = _new; %2 = *ptr + // constraints + : "=a"(prev) // output: =a: RAX -> prev (%0)) + : "r"(_new), "m"(*ptr), "a"(old) // input = %1, %2, %3 (r=register, m=memory, a=accumlator = eax + : "memory"); // ensures assembly block will not be moved by gcc + + return prev; // return pointer instead of prev to prevent unnecessary second call +} + +/***************************************************************************** + * Methode: SpinLock::acquire * + *---------------------------------------------------------------------------* + * Beschreibung: Lock belegen. * + *****************************************************************************/ +void SpinLock::acquire() { + while (CAS(ptr, 0, 1) != 0) {} +} + +/***************************************************************************** + * Methode: SpinLock::release * + *---------------------------------------------------------------------------* + * Beschreibung: Lock freigeben. * + *****************************************************************************/ +void SpinLock::release() { + lock = 0; +} diff --git a/c_os/lib/SpinLock.h b/c_os/lib/SpinLock.h new file mode 100644 index 0000000..bdf622d --- /dev/null +++ b/c_os/lib/SpinLock.h @@ -0,0 +1,31 @@ +/***************************************************************************** + * * + * S P I N L O C K * + * * + *---------------------------------------------------------------------------* + * Beschreibung: Implementierung eines Spinlocks mithilfe der cmpxchg * + * Instruktion. * + * * + * Autor: Michael Schoettner, 2.2.2018 * + *****************************************************************************/ + +#ifndef __SpinLock_include__ +#define __SpinLock_include__ + +class SpinLock { + +private: + SpinLock(const SpinLock& copy) = delete; // Verhindere Kopieren + + unsigned long lock; + unsigned long* ptr; + +public: + SpinLock() : lock(0), ptr(&lock) {} + + void acquire(); + + void release(); +}; + +#endif