Files
my-kern/kernel/tasks/locking.c
2021-03-26 19:57:07 +01:00

150 lines
3.6 KiB
C

//
// Created by rick on 27-02-21.
//
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <myke/tasks/locking.h>
#include <myke/tasks/task.h>
#include <myke/libk/syscall.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));
memset((uint8_t *) semaphore, 0, 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();
lock->next = NULL;
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) {
semaphore->value++;
task_lock_acquire();
if (semaphore->first_wait != NULL) {
task_signal(semaphore->first_wait->tid);
lock_fifo_entry_t *first_entry = semaphore->first_wait;
semaphore->first_wait = first_entry->next;
if (semaphore->first_wait == NULL) {
semaphore->last_wait = NULL;
}
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));
memset((uint8_t *) mutex, 0, 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();
lock->next = NULL;
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() {
spinlock_t *lock = malloc(sizeof(spinlock_t));
lock->lock = false;
return lock;
}
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);
}