diff --git a/smp/condiction.c b/smp/condiction.c new file mode 100644 index 0000000..06e2886 --- /dev/null +++ b/smp/condiction.c @@ -0,0 +1,71 @@ + +#include "condiction.h" +#include "kthread.h" +#include "internal.h" +#include "../interrupt/interrupt.h" +#include "../memory/memory.h" + + +smp_Condition *smp_Condition_Create() { + smp_Condition *c = kMalloc(sizeof(smp_Condition)); + c->threads = vector_Create(sizeof(__smp_Thread *)); + return c; +} + +void smp_Condition_Destroy(smp_Condition *c) { + vector_Destroy(c->threads); + kFree(c); +} + +void *smp_Condition_Wait(smp_Condition *c) { + INTERRUPT_DISABLE; + + __smp_Thread *t = __smp_Current[0]; + t->waitCondition = c; + vector_Push(c->threads, &t); + + INTERRUPT_RESTORE; + smp_thread_Yield(); + return 0; +} + +bool smp_Condition_NotifyOne(smp_Condition *c, void *data) { + INTERRUPT_DISABLE; + + uintptr_t size = vector_Size(c->threads); + if (size == 0) { + INTERRUPT_RESTORE; + return false; + } + + __smp_Thread *last = *(__smp_Thread **)vector_At(c->threads, size - 1); + last->waitCondition = NULL; + vector_Resize(c->threads, size - 1); + + INTERRUPT_RESTORE; + return true; +} + +// NotifyAll unblocks all waiting threads. +// +// The data is (for now) not sent. +// +// Returns the number of threads unblocked. +int smp_Condition_NotifyAll(smp_Condition *c, void *data) { + INTERRUPT_DISABLE; + + uintptr_t size = vector_Size(c->threads); + if (size == 0) { + INTERRUPT_RESTORE; + return size; + } + + for (uintptr_t i = 0; i < size; i++) { + __smp_Thread *t = *(__smp_Thread **)vector_At(c->threads, i); + t->waitCondition = NULL; + } + vector_Clear(c->threads); + + INTERRUPT_RESTORE; + return true; +} diff --git a/smp/condiction.h b/smp/condiction.h new file mode 100644 index 0000000..87d8a0a --- /dev/null +++ b/smp/condiction.h @@ -0,0 +1,44 @@ +#pragma once + +#include "../main.h" +#include "../util/vector.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +// Condition is a waiting condition. +typedef struct { + vector_Vector *threads; // threads waiting on the condition +} smp_Condition; + +// Create allocates a new, empty Condition. +smp_Condition *smp_Condition_Create(); + +void smp_Condition_Destroy(smp_Condition *con); + +// Wait waits until the condition is notified. +// +// The returned data is (for now) useless. +void *smp_Condition_Wait(smp_Condition *con); + +// NotifyOne unblocks (at most) one waiting thread. +// +// The data is (for now) not sent. +// +// If there are actually threads waiting, true is returned. +bool smp_Condition_NotifyOne(smp_Condition *con, void *data); + +// NotifyAll unblocks all waiting threads. +// +// The data is (for now) not sent. +// +// Returns the number of threads unblocked. +int smp_Condition_NotifyAll(smp_Condition *con, void *data); + +#ifdef __cplusplus +} +#endif diff --git a/smp/internal.h b/smp/internal.h index d751c53..21ddc1d 100644 --- a/smp/internal.h +++ b/smp/internal.h @@ -2,6 +2,7 @@ #include "kthread.h" #include "kthread_switch.h" +#include "condiction.h" #include "../util/tree.h" @@ -14,8 +15,9 @@ typedef struct { unsigned int nice; // Last tick at which the thread started waiting - // More than Now means the thread is actively sleeping and is not to be resumed. - uint64_t lastTick; + uint64_t lastTick; + uint64_t sleepUntil; + smp_Condition *waitCondition; // Last-saved thread state after preemptive context switch smp_thread_State state; diff --git a/smp/kthread.c b/smp/kthread.c index 4fbe075..820a555 100644 --- a/smp/kthread.c +++ b/smp/kthread.c @@ -87,7 +87,7 @@ int smp_thread_Nice(smp_thread_ID id, int newnice) { void smp_thread_Sleep(int ticks) { INTERRUPT_DISABLE; - __smp_Current[0]->lastTick = __smp_Now + ticks; + __smp_Current[0]->sleepUntil = __smp_Now + ticks; INTERRUPT_RESTORE; smp_thread_Yield(); } diff --git a/smp/kthread.h b/smp/kthread.h index f5a0fc8..ed7373b 100644 --- a/smp/kthread.h +++ b/smp/kthread.h @@ -1,5 +1,6 @@ #pragma once +#include "../main.h" #include #ifdef __cplusplus @@ -37,7 +38,7 @@ smp_thread_ID smp_thread_Start(void *entry, const smp_thread_Arguments *args, un int smp_thread_Nice(smp_thread_ID id, int newnice); // Yield pauses the execution of the current thread, possibly switching to another. -void smp_thread_Yield(); +SYSV_ABI void smp_thread_Yield(); // Sleep sleeps for a given amount of ticks (1024Hz) void smp_thread_Sleep(int ticks); diff --git a/smp/kthread_switch.c b/smp/kthread_switch.c index 0abb604..7080b3c 100644 --- a/smp/kthread_switch.c +++ b/smp/kthread_switch.c @@ -31,32 +31,24 @@ SYSV_ABI uintptr_t __smp_Switch() { __smp_Thread *t = __smp_Current[0]; - tree_Node *node = 0; + uint64_t priority = UINT64_MAX; // insert the current thread back into the waiting queue if (t) { - uint64_t priority = t->nice + (t->lastTick > __smp_Now ? t->lastTick : __smp_Now); // new priority for the thread - bool ok = false; - do { - node = tree_InsertNode(__smp_ThreadsWaiting, priority, &ok); - priority++; - } while (!ok); - NODE_POINTER(node) = t; + priority = t->nice + (t->lastTick > __smp_Now ? t->lastTick : __smp_Now); // new priority for the thread } //printTree(__smp_Threads->root, 0, 0); //printTree(__smp_ThreadsWaiting->root, 0, 0); tree_Node *first = tree_FirstNode(__smp_ThreadsWaiting); //io_Printf(" first0.id=%d\n", NODE_POINTER(first) ? NODE_POINTER(first)->id : 0); - while (first && NODE_POINTER(first)->lastTick > __smp_Now) { + while (first && (NODE_POINTER(first)->sleepUntil > __smp_Now || NODE_POINTER(first)->waitCondition != NULL)) { //io_Printf(" iterating, .id=%d\n", NODE_POINTER(first) ? NODE_POINTER(first)->id : 0); first = tree_Node_Next(first); } - if (first == node) { + if ((t->sleepUntil <= __smp_Now && t->waitCondition == NULL) && first->key > priority) { // the current thread is still the first, return //io_Printf(" Not context switching, still running %d\n", t ? t->id : 0); - if (node) - tree_Delete(__smp_ThreadsWaiting, node); return 0; } @@ -64,6 +56,13 @@ SYSV_ABI uintptr_t __smp_Switch() { // first save the current thread context t->lastTick = __smp_Now; memcpy(&t->state, &__smp_IntSwitch_LastState, sizeof(smp_thread_State)); + tree_Node *node = 0; + bool ok = false; + do { + node = tree_InsertNode(__smp_ThreadsWaiting, priority, &ok); + priority++; + } while (!ok); + NODE_POINTER(node) = t; if (!first) { // no thread available, load a dummy idle thread