summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kern/semaphore.c107
-rw-r--r--kern/semaphore.h89
-rw-r--r--kern/semaphore_i.h42
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 */