mirror of
https://github.com/Mathewnd/Astral.git
synced 2026-11-01 23:18:21 +00:00
kernel: fix semaphore wakeup race
This commit is contained in:
+16
-11
@@ -74,16 +74,17 @@ int semaphore_wait(semaphore_t *sem, bool interruptible) {
|
|||||||
sched_prepare_sleep(interruptible);
|
sched_prepare_sleep(interruptible);
|
||||||
spinlock_release(&sem->lock);
|
spinlock_release(&sem->lock);
|
||||||
ret = sched_yield();
|
ret = sched_yield();
|
||||||
if (ret) {
|
|
||||||
spinlock_acquire(&sem->lock);
|
|
||||||
|
|
||||||
|
// Wait for the signaller to finish using this semaphore before
|
||||||
|
// returning as it may belong to the caller's stack.
|
||||||
|
spinlock_acquire(&sem->lock);
|
||||||
|
if (ret) {
|
||||||
if (removeself(sem))
|
if (removeself(sem))
|
||||||
++sem->i;
|
++sem->i;
|
||||||
else
|
else
|
||||||
ret = 0; // we have already been removed in the meantime, so pretend we did not actually get interrupted
|
ret = 0; // The semaphore signal won the race.
|
||||||
|
|
||||||
spinlock_release(&sem->lock);
|
|
||||||
}
|
}
|
||||||
|
spinlock_release(&sem->lock);
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,11 +122,13 @@ void semaphore_signal(semaphore_t *sem) {
|
|||||||
__assert(thread);
|
__assert(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
spinlock_release(&sem->lock);
|
// Complete the wake before an interrupted waiter can observe its
|
||||||
interrupt_set(intstate);
|
// removal and return (otherwise this wake could reach its next wait).
|
||||||
|
|
||||||
if (thread)
|
if (thread)
|
||||||
sched_wakeup(thread, 0);
|
sched_wakeup(thread, 0);
|
||||||
|
|
||||||
|
spinlock_release(&sem->lock);
|
||||||
|
interrupt_set(intstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool semaphore_signal_limit(semaphore_t *sem, int limit) {
|
bool semaphore_signal_limit(semaphore_t *sem, int limit) {
|
||||||
@@ -146,11 +149,13 @@ bool semaphore_signal_limit(semaphore_t *sem, int limit) {
|
|||||||
|
|
||||||
successful = true;
|
successful = true;
|
||||||
|
|
||||||
leave:
|
leave:
|
||||||
spinlock_release(&sem->lock);
|
// Complete the wake before an interrupted waiter can observe its
|
||||||
interrupt_set(intstate);
|
// removal and return (otherwise this wake could reach its next wait).
|
||||||
if (thread)
|
if (thread)
|
||||||
sched_wakeup(thread, 0);
|
sched_wakeup(thread, 0);
|
||||||
|
spinlock_release(&sem->lock);
|
||||||
|
interrupt_set(intstate);
|
||||||
return successful;
|
return successful;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user