#include <sys/cpu.h>
#include "thread.h"
void sem_init(struct semaphore *sem, int count)
{
if (!!sem) {
sem->list.next = sem->list.prev = &sem->list;
sem->count = count;
}
}
mstime_t __sem_down_slow(struct semaphore *sem, mstime_t timeout)
{
irq_state_t irq;
mstime_t rv;
irq = irq_save();
if (!sem_is_valid(sem)) {
rv = -1;
} else if (sem->count >= 0) {
/* Something already freed the semaphore on us */
rv = 0;
} else if (timeout == -1) {
/* Immediate timeout */
sem->count++;
rv = -1;
} else {
/* Put the thread to sleep... */
struct thread_block block;
struct thread *curr = current();
mstime_t now = ms_timer();
block.thread = curr;
block.semaphore = sem;
block.block_time = now;
block.timeout = timeout ? now+timeout : 0;
block.timed_out = false;
curr->blocked = █
/* Add to the end of the wakeup list */
block.list.prev = sem->list.prev;
block.list.next = &sem->list;
sem->list.prev = &block.list;
block.list.prev->next = &block.list;
__schedule();
rv = block.timed_out ? -1 : ms_timer() - block.block_time;
}
irq_restore(irq);
return rv;
}
void __sem_up_slow(struct semaphore *sem)
{
irq_state_t irq;
struct thread_list *l;
irq = irq_save();
/*
* It's possible that something did a down on the semaphore, but
* didn't get to add themselves to the queue just yet. In that case
* we don't have to do anything, since the bailout clause in
* __sem_down_slow will take care of it.
*/
if (!!sem) {
l = sem->list.next;
if (l != &sem->list) {
struct thread_block *block;
block = container_of(l, struct thread_block, list);
sem->list.next = block->list.next;
block->list.next->prev = &sem->list;
block->thread->blocked = NULL;
__schedule();
}
}
irq_restore(irq);
}