Files
my-kern/kernel/tasks/locking.c

139 lines
3.3 KiB
C

//
// Created by rick on 27-02-21.
//
#include "locking.h"
#include <mem/malloc.h>
#include <tasks/task.h>
#include <libk/syscall.h>
#include <stdbool.h>
#include <libc/kprintf.h>
typedef struct lock_fifo_entry {
uint32_t tid;
struct lock_fifo_entry *next;
} lock_fifo_entry_t;
struct semaphore {
volatile int32_t value;
lock_fifo_entry_t *first_wait;
lock_fifo_entry_t *last_wait;
};
struct mutex {
volatile bool value;
lock_fifo_entry_t *first_wait;
lock_fifo_entry_t *last_wait;
};
struct spinlock {
volatile uint32_t lock;
};
semaphore_t *semaphore_create(int32_t start) {
semaphore_t *semaphore = malloc(sizeof(semaphore_t));
semaphore->value = start;
return semaphore;
}
void semaphore_wait(semaphore_t *semaphore) {
if (__sync_sub_and_fetch(&semaphore->value, 1) == 0) {
return; // first to lock
}
task_lock_acquire();
lock_fifo_entry_t *lock = malloc(sizeof(lock_fifo_entry_t));
lock->tid = task_get_current_tid();
if (semaphore->first_wait == NULL) {
semaphore->first_wait = lock;
} else {
semaphore->last_wait->next = lock;
}
semaphore->last_wait = lock;
task_lock_free();
syscall_job_suspend();
}
void semaphore_signal(semaphore_t *semaphore) {
if (__sync_add_and_fetch(&semaphore->value, 1) >= 1) {
return; // last in queue
}
task_lock_acquire();
task_signal(semaphore->first_wait->tid);
lock_fifo_entry_t *first_entry = semaphore->first_wait;
semaphore->first_wait = first_entry->next;
free(first_entry);
task_lock_free();
}
void semaphore_free(semaphore_t *semaphore) {
if (semaphore->value < 1) {
printf("WARN: freeing semaphore which still has a task waiting\n");
}
free(semaphore);
}
mutex_t *mutex_create() {
mutex_t *mutex = malloc(sizeof(mutex_t));
mutex->value = 1;
return mutex;
}
void mutex_acquire(mutex_t *mutex) {
if (__sync_bool_compare_and_swap(&mutex->value, true, false) == true) {
return; // first one to lock
}
task_lock_acquire();
lock_fifo_entry_t *lock = malloc(sizeof(lock_fifo_entry_t));
lock->tid = task_get_current_tid();
if (mutex->first_wait == NULL) {
mutex->first_wait = lock;
} else {
mutex->last_wait->next = lock;
}
mutex->last_wait = lock;
task_lock_free();
syscall_job_suspend();
}
void mutex_release(mutex_t *mutex) {
task_lock_acquire();
if (mutex->first_wait == NULL) {
mutex->value = true;
task_lock_free();
return; // no one left
}
lock_fifo_entry_t *entry = mutex->first_wait;
task_signal(entry->tid);
mutex->first_wait = entry->next;
free(entry);
task_lock_free();
}
void mutex_free(mutex_t *mutex) {
if (mutex->value != true) {
printf("WARN: freeing mutex which still has a task waiting\n");
}
free(mutex);
}
spinlock_t *spinlock_create() {
return malloc(sizeof(spinlock_t));
}
void spinlock_acquire(spinlock_t *spinlock) {
while (!__sync_bool_compare_and_swap(&spinlock->lock, false, true));
__sync_synchronize();
}
void spinlock_release(spinlock_t *spinlock) {
__sync_synchronize();
spinlock->lock = false;
}
void spinlock_free(spinlock_t *spinlock) {
if (spinlock->lock != false) {
printf("WARN: freeing spinlock which is still spinning\n");
}
free(spinlock);
}