diff options
-rw-r--r-- | kern/semaphore.c | 107 | ||||
-rw-r--r-- | kern/semaphore.h | 89 | ||||
-rw-r--r-- | kern/semaphore_i.h | 42 |
3 files changed, 89 insertions, 149 deletions
diff --git a/kern/semaphore.c b/kern/semaphore.c index a95f17fa..0e3c6c19 100644 --- a/kern/semaphore.c +++ b/kern/semaphore.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Richard Braun. + * Copyright (c) 2017-2019 Richard Braun. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,43 +16,37 @@ */ #include <assert.h> +#include <errno.h> #include <stdbool.h> -#include <stddef.h> #include <stdint.h> #include <kern/semaphore.h> #include <kern/semaphore_i.h> #include <kern/sleepq.h> -static int -semaphore_wait_slow_common(struct semaphore *semaphore, - bool timed, uint64_t ticks) +void +semaphore_init(struct semaphore *semaphore, uint16_t value, uint16_t max_value) +{ + assert(value <= max_value); + + semaphore->value = value; + semaphore->max_value = max_value; +} + +int +semaphore_trywait(struct semaphore *semaphore) { struct sleepq *sleepq; unsigned long flags; - unsigned int prev; int error; - error = 0; - sleepq = sleepq_lend_intr_save(semaphore, false, &flags); - for (;;) { - prev = semaphore_dec(semaphore); - - if (prev != 0) { - break; - } - - if (!timed) { - sleepq_wait(sleepq, "sem"); - } else { - error = sleepq_timedwait(sleepq, "sem", ticks); - - if (error) { - break; - } - } + if (semaphore->value == 0) { + error = EAGAIN; + } else { + semaphore->value--; + error = 0; } sleepq_return_intr_restore(sleepq, flags); @@ -61,33 +55,72 @@ semaphore_wait_slow_common(struct semaphore *semaphore, } void -semaphore_wait_slow(struct semaphore *semaphore) +semaphore_wait(struct semaphore *semaphore) { - int error; + struct sleepq *sleepq; + unsigned long flags; + + sleepq = sleepq_lend_intr_save(semaphore, false, &flags); + + for (;;) { + if (semaphore->value != 0) { + semaphore->value--; + break; + } + + sleepq_wait(sleepq, "sem"); + } - error = semaphore_wait_slow_common(semaphore, false, 0); - assert(!error); + sleepq_return_intr_restore(sleepq, flags); } int -semaphore_timedwait_slow(struct semaphore *semaphore, uint64_t ticks) +semaphore_timedwait(struct semaphore *semaphore, uint64_t ticks) { - return semaphore_wait_slow_common(semaphore, true, ticks); + struct sleepq *sleepq; + unsigned long flags; + int error; + + sleepq = sleepq_lend_intr_save(semaphore, false, &flags); + + for (;;) { + if (semaphore->value != 0) { + semaphore->value--; + error = 0; + break; + } + + error = sleepq_timedwait(sleepq, "sem", ticks); + + if (error) { + break; + } + } + + sleepq_return_intr_restore(sleepq, flags); + + return error; } -void -semaphore_post_slow(struct semaphore *semaphore) +int +semaphore_post(struct semaphore *semaphore) { struct sleepq *sleepq; unsigned long flags; + int error; - sleepq = sleepq_acquire_intr_save(semaphore, false, &flags); + sleepq = sleepq_lend_intr_save(semaphore, false, &flags); - if (sleepq == NULL) { - return; + if (semaphore->value == semaphore->max_value) { + error = EOVERFLOW; + } else { + assert(semaphore->value < semaphore->max_value); + semaphore->value++; + sleepq_signal(sleepq); + error = 0; } - sleepq_signal(sleepq); + sleepq_return_intr_restore(sleepq, flags); - sleepq_release_intr_restore(sleepq, flags); + return error; } diff --git a/kern/semaphore.h b/kern/semaphore.h index 640d6d6c..a18e4701 100644 --- a/kern/semaphore.h +++ b/kern/semaphore.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Richard Braun. + * Copyright (c) 2017-2019 Richard Braun. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,8 +19,8 @@ * They are used to synchronize access to resources and signal events. * * The main operations supported by semaphores are waiting and signalling. - * A semaphore is implemented as an atomic integer with an initial value. - * Waiting on a semaphore means decrementing that integer, whereas signalling + * A semaphore is implemented as a counter with an initial value. Waiting + * on a semaphore means decrementing that counter, whereas signalling * means incrementing it. Waiting can only succeed if the semaphore value * is strictly greater than 0. * @@ -48,10 +48,6 @@ #include <errno.h> #include <stdint.h> -#include <kern/atomic.h> - -#define SEMAPHORE_VALUE_MAX 32768 - #include <kern/semaphore_i.h> struct semaphore; @@ -59,12 +55,8 @@ struct semaphore; /* * Initialize a semaphore. */ -static inline void -semaphore_init(struct semaphore *semaphore, unsigned int value) -{ - assert(value <= SEMAPHORE_VALUE_MAX); - semaphore->value = value; -} +void semaphore_init(struct semaphore *semaphore, uint16_t value, + uint16_t max_value); /* * Attempt to decrement a semaphore. @@ -73,19 +65,7 @@ semaphore_init(struct semaphore *semaphore, unsigned int value) * * Return 0 on success, EAGAIN if the semaphore could not be decremented. */ -static inline int -semaphore_trywait(struct semaphore *semaphore) -{ - unsigned int prev; - - prev = semaphore_dec(semaphore); - - if (prev == 0) { - return EAGAIN; - } - - return 0; -} +int semaphore_trywait(struct semaphore *semaphore); /* * Wait on a semaphore. @@ -93,17 +73,7 @@ semaphore_trywait(struct semaphore *semaphore) * If the semaphore value cannot be decremented, the calling thread sleeps * until the semaphore value is incremented. */ -static inline void -semaphore_wait(struct semaphore *semaphore) -{ - unsigned int prev; - - prev = semaphore_dec(semaphore); - - if (prev == 0) { - semaphore_wait_slow(semaphore); - } -} +void semaphore_wait(struct semaphore *semaphore); /* * Wait on a semaphore, with a time boundary. @@ -112,47 +82,20 @@ semaphore_wait(struct semaphore *semaphore) * * If successful, the semaphore is decremented, otherwise an error is returned. */ -static inline int -semaphore_timedwait(struct semaphore *semaphore, uint64_t ticks) -{ - unsigned int prev; - - prev = semaphore_dec(semaphore); - - if (prev == 0) { - return semaphore_timedwait_slow(semaphore, ticks); - } - - return 0; -} +int semaphore_timedwait(struct semaphore *semaphore, uint64_t ticks); /* * Signal a semaphore. * - * If the semaphore value becomes strictly greater than 0, a thread waiting - * on the semaphore is awaken. + * This function attempts to increment the semaphore value. If successful, and + * if one or more threads are waiting on the semaphore, one of them is awaken. * - * A semaphore may be signalled from interrupt context. - */ -static inline void -semaphore_post(struct semaphore *semaphore) -{ - unsigned int prev; - - prev = semaphore_inc(semaphore); - - if (prev == 0) { - semaphore_post_slow(semaphore); - } -} - -/* - * Get the value of a semaphore. + * A semaphore may safely be signalled from interrupt context. + * + * If successful, the semaphore is incremented. Otherwise, if the semaphore + * value is already at its maximum before calling this function, EOVERFLOW + * is returned. */ -static inline unsigned int -semaphore_getvalue(const struct semaphore *semaphore) -{ - return atomic_load(&semaphore->value, ATOMIC_RELAXED); -} +int semaphore_post(struct semaphore *semaphore); #endif /* KERN_SEMAPHORE_H */ diff --git a/kern/semaphore_i.h b/kern/semaphore_i.h index d58ad0ba..f2397e51 100644 --- a/kern/semaphore_i.h +++ b/kern/semaphore_i.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Richard Braun. + * Copyright (c) 2017-2019 Richard Braun. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,47 +18,11 @@ #ifndef KERN_SEMAPHORE_I_H #define KERN_SEMAPHORE_I_H -#include <assert.h> #include <stdint.h> -#include <kern/atomic.h> - struct semaphore { - unsigned int value; + uint16_t value; + uint16_t max_value; }; -static inline unsigned int -semaphore_dec(struct semaphore *semaphore) -{ - unsigned int prev, value; - - do { - value = atomic_load(&semaphore->value, ATOMIC_RELAXED); - - if (value == 0) { - break; - } - - prev = atomic_cas(&semaphore->value, value, value - 1, ATOMIC_ACQUIRE); - } while (prev != value); - - return value; -} - -static inline unsigned int -semaphore_inc(struct semaphore *semaphore) -{ - unsigned int prev; - - prev = atomic_fetch_add(&semaphore->value, 1, ATOMIC_RELEASE); - assert(prev != SEMAPHORE_VALUE_MAX); - return prev; -} - -void semaphore_wait_slow(struct semaphore *semaphore); - -int semaphore_timedwait_slow(struct semaphore *semaphore, uint64_t ticks); - -void semaphore_post_slow(struct semaphore *semaphore); - #endif /* KERN_SEMAPHORE_I_H */ |