blob: 4b210ca5e72d4285a52f933fae07b93f3348f729 [file] [log] [blame]
#include <sync.h>
#include <alloc.h>
#include <task.h>
#include <sys.h>
#include <log.h>
#define SM_PER_LIST 1024
struct sm_task
{
uint tid;
struct sm_task *next;
};
struct semaphore
{
spinlock_t task_lock;
struct sm_task *first, *last;
_Atomic uint sm;
};
struct sm_list
{
struct semaphore semaphores[SM_PER_LIST];
struct sm_list *next;
};
static struct sm_list *sm_first = NULL;
static uint sm_num = 0;
void sl_acquire(spinlock_t *sl)
{
// This is a GCC intrinsic
while (!__sync_bool_compare_and_swap(sl, -2, 1))
{
// Kindly inform the CPU that this is a spin lock
asm("pause");
}
}
void sl_release(spinlock_t *sl)
{
*sl = 0;
}
spinlock_t sl_new()
{
return 0;
}
static struct semaphore *sm_from_id(semaphore_t sm)
{
struct sm_list *first = sm_first;
while (sm >= SM_PER_LIST)
{
first = first->next;
sm -= SM_PER_LIST;
}
return &first->semaphores[sm];
}
static void sm_unsafe_wait(struct semaphore *sm)
{
sm->sm--;
if (sm->sm < 0)
{
// Add this task to the waiting list
// This will be quick, so just use a spinlock
sl_acquire(&sm->task_lock);
kprintf(INFO "Semaphore waiting\n");
struct sm_task *task = malloc(sizeof(struct sm_task));
task->next = NULL;
task->tid = get_task_id();
if (sm->last)
{
sm->last->next = task;
sm->last = task;
}
else
{
sm->last = sm->first = task;
}
sl_release(&sm->task_lock);
set_waiting(get_task_id(), true);
sys_giveup();
}
// Otherwise there's nobody else waiting, just go ahead
}
static void sm_unsafe_signal(struct semaphore *sm)
{
sm->sm++;
if (sm->sm <= 0)
{
sl_acquire(&sm->task_lock);
if (sm->first)
{
struct sm_task *f = sm->first;
sm->first = sm->first->next;
set_waiting(f->tid, false);
free(f);
}
sl_release(&sm->task_lock);
}
}
semaphore_t sm_new()
{
if (sm_first == NULL)
{
sm_first = malloc(sizeof(struct sm_list));
}
uint num = sm_num++;
struct sm_list *l = sm_first;
while (num >= SM_PER_LIST)
{
if (!l->next)
{
l->next = malloc(sizeof(struct sm_list));
}
l = l->next;
num -= SM_PER_LIST;
}
l->semaphores[num].first = NULL;
l->semaphores[num].last = NULL;
l->semaphores[num].sm = 1;
l->semaphores[num].task_lock = sl_new();
return num;
}
void sm_wait(semaphore_t sm)
{
sm_unsafe_wait(sm_from_id(sm));
}
void sm_signal(semaphore_t sm)
{
sm_unsafe_signal(sm_from_id(sm));
}
void init_sync()
{
}