smp: Add Condition, separate SleepUntil from LastTick

This commit is contained in:
Edgaru089 2021-11-11 01:50:02 +08:00
parent 9eeb9c8632
commit 2fd9209b85
6 changed files with 137 additions and 16 deletions

71
smp/condiction.c Normal file
View File

@ -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;
}

44
smp/condiction.h Normal file
View File

@ -0,0 +1,44 @@
#pragma once
#include "../main.h"
#include "../util/vector.h"
#include <stdint.h>
#include <stdbool.h>
#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

View File

@ -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 sleepUntil;
smp_Condition *waitCondition;
// Last-saved thread state after preemptive context switch
smp_thread_State state;

View File

@ -22,6 +22,8 @@ smp_thread_ID smp_thread_Init() {
t->nice = SMP_NICENESS_DEFAULT;
t->id = id;
t->lastTick = 1;
t->sleepUntil = 0;
t->waitCondition = NULL;
__smp_Count = 1;
__smp_Current = kMalloc(sizeof(void *) * __smp_Count);
@ -44,6 +46,8 @@ smp_thread_ID smp_thread_Start(void *entry, const smp_thread_Arguments *args, un
t->nice = nice;
t->id = id;
t->lastTick = __smp_Now;
t->sleepUntil = 0;
t->waitCondition = NULL;
t->state.cs = GDT_EXEC_SELECTOR;
t->state.ss = 0;
@ -87,7 +91,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();
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "../main.h"
#include <stdint.h>
#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);

View File

@ -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