diff --git a/kernel-src/include/kernel/scheduler.h b/kernel-src/include/kernel/scheduler.h index 284f808f..4961dbf8 100644 --- a/kernel-src/include/kernel/scheduler.h +++ b/kernel-src/include/kernel/scheduler.h @@ -121,7 +121,7 @@ void sched_queue(thread_t *thread); void sched_stopcurrentthread(); int sched_yield(); void sched_preparesleep(bool interruptible); -void sched_wakeup(thread_t *thread, int reason); +bool sched_wakeup(thread_t *thread, int reason); int sched_sleep(); proc_t *sched_newproc(); vnode_t *sched_getcwd(); diff --git a/kernel-src/lib/semaphore.c b/kernel-src/lib/semaphore.c index 520860b9..da8a8298 100644 --- a/kernel-src/lib/semaphore.c +++ b/kernel-src/lib/semaphore.c @@ -17,7 +17,8 @@ static void insert(semaphore_t *sem, thread_t *thread) { static thread_t *get(semaphore_t *sem) { thread_t *thread = sem->tail; - __assert(thread); + if (thread == NULL) + return NULL; sem->tail = thread->sleepprev; @@ -44,6 +45,11 @@ int semaphore_wait(semaphore_t *sem, bool interruptible) { sched_preparesleep(interruptible); spinlock_release(&sem->lock); ret = sched_yield(); + if (ret) { + spinlock_acquire(&sem->lock); + ++sem->i; + spinlock_release(&sem->lock); + } goto leave; } @@ -57,9 +63,17 @@ void semaphore_signal(semaphore_t *sem) { bool intstate = interrupt_set(false); spinlock_acquire(&sem->lock); if (++sem->i <= 0) { - thread_t *thread = get(sem); - // XXX the current method doesn't respect thread priorities - sched_wakeup(thread, 0); + thread_t *thread; + do { + // wake SOMEONE up. this will *try* to wake up a thread + // it could wake up none though, as a thread could have been interrupted by a signal + thread = get(sem); + // XXX the current method doesn't respect thread priorities + if (thread) { + sched_wakeup(thread, 0); + break; + } + } while (thread); } spinlock_release(&sem->lock); interrupt_set(intstate); diff --git a/kernel-src/sys/scheduler.c b/kernel-src/sys/scheduler.c index daf71b36..e7500eb5 100644 --- a/kernel-src/sys/scheduler.c +++ b/kernel-src/sys/scheduler.c @@ -417,14 +417,14 @@ void sched_preparesleep(bool interruptible) { _cpu()->thread->flags |= SCHED_THREAD_FLAGS_SLEEP | (interruptible ? SCHED_THREAD_FLAGS_INTERRUPTIBLE : 0); } -void sched_wakeup(thread_t *thread, int reason) { +bool sched_wakeup(thread_t *thread, int reason) { bool intstate = interrupt_set(false); spinlock_acquire(&thread->sleeplock); if ((thread->flags & SCHED_THREAD_FLAGS_SLEEP) == 0 || ((reason == SCHED_WAKEUP_REASON_INTERRUPTED) && (thread->flags & SCHED_THREAD_FLAGS_INTERRUPTIBLE) == 0)) { spinlock_release(&thread->sleeplock); interrupt_set(intstate); - return; + return false; } thread->flags &= ~(SCHED_THREAD_FLAGS_SLEEP | SCHED_THREAD_FLAGS_INTERRUPTIBLE); @@ -433,6 +433,8 @@ void sched_wakeup(thread_t *thread, int reason) { spinlock_release(&thread->sleeplock); sched_queue(thread); interrupt_set(intstate); + + return true; } static vnode_t *getnodeslock(vnode_t **addr) {