metalsvm/include/metalsvm/semaphore.h
Stefan Lankes 4c9855c83a redesign of the scheduler, defining of a runqueue per core
=> Currently, we work stealing isn't supported
2011-08-17 13:51:19 +02:00

212 lines
4.2 KiB
C

/*
* Copyright 2010 Stefan Lankes, Chair for Operating Systems,
* RWTH Aachen University
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of MetalSVM.
*/
/**
* @author Stefan Lankes
* @file include/metalsvm/semaphore.h
* @brief semaphore functions definition
*/
#ifndef __SEMAPHORE_H__
#define __SEMAPHORE_H__
#include <metalsvm/string.h>
#include <metalsvm/semaphore_types.h>
#include <metalsvm/spinlock.h>
#include <metalsvm/errno.h>
#include <metalsvm/time.h>
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Semaphore initialization
*
* Always init semaphores before use!
*
* @param s Pointer to semaphore structure to initialize
* @param v Resource count
*
* @return
* - 0 on success
* - -EINVAL on invalid argument
*/
inline static int sem_init(sem_t* s, unsigned int v) {
unsigned int i;
if (BUILTIN_EXPECT(!s, 0))
return -EINVAL;
s->value = v;
s->pos = 0;
for(i=0; i<MAX_TASKS; i++)
s->queue[i] = MAX_TASKS;
spinlock_irqsave_init(&s->lock);
return 0;
}
/** @brief Destroy semaphore
* @return
* - 0 on success
* - -EINVAL on invalid argument
*/
inline static int sem_destroy(sem_t* s) {
if (BUILTIN_EXPECT(!s, 0))
return -EINVAL;
spinlock_irqsave_destroy(&s->lock);
return 0;
}
/** @brief Nonblocking trywait for sempahore
*
* Will return immediately if not available
*
* @return
* - 0 on success (You got the semaphore)
* - -EINVAL on invalid argument
* - -ECANCELED on failure (You still have to wait)
*/
inline static int sem_trywait(sem_t* s) {
int ret = -ECANCELED;
if (BUILTIN_EXPECT(!s, 0))
return -EINVAL;
spinlock_irqsave_lock(&s->lock);
if (s->value > 0) {
s->value--;
ret = 0;
}
spinlock_irqsave_unlock(&s->lock);
return ret;
}
/** @brief Blocking wait for semaphore
*
* @param ms Timeout in milliseconds
* @return
* - 0 on success
* - -EINVAL on invalid argument
* - -ETIME on timer expired
*/
inline static int sem_wait(sem_t* s, uint32_t ms) {
task_t* curr_task = per_core(current_task);
if (BUILTIN_EXPECT(!s, 0))
return -EINVAL;
if (!ms) {
next_try1:
spinlock_irqsave_lock(&s->lock);
if (s->value > 0) {
s->value--;
spinlock_irqsave_unlock(&s->lock);
} else {
s->queue[s->pos] = curr_task->id;
s->pos = (s->pos + 1) % MAX_TASKS;
block_current_task();
spinlock_irqsave_unlock(&s->lock);
reschedule();
NOP2;
goto next_try1;
}
return 0;
} else {
uint32_t ticks = (ms * TIMER_FREQ) / 1000;
uint32_t remain = (ms * TIMER_FREQ) % 1000;
if (ticks) {
uint64_t deadline = get_clock_tick() + ticks;
next_try2:
spinlock_irqsave_lock(&s->lock);
if (s->value > 0) {
s->value--;
spinlock_irqsave_unlock(&s->lock);
return 0;
} else {
if (get_clock_tick() >= deadline) {
spinlock_irqsave_unlock(&s->lock);
goto timeout;
}
s->queue[s->pos] = curr_task->id;
s->pos = (s->pos + 1) % MAX_TASKS;
set_timer(deadline);
spinlock_irqsave_unlock(&s->lock);
reschedule();
NOP2;
goto next_try2;
}
}
timeout:
while (remain) {
udelay(1000);
remain--;
if (!sem_trywait(s))
return 0;
}
return -ETIME;
}
}
/** @brief Give back resource
* @return
* - 0 on success
* - -EINVAL on invalid argument
*/
inline static int sem_post(sem_t* s) {
if (BUILTIN_EXPECT(!s, 0))
return -EINVAL;
spinlock_irqsave_lock(&s->lock);
if (s->value > 0) {
s->value++;
spinlock_irqsave_unlock(&s->lock);
} else {
unsigned int k, i;
s->value++;
i = s->pos;
for(k=0; k<MAX_TASKS; k++) {
if (s->queue[i] < MAX_TASKS) {
wakeup_task(s->queue[i]);
s->queue[i] = MAX_TASKS;
break;
}
i = (i + 1) % MAX_TASKS;
}
spinlock_irqsave_unlock(&s->lock);
}
return 0;
}
#ifdef __cplusplus
}
#endif
#endif