metal-cos/sys/kern/waitchannel.c
2023-10-16 15:07:17 -04:00

132 lines
2.5 KiB
C

/*
* Copyright (c) 2023 Ali Mashtizadeh
* All rights reserved.
*/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <sys/cdefs.h>
#include <sys/kassert.h>
#include <sys/kdebug.h>
#include <sys/queue.h>
#include <sys/thread.h>
#include <sys/spinlock.h>
#include <sys/waitchannel.h>
Spinlock chanListLock;
LIST_HEAD(ChanListHead, WaitChannel) chanList = LIST_HEAD_INITIALIZER(chanList);
void
WaitChannel_EarlyInit()
{
Spinlock_Init(&chanListLock, "WaitChannel List", SPINLOCK_TYPE_NORMAL);
}
void
WaitChannel_Init(WaitChannel *wchan, const char *name)
{
TAILQ_INIT(&wchan->chanQueue);
strncpy(&wchan->name[0], name, WAITCHANNEL_NAMELEN);
Spinlock_Init(&wchan->lock, name, SPINLOCK_TYPE_NORMAL);
Spinlock_Lock(&chanListLock);
LIST_INSERT_HEAD(&chanList, wchan, chanList);
Spinlock_Unlock(&chanListLock);
}
void
WaitChannel_Destroy(WaitChannel *wchan)
{
ASSERT(TAILQ_EMPTY(&wchan->chanQueue));
Spinlock_Lock(&chanListLock);
LIST_REMOVE(wchan, chanList);
Spinlock_Unlock(&chanListLock);
Spinlock_Destroy(&wchan->lock);
}
/**
* WaitChannel_Lock --
*
* Acquires the wait channel lock.
*/
void
WaitChannel_Lock(WaitChannel *wchan)
{
Spinlock_Lock(&wchan->lock);
}
/**
* WaitChannel_Sleep --
*
* Places the current thread to asleep while releasing the wait channel lock.
*
* Side Effect:
* Retains a reference to thread until the thread is woken up.
*/
void
WaitChannel_Sleep(WaitChannel *wchan)
{
Thread *thr = Sched_Current();
Sched_SetWaiting(thr);
TAILQ_INSERT_TAIL(&wchan->chanQueue, thr, chanQueue);
Spinlock_Unlock(&wchan->lock);
Sched_Scheduler();
}
/**
* WaitChannel_Wake --
*
* Wake up a single thread.
*
* Side Effects:
* Releases the thread reference once complete.
*/
void
WaitChannel_Wake(WaitChannel *wchan)
{
Thread *thr;
Spinlock_Lock(&wchan->lock);
thr = TAILQ_FIRST(&wchan->chanQueue);
if (thr != NULL) {
TAILQ_REMOVE(&wchan->chanQueue, thr, chanQueue);
Sched_SetRunnable(thr);
Thread_Release(thr);
}
Spinlock_Unlock(&wchan->lock);
}
/**
* WaitChannel_WakeAll --
*
* Wakes up all threads currently sleeping on the wait channel.
*
* Side Effects:
* Releases all thread references.
*/
void
WaitChannel_WakeAll(WaitChannel *wchan)
{
Thread *thr;
Thread *thrTemp;
Spinlock_Lock(&wchan->lock);
TAILQ_FOREACH_SAFE(thr, &wchan->chanQueue, chanQueue, thrTemp) {
TAILQ_REMOVE(&wchan->chanQueue, thr, chanQueue);
Sched_SetRunnable(thr);
Thread_Release(thr);
}
Spinlock_Unlock(&wchan->lock);
}