summaryrefslogtreecommitdiffstats
path: root/src/ioq.c
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2023-07-12 13:12:27 -0400
committerTavian Barnes <tavianator@tavianator.com>2023-07-12 13:12:27 -0400
commitd9fb2e5e76e1e2de63af78f4de308c3de577c90b (patch)
tree810cbc53332e2b596838a38591f561ae1e318949 /src/ioq.c
parentdcb6cf524a1d7279457a3e1e7fecff5f46181ab4 (diff)
downloadbfs-d9fb2e5e76e1e2de63af78f4de308c3de577c90b.tar.xz
ioq: Try harder to avoid setting IOQ_BLOCKED
Diffstat (limited to 'src/ioq.c')
-rw-r--r--src/ioq.c22
1 files changed, 16 insertions, 6 deletions
diff --git a/src/ioq.c b/src/ioq.c
index c66ebda..a394e07 100644
--- a/src/ioq.c
+++ b/src/ioq.c
@@ -148,20 +148,30 @@ static struct ioqq *ioqq_create(size_t size) {
/** Atomically wait for a slot to change. */
static uintptr_t ioq_slot_wait(struct ioqq *ioqq, ioq_slot *slot, uintptr_t value) {
- fetch_or(slot, IOQ_BLOCKED, relaxed);
- value |= IOQ_BLOCKED;
-
size_t i = slot - ioqq->slots;
struct ioq_monitor *monitor = &ioqq->monitors[i & ioqq->monitor_mask];
mutex_lock(&monitor->mutex);
- uintptr_t ret;
- while ((ret = load(slot, relaxed)) == value) {
+ uintptr_t ret = load(slot, relaxed);
+ if (ret != value) {
+ goto done;
+ }
+
+ if (!(value & IOQ_BLOCKED)) {
+ value |= IOQ_BLOCKED;
+ if (!compare_exchange_strong(slot, &ret, value, relaxed, relaxed)) {
+ goto done;
+ }
+ }
+
+ do {
// To avoid missed wakeups, it is important that
// cond_broadcast() is not called right here
cond_wait(&monitor->cond, &monitor->mutex);
- }
+ ret = load(slot, relaxed);
+ } while (ret == value);
+done:
mutex_unlock(&monitor->mutex);
return ret;
}