Index: kernel/generic/include/proc/scheduler.h
===================================================================
--- kernel/generic/include/proc/scheduler.h	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/include/proc/scheduler.h	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -57,4 +57,5 @@
 extern void scheduler_fpu_lazy_request(void);
 extern void scheduler(void);
+extern void scheduler_locked(ipl_t);
 extern void kcpulb(void *arg);
 
Index: kernel/generic/include/proc/thread.h
===================================================================
--- kernel/generic/include/proc/thread.h	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/include/proc/thread.h	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -79,4 +79,7 @@
 	odlink_t lthreads;
 
+	/** Tracking variable for thread_wait/thread_wakeup */
+	atomic_int sleep_state;
+
 	/**
 	 * If true, the thread is terminating.
@@ -85,4 +88,7 @@
 	 */
 	volatile bool interrupted;
+
+	/** Wait queue in which this thread sleeps. Used for debug printouts. */
+	_Atomic(waitq_t *) sleep_queue;
 
 	/** Waitq for thread_join_timeout(). */
@@ -108,29 +114,4 @@
 	context_t saved_context;
 	ipl_t saved_ipl;
-
-	/**
-	 * From here, the stored timeout context
-	 * is restored when sleep times out.
-	 */
-	context_t sleep_timeout_context;
-
-	/**
-	 * From here, the stored interruption context
-	 * is restored when sleep is interrupted.
-	 */
-	context_t sleep_interruption_context;
-
-	/** If true, the thread can be interrupted from sleep. */
-	bool sleep_interruptible;
-
-	/**
-	 * If true, and this thread's sleep returns without a wakeup
-	 * (timed out or interrupted), waitq ignores the next wakeup.
-	 * This is necessary for futex to be able to handle those conditions.
-	 */
-	bool sleep_composable;
-
-	/** Wait queue in which this thread sleeps. */
-	waitq_t *sleep_queue;
 
 	/**
@@ -216,5 +197,19 @@
 extern void thread_ready(thread_t *);
 extern void thread_exit(void) __attribute__((noreturn));
-extern void thread_interrupt(thread_t *, bool);
+extern void thread_interrupt(thread_t *);
+
+typedef enum {
+	THREAD_OK,
+	THREAD_TERMINATING,
+} thread_termination_state_t;
+
+typedef enum {
+	THREAD_WAIT_SUCCESS,
+	THREAD_WAIT_TIMEOUT,
+} thread_wait_result_t;
+
+extern thread_termination_state_t thread_wait_start(void);
+extern thread_wait_result_t thread_wait_finish(deadline_t);
+extern void thread_wakeup(thread_t *);
 
 static inline thread_t *thread_ref(thread_t *thread)
Index: kernel/generic/include/synch/waitq.h
===================================================================
--- kernel/generic/include/synch/waitq.h	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/include/synch/waitq.h	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -41,10 +41,4 @@
 #include <adt/list.h>
 
-typedef enum {
-	WAKEUP_FIRST = 0,
-	WAKEUP_ALL,
-	WAKEUP_CLOSE,
-} wakeup_mode_t;
-
 /** Wait queue structure.
  *
@@ -58,15 +52,18 @@
 
 	/**
-	 * Number of waitq_wakeup() calls that didn't find a thread to wake up.
-	 *
+	 * If negative, number of wakeups that are to be ignored (necessary for futex operation).
+	 * If positive, number of wakeups that weren't able to wake a thread.
 	 */
-	int missed_wakeups;
-
-	/** Number of wakeups that need to be ignored due to futex timeout. */
-	int ignore_wakeups;
+	int wakeup_balance;
 
 	/** List of sleeping threads for which there was no missed_wakeup. */
 	list_t sleepers;
+
+	bool closed;
 } waitq_t;
+
+typedef struct wait_guard {
+	ipl_t ipl;
+} wait_guard_t;
 
 struct thread;
@@ -75,12 +72,14 @@
 extern void waitq_initialize_with_count(waitq_t *, int);
 extern errno_t waitq_sleep(waitq_t *);
-extern errno_t waitq_sleep_timeout(waitq_t *, uint32_t, unsigned int, bool *);
-extern ipl_t waitq_sleep_prepare(waitq_t *);
-extern errno_t waitq_sleep_unsafe(waitq_t *, bool *);
-extern errno_t waitq_sleep_timeout_unsafe(waitq_t *, uint32_t, unsigned int, bool *);
-extern void waitq_sleep_finish(waitq_t *, bool, ipl_t);
-extern void waitq_wakeup(waitq_t *, wakeup_mode_t);
-extern void _waitq_wakeup_unsafe(waitq_t *, wakeup_mode_t);
-extern void waitq_interrupt_sleep(struct thread *);
+extern errno_t _waitq_sleep_timeout(waitq_t *, uint32_t, unsigned int);
+extern errno_t waitq_sleep_timeout(waitq_t *, uint32_t);
+extern wait_guard_t waitq_sleep_prepare(waitq_t *);
+extern errno_t waitq_sleep_unsafe(waitq_t *, wait_guard_t);
+extern errno_t waitq_sleep_timeout_unsafe(waitq_t *, uint32_t, unsigned int, wait_guard_t);
+
+extern void waitq_wake_one(waitq_t *);
+extern void waitq_wake_all(waitq_t *);
+extern void waitq_signal(waitq_t *);
+extern void waitq_close(waitq_t *);
 
 #endif
Index: kernel/generic/include/time/timeout.h
===================================================================
--- kernel/generic/include/time/timeout.h	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/include/time/timeout.h	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -42,9 +42,12 @@
 typedef void (*timeout_handler_t)(void *arg);
 
+typedef uint64_t deadline_t;
+#define DEADLINE_NEVER ((deadline_t) UINT64_MAX)
+
 typedef struct {
 	/** Link to the list of active timeouts on timeout->cpu */
 	link_t link;
 	/** Timeout will be activated when current clock tick reaches this value. */
-	uint64_t deadline;
+	deadline_t deadline;
 	/** Function that will be called on timeout activation. */
 	timeout_handler_t handler;
@@ -59,7 +62,10 @@
 #define us2ticks(us)  ((uint64_t) (((uint32_t) (us) / (1000000 / HZ))))
 
+extern deadline_t timeout_deadline_in_usec(uint32_t us);
+
 extern void timeout_init(void);
 extern void timeout_initialize(timeout_t *);
 extern void timeout_register(timeout_t *, uint64_t, timeout_handler_t, void *);
+extern void timeout_register_deadline(timeout_t *, deadline_t, timeout_handler_t, void *);
 extern bool timeout_unregister(timeout_t *);
 
Index: kernel/generic/src/ipc/event.c
===================================================================
--- kernel/generic/src/ipc/event.c	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/src/ipc/event.c	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -169,6 +169,5 @@
 				    true);
 
-				waitq_wakeup(&event->answerbox->wq,
-				    WAKEUP_FIRST);
+				waitq_wake_one(&event->answerbox->wq);
 
 				if (mask)
Index: kernel/generic/src/ipc/ipc.c
===================================================================
--- kernel/generic/src/ipc/ipc.c	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/src/ipc/ipc.c	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -326,5 +326,5 @@
 		irq_spinlock_unlock(&callerbox->lock, true);
 
-	waitq_wakeup(&callerbox->wq, WAKEUP_FIRST);
+	waitq_wake_one(&callerbox->wq);
 }
 
@@ -416,5 +416,5 @@
 	irq_spinlock_unlock(&box->lock, true);
 
-	waitq_wakeup(&box->wq, WAKEUP_FIRST);
+	waitq_wake_one(&box->wq);
 }
 
@@ -555,5 +555,5 @@
 	errno_t rc;
 
-	rc = waitq_sleep_timeout(&box->wq, usec, flags, NULL);
+	rc = _waitq_sleep_timeout(&box->wq, usec, flags);
 	if (rc != EOK)
 		return rc;
Index: kernel/generic/src/ipc/irq.c
===================================================================
--- kernel/generic/src/ipc/irq.c	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/src/ipc/irq.c	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -429,5 +429,5 @@
 	irq_spinlock_unlock(&irq->notif_cfg.answerbox->irq_lock, false);
 
-	waitq_wakeup(&irq->notif_cfg.answerbox->wq, WAKEUP_FIRST);
+	waitq_wake_one(&irq->notif_cfg.answerbox->wq);
 }
 
Index: kernel/generic/src/ipc/sysipc.c
===================================================================
--- kernel/generic/src/ipc/sysipc.c	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/src/ipc/sysipc.c	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -871,5 +871,5 @@
 sys_errno_t sys_ipc_poke(void)
 {
-	waitq_wakeup(&TASK->answerbox.wq, WAKEUP_FIRST);
+	waitq_wake_one(&TASK->answerbox.wq);
 	return EOK;
 }
Index: kernel/generic/src/proc/scheduler.c
===================================================================
--- kernel/generic/src/proc/scheduler.c	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/src/proc/scheduler.c	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -300,4 +300,18 @@
 }
 
+void scheduler(void)
+{
+	ipl_t ipl = interrupts_disable();
+
+	if (atomic_load(&haltstate))
+		halt();
+
+	if (THREAD) {
+		irq_spinlock_lock(&THREAD->lock, false);
+	}
+
+	scheduler_locked(ipl);
+}
+
 /** The scheduler
  *
@@ -307,18 +321,9 @@
  *
  */
-void scheduler(void)
-{
-	volatile ipl_t ipl;
-
+void scheduler_locked(ipl_t ipl)
+{
 	assert(CPU != NULL);
 
-	ipl = interrupts_disable();
-
-	if (atomic_load(&haltstate))
-		halt();
-
 	if (THREAD) {
-		irq_spinlock_lock(&THREAD->lock, false);
-
 		/* Update thread kernel accounting */
 		THREAD->kcycles += get_cycle() - THREAD->last_cycle;
@@ -419,5 +424,5 @@
 		case Exiting:
 			irq_spinlock_unlock(&THREAD->lock, false);
-			waitq_wakeup(&THREAD->join_wq, WAKEUP_CLOSE);
+			waitq_close(&THREAD->join_wq);
 
 			/*
@@ -434,12 +439,4 @@
 			 */
 			THREAD->priority = -1;
-
-			/*
-			 * We need to release wq->lock which we locked in
-			 * waitq_sleep(). Address of wq->lock is kept in
-			 * THREAD->sleep_queue.
-			 */
-			irq_spinlock_unlock(&THREAD->sleep_queue->lock, false);
-
 			irq_spinlock_unlock(&THREAD->lock, false);
 			break;
Index: kernel/generic/src/proc/task.c
===================================================================
--- kernel/generic/src/proc/task.c	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/src/proc/task.c	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -533,9 +533,5 @@
 
 	list_foreach(task->threads, th_link, thread_t, thread) {
-		thread_t *thr = thread_try_ref(thread);
-		if (thr)
-			thread_interrupt(thr, false);
-
-		// If NULL, the thread is already getting destroyed concurrently with this.
+		thread_interrupt(thread);
 	}
 
Index: kernel/generic/src/proc/thread.c
===================================================================
--- kernel/generic/src/proc/thread.c	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/src/proc/thread.c	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -69,4 +69,5 @@
 #include <errno.h>
 #include <debug.h>
+#include <halt.h>
 
 /** Thread states */
@@ -79,4 +80,10 @@
 	"Exiting",
 	"Lingering"
+};
+
+enum sleep_state {
+	SLEEP_INITIAL,
+	SLEEP_ASLEEP,
+	SLEEP_WOKE,
 };
 
@@ -365,7 +372,5 @@
 	thread->state = Entering;
 
-	thread->sleep_interruptible = false;
-	thread->sleep_composable = false;
-	thread->sleep_queue = NULL;
+	atomic_init(&thread->sleep_queue, NULL);
 
 	thread->in_copy_from_uspace = false;
@@ -373,4 +378,6 @@
 
 	thread->interrupted = false;
+	atomic_init(&thread->sleep_state, SLEEP_INITIAL);
+
 	waitq_initialize(&thread->join_wq);
 
@@ -545,19 +552,144 @@
  * @param thread A valid thread object.
  */
-void thread_interrupt(thread_t *thread, bool irq_dis)
+void thread_interrupt(thread_t *thread)
 {
 	assert(thread != NULL);
-
-	irq_spinlock_lock(&thread->lock, irq_dis);
-
 	thread->interrupted = true;
-	bool sleeping = (thread->state == Sleeping);
-
-	irq_spinlock_unlock(&thread->lock, irq_dis);
-
-	if (sleeping)
-		waitq_interrupt_sleep(thread);
-
-	thread_put(thread);
+	thread_wakeup(thread);
+}
+
+/** Prepare for putting the thread to sleep.
+ *
+ * @returns whether the thread is currently terminating. If THREAD_OK
+ * is returned, the thread is guaranteed to be woken up instantly if the thread
+ * is terminated at any time between this function's return and
+ * thread_wait_finish(). If THREAD_TERMINATING is returned, the thread can still
+ * go to sleep, but doing so will delay termination.
+ */
+thread_termination_state_t thread_wait_start(void)
+{
+	assert(THREAD != NULL);
+
+	/*
+	 * This is an exchange rather than a store so that we can use the acquire
+	 * semantics, which is needed to ensure that code after this operation sees
+	 * memory ops made before thread_wakeup() in other thread, if that wakeup
+	 * was reset by this operation.
+	 *
+	 * In particular, we need this to ensure we can't miss the thread being
+	 * terminated concurrently with a synchronization primitive preparing to
+	 * sleep.
+	 */
+	(void) atomic_exchange_explicit(&THREAD->sleep_state, SLEEP_INITIAL,
+	    memory_order_acquire);
+
+	return THREAD->interrupted ? THREAD_TERMINATING : THREAD_OK;
+}
+
+static void thread_wait_internal(void)
+{
+	assert(THREAD != NULL);
+
+	ipl_t ipl = interrupts_disable();
+
+	if (atomic_load(&haltstate))
+		halt();
+
+	/*
+	 * Lock here to prevent a race between entering the scheduler and another
+	 * thread rescheduling this thread.
+	 */
+	irq_spinlock_lock(&THREAD->lock, false);
+
+	int expected = SLEEP_INITIAL;
+
+	/* Only set SLEEP_ASLEEP in sleep pad if it's still in initial state */
+	if (atomic_compare_exchange_strong_explicit(&THREAD->sleep_state, &expected,
+	    SLEEP_ASLEEP, memory_order_acq_rel, memory_order_acquire)) {
+		THREAD->state = Sleeping;
+		scheduler_locked(ipl);
+	} else {
+		assert(expected == SLEEP_WOKE);
+		/* Return immediately. */
+		irq_spinlock_unlock(&THREAD->lock, false);
+		interrupts_restore(ipl);
+	}
+}
+
+static void thread_wait_timeout_callback(void *arg)
+{
+	thread_wakeup(arg);
+}
+
+/**
+ * Suspends this thread's execution until thread_wakeup() is called on it,
+ * or deadline is reached.
+ *
+ * The way this would normally be used is that the current thread call
+ * thread_wait_start(), and if interruption has not been signaled, stores
+ * a reference to itself in a synchronized structure (such as waitq).
+ * After that, it releases any spinlocks it might hold and calls this function.
+ *
+ * The thread doing the wakeup will acquire the thread's reference from said
+ * synchronized structure and calls thread_wakeup() on it.
+ *
+ * Notably, there can be more than one thread performing wakeup.
+ * The number of performed calls to thread_wakeup(), or their relative
+ * ordering with thread_wait_finish(), does not matter. However, calls to
+ * thread_wakeup() are expected to be synchronized with thread_wait_start()
+ * with which they are associated, otherwise wakeups may be missed.
+ * However, the operation of thread_wakeup() is defined at any time,
+ * synchronization notwithstanding (in the sense of C un/defined behavior),
+ * and is in fact used to interrupt waiting threads by external events.
+ * The waiting thread must operate correctly in face of spurious wakeups,
+ * and clean up its reference in the synchronization structure if necessary.
+ *
+ * Returns THREAD_WAIT_TIMEOUT if timeout fired, which is a necessary condition
+ * for it to have been waken up by the timeout, but the caller must assume
+ * that proper wakeups, timeouts and interrupts may occur concurrently, so
+ * the fact timeout has been registered does not necessarily mean the thread
+ * has not been woken up or interrupted.
+ */
+thread_wait_result_t thread_wait_finish(deadline_t deadline)
+{
+	assert(THREAD != NULL);
+
+	timeout_t timeout;
+
+	if (deadline != DEADLINE_NEVER) {
+		/* Extra check to avoid setting up a deadline if we don't need to. */
+		if (atomic_load_explicit(&THREAD->sleep_state, memory_order_acquire) !=
+		    SLEEP_INITIAL)
+			return THREAD_WAIT_SUCCESS;
+
+		timeout_initialize(&timeout);
+		timeout_register_deadline(&timeout, deadline,
+		    thread_wait_timeout_callback, THREAD);
+	}
+
+	thread_wait_internal();
+
+	if (deadline != DEADLINE_NEVER && !timeout_unregister(&timeout)) {
+		return THREAD_WAIT_TIMEOUT;
+	} else {
+		return THREAD_WAIT_SUCCESS;
+	}
+}
+
+void thread_wakeup(thread_t *thread)
+{
+	assert(thread != NULL);
+
+	int state = atomic_exchange_explicit(&thread->sleep_state, SLEEP_WOKE,
+	    memory_order_release);
+
+	if (state == SLEEP_ASLEEP) {
+		/*
+		 * Only one thread gets to do this.
+		 * The reference consumed here is the reference implicitly passed to
+		 * the waking thread by the sleeper in thread_wait_finish().
+		 */
+		thread_ready(thread);
+	}
 }
 
@@ -628,6 +760,5 @@
 		return EOK;
 	} else {
-		return waitq_sleep_timeout(&thread->join_wq, usec,
-		    SYNCH_FLAGS_NON_BLOCKING, NULL);
+		return _waitq_sleep_timeout(&thread->join_wq, usec, flags);
 	}
 }
@@ -646,5 +777,5 @@
 	waitq_initialize(&wq);
 
-	(void) waitq_sleep_timeout(&wq, usec, SYNCH_FLAGS_NON_BLOCKING, NULL);
+	(void) waitq_sleep_timeout(&wq, usec);
 }
 
@@ -890,5 +1021,5 @@
 
 	if (sleeping)
-		waitq_interrupt_sleep(thread);
+		thread_wakeup(thread);
 
 	thread_put(thread);
Index: kernel/generic/src/synch/condvar.c
===================================================================
--- kernel/generic/src/synch/condvar.c	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/src/synch/condvar.c	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -58,5 +58,5 @@
 void condvar_signal(condvar_t *cv)
 {
-	waitq_wakeup(&cv->wq, WAKEUP_FIRST);
+	waitq_signal(&cv->wq);
 }
 
@@ -68,5 +68,5 @@
 void condvar_broadcast(condvar_t *cv)
 {
-	waitq_wakeup(&cv->wq, WAKEUP_ALL);
+	waitq_wake_all(&cv->wq);
 }
 
@@ -81,20 +81,12 @@
 errno_t condvar_wait_timeout(condvar_t *cv, mutex_t *mtx, uint32_t usec)
 {
-	errno_t rc;
-	ipl_t ipl;
-	bool blocked;
+	wait_guard_t guard = waitq_sleep_prepare(&cv->wq);
 
-	ipl = waitq_sleep_prepare(&cv->wq);
 	/* Unlock only after the waitq is locked so we don't miss a wakeup. */
 	mutex_unlock(mtx);
 
-	cv->wq.missed_wakeups = 0;	/* Enforce blocking. */
-	rc = waitq_sleep_timeout_unsafe(&cv->wq, usec, SYNCH_FLAGS_NON_BLOCKING, &blocked);
-	assert(blocked || rc != EOK);
+	errno_t rc = waitq_sleep_timeout_unsafe(&cv->wq, usec, SYNCH_FLAGS_NON_BLOCKING, guard);
 
-	waitq_sleep_finish(&cv->wq, blocked, ipl);
-	/* Lock only after releasing the waitq to avoid a possible deadlock. */
 	mutex_lock(mtx);
-
 	return rc;
 }
@@ -102,20 +94,12 @@
 errno_t condvar_wait(condvar_t *cv, mutex_t *mtx)
 {
-	errno_t rc;
-	ipl_t ipl;
-	bool blocked;
+	wait_guard_t guard = waitq_sleep_prepare(&cv->wq);
 
-	ipl = waitq_sleep_prepare(&cv->wq);
 	/* Unlock only after the waitq is locked so we don't miss a wakeup. */
 	mutex_unlock(mtx);
 
-	cv->wq.missed_wakeups = 0;	/* Enforce blocking. */
-	rc = waitq_sleep_unsafe(&cv->wq, &blocked);
-	assert(blocked || rc != EOK);
+	errno_t rc = waitq_sleep_unsafe(&cv->wq, guard);
 
-	waitq_sleep_finish(&cv->wq, blocked, ipl);
-	/* Lock only after releasing the waitq to avoid a possible deadlock. */
 	mutex_lock(mtx);
-
 	return rc;
 }
@@ -142,21 +126,12 @@
     uint32_t usec, int flags)
 {
-	errno_t rc;
-	ipl_t ipl;
-	bool blocked;
-
-	ipl = waitq_sleep_prepare(&cv->wq);
+	wait_guard_t guard = waitq_sleep_prepare(&cv->wq);
 
 	/* Unlock only after the waitq is locked so we don't miss a wakeup. */
 	spinlock_unlock(lock);
 
-	cv->wq.missed_wakeups = 0;	/* Enforce blocking. */
-	rc = waitq_sleep_timeout_unsafe(&cv->wq, usec, flags, &blocked);
-	assert(blocked || rc != EOK);
+	errno_t rc = waitq_sleep_timeout_unsafe(&cv->wq, usec, flags, guard);
 
-	waitq_sleep_finish(&cv->wq, blocked, ipl);
-	/* Lock only after releasing the waitq to avoid a possible deadlock. */
 	spinlock_lock(lock);
-
 	return rc;
 }
Index: kernel/generic/src/synch/semaphore.c
===================================================================
--- kernel/generic/src/synch/semaphore.c	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/src/synch/semaphore.c	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -70,5 +70,5 @@
 errno_t semaphore_down_timeout(semaphore_t *sem, uint32_t usec)
 {
-	errno_t rc = waitq_sleep_timeout(&sem->wq, usec, SYNCH_FLAGS_NON_BLOCKING, NULL);
+	errno_t rc = waitq_sleep_timeout(&sem->wq, usec);
 	assert(rc == EOK || rc == ETIMEOUT || rc == EAGAIN);
 	return rc;
@@ -90,5 +90,5 @@
 void semaphore_up(semaphore_t *sem)
 {
-	waitq_wakeup(&sem->wq, WAKEUP_FIRST);
+	waitq_wake_one(&sem->wq);
 }
 
Index: kernel/generic/src/synch/syswaitq.c
===================================================================
--- kernel/generic/src/synch/syswaitq.c	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/src/synch/syswaitq.c	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -159,6 +159,6 @@
 #endif
 
-	errno_t rc = waitq_sleep_timeout(kobj->waitq, timeout,
-	    SYNCH_FLAGS_INTERRUPTIBLE | flags, NULL);
+	errno_t rc = _waitq_sleep_timeout(kobj->waitq, timeout,
+	    SYNCH_FLAGS_INTERRUPTIBLE | flags);
 
 #ifdef CONFIG_UDEBUG
@@ -183,5 +183,5 @@
 		return (sys_errno_t) ENOENT;
 
-	waitq_wakeup(kobj->waitq, WAKEUP_FIRST);
+	waitq_wake_one(kobj->waitq);
 
 	kobject_put(kobj);
Index: kernel/generic/src/synch/waitq.c
===================================================================
--- kernel/generic/src/synch/waitq.c	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/src/synch/waitq.c	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2001-2004 Jakub Jermar
+ * Copyright (c) 2022 Jiří Zárevúcky
  * All rights reserved.
  *
@@ -60,7 +61,4 @@
 #include <mem.h>
 
-static void waitq_sleep_timed_out(void *);
-static void waitq_complete_wakeup(waitq_t *);
-
 /** Initialize wait queue
  *
@@ -77,117 +75,12 @@
 }
 
+/**
+ * Initialize wait queue with an initial number of queued wakeups
+ * (or a wakeup debt if negative).
+ */
 void waitq_initialize_with_count(waitq_t *wq, int count)
 {
-	memsetb(wq, sizeof(*wq), 0);
-	irq_spinlock_initialize(&wq->lock, "wq.lock");
-	list_initialize(&wq->sleepers);
-	wq->missed_wakeups = count;
-}
-
-/** Handle timeout during waitq_sleep_timeout() call
- *
- * This routine is called when waitq_sleep_timeout() times out.
- * Interrupts are disabled.
- *
- * It is supposed to try to remove 'its' thread from the wait queue;
- * it can eventually fail to achieve this goal when these two events
- * overlap. In that case it behaves just as though there was no
- * timeout at all.
- *
- * @param data Pointer to the thread that called waitq_sleep_timeout().
- *
- */
-void waitq_sleep_timed_out(void *data)
-{
-	thread_t *thread = (thread_t *) data;
-	bool do_wakeup = false;
-	DEADLOCK_PROBE_INIT(p_wqlock);
-
-	irq_spinlock_lock(&threads_lock, false);
-
-grab_locks:
-	irq_spinlock_lock(&thread->lock, false);
-
-	waitq_t *wq;
-	if ((wq = thread->sleep_queue)) {  /* Assignment */
-		if (!irq_spinlock_trylock(&wq->lock)) {
-			irq_spinlock_unlock(&thread->lock, false);
-			DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD);
-			/* Avoid deadlock */
-			goto grab_locks;
-		}
-
-		list_remove(&thread->wq_link);
-		thread->saved_context = thread->sleep_timeout_context;
-		do_wakeup = true;
-		if (thread->sleep_composable)
-			wq->ignore_wakeups++;
-		thread->sleep_queue = NULL;
-		irq_spinlock_unlock(&wq->lock, false);
-	}
-
-	irq_spinlock_unlock(&thread->lock, false);
-
-	if (do_wakeup)
-		thread_ready(thread);
-
-	irq_spinlock_unlock(&threads_lock, false);
-}
-
-/** Interrupt sleeping thread.
- *
- * This routine attempts to interrupt a thread from its sleep in
- * a waitqueue. If the thread is not found sleeping, no action
- * is taken.
- *
- * The threads_lock must be already held and interrupts must be
- * disabled upon calling this function.
- *
- * @param thread Thread to be interrupted.
- *
- */
-void waitq_interrupt_sleep(thread_t *thread)
-{
-	bool do_wakeup = false;
-	DEADLOCK_PROBE_INIT(p_wqlock);
-
-	/*
-	 * The thread is quaranteed to exist because
-	 * threads_lock is held.
-	 */
-
-grab_locks:
-	irq_spinlock_lock(&thread->lock, false);
-
-	waitq_t *wq;
-	if ((wq = thread->sleep_queue)) {  /* Assignment */
-		if (!(thread->sleep_interruptible)) {
-			/*
-			 * The sleep cannot be interrupted.
-			 */
-			irq_spinlock_unlock(&thread->lock, false);
-			return;
-		}
-
-		if (!irq_spinlock_trylock(&wq->lock)) {
-			/* Avoid deadlock */
-			irq_spinlock_unlock(&thread->lock, false);
-			DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD);
-			goto grab_locks;
-		}
-
-		list_remove(&thread->wq_link);
-		thread->saved_context = thread->sleep_interruption_context;
-		if (thread->sleep_composable)
-			wq->ignore_wakeups++;
-		do_wakeup = true;
-		thread->sleep_queue = NULL;
-		irq_spinlock_unlock(&wq->lock, false);
-	}
-
-	irq_spinlock_unlock(&thread->lock, false);
-
-	if (do_wakeup)
-		thread_ready(thread);
+	waitq_initialize(wq);
+	wq->wakeup_balance = count;
 }
 
@@ -197,16 +90,18 @@
 errno_t waitq_sleep(waitq_t *wq)
 {
-	return waitq_sleep_timeout(wq, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE, NULL);
+	return _waitq_sleep_timeout(wq, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE);
+}
+
+errno_t waitq_sleep_timeout(waitq_t *wq, uint32_t usec)
+{
+	return _waitq_sleep_timeout(wq, usec, SYNCH_FLAGS_NON_BLOCKING);
 }
 
 /** Sleep until either wakeup, timeout or interruption occurs
  *
- * This is a sleep implementation which allows itself to time out or to be
- * interrupted from the sleep, restoring a failover context.
- *
  * Sleepers are organised in a FIFO fashion in a structure called wait queue.
  *
- * This function is really basic in that other functions as waitq_sleep()
- * and all the *_timeout() functions use it.
+ * Other functions as waitq_sleep() and all the *_timeout() functions are
+ * implemented using this function.
  *
  * @param wq    Pointer to wait queue.
@@ -214,8 +109,4 @@
  * @param flags Specify mode of the sleep.
  *
- * @param[out] blocked  On return, regardless of the return code,
- *                      `*blocked` is set to `true` iff the thread went to
- *                      sleep.
- *
  * The sleep can be interrupted only if the
  * SYNCH_FLAGS_INTERRUPTIBLE bit is specified in flags.
@@ -231,29 +122,15 @@
  * call will immediately return, reporting either success or failure.
  *
- * @return EAGAIN, meaning that the sleep failed because it was requested
- *                 as SYNCH_FLAGS_NON_BLOCKING, but there was no pending wakeup.
- * @return ETIMEOUT, meaning that the sleep timed out.
- * @return EINTR, meaning that somebody interrupted the sleeping
- *         thread. Check the value of `*blocked` to see if the thread slept,
- *         or if a pending interrupt forced it to return immediately.
+ * @return ETIMEOUT, meaning that the sleep timed out, or a nonblocking call
+ *                   returned unsuccessfully.
+ * @return EINTR, meaning that somebody interrupted the sleeping thread.
  * @return EOK, meaning that none of the above conditions occured, and the
- *              thread was woken up successfuly by `waitq_wakeup()`. Check
- *              the value of `*blocked` to see if the thread slept or if
- *              the wakeup was already pending.
- *
- */
-errno_t waitq_sleep_timeout(waitq_t *wq, uint32_t usec, unsigned int flags, bool *blocked)
+ *              thread was woken up successfuly by `waitq_wake_*()`.
+ *
+ */
+errno_t _waitq_sleep_timeout(waitq_t *wq, uint32_t usec, unsigned int flags)
 {
 	assert((!PREEMPTION_DISABLED) || (PARAM_NON_BLOCKING(flags, usec)));
-
-	ipl_t ipl = waitq_sleep_prepare(wq);
-	bool nblocked;
-	errno_t rc = waitq_sleep_timeout_unsafe(wq, usec, flags, &nblocked);
-	waitq_sleep_finish(wq, nblocked, ipl);
-
-	if (blocked != NULL) {
-		*blocked = nblocked;
-	}
-	return rc;
+	return waitq_sleep_timeout_unsafe(wq, usec, flags, waitq_sleep_prepare(wq));
 }
 
@@ -268,48 +145,16 @@
  *
  */
-ipl_t waitq_sleep_prepare(waitq_t *wq)
+wait_guard_t waitq_sleep_prepare(waitq_t *wq)
 {
 	ipl_t ipl = interrupts_disable();
 	irq_spinlock_lock(&wq->lock, false);
-	return ipl;
-}
-
-/** Finish waiting in a wait queue.
- *
- * This function restores interrupts to the state that existed prior
- * to the call to waitq_sleep_prepare(). If necessary, the wait queue
- * lock is released.
- *
- * @param wq       Wait queue.
- * @param blocked  Out parameter of waitq_sleep_timeout_unsafe().
- * @param ipl      Interrupt level returned by waitq_sleep_prepare().
- *
- */
-void waitq_sleep_finish(waitq_t *wq, bool blocked, ipl_t ipl)
-{
-	if (blocked) {
-		/*
-		 * Wait for a waitq_wakeup() or waitq_unsleep() to complete
-		 * before returning from waitq_sleep() to the caller. Otherwise
-		 * the caller might expect that the wait queue is no longer used
-		 * and deallocate it (although the wakeup on a another cpu has
-		 * not yet completed and is using the wait queue).
-		 *
-		 * Note that we have to do this for EOK and EINTR, but not
-		 * necessarily for ETIMEOUT where the timeout handler stops
-		 * using the waitq before waking us up. To be on the safe side,
-		 * ensure the waitq is not in use anymore in this case as well.
-		 */
-		waitq_complete_wakeup(wq);
-	} else {
-		irq_spinlock_unlock(&wq->lock, false);
-	}
-
-	interrupts_restore(ipl);
-}
-
-errno_t waitq_sleep_unsafe(waitq_t *wq, bool *blocked)
-{
-	return waitq_sleep_timeout_unsafe(wq, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE, blocked);
+	return (wait_guard_t) {
+		.ipl = ipl,
+	};
+}
+
+errno_t waitq_sleep_unsafe(waitq_t *wq, wait_guard_t guard)
+{
+	return waitq_sleep_timeout_unsafe(wq, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE, guard);
 }
 
@@ -317,6 +162,5 @@
  *
  * This function implements logic of sleeping in a wait queue.
- * This call must be preceded by a call to waitq_sleep_prepare()
- * and followed by a call to waitq_sleep_finish().
+ * This call must be preceded by a call to waitq_sleep_prepare().
  *
  * @param wq    See waitq_sleep_timeout().
@@ -329,229 +173,175 @@
  *
  */
-errno_t waitq_sleep_timeout_unsafe(waitq_t *wq, uint32_t usec, unsigned int flags, bool *blocked)
-{
-	*blocked = false;
+errno_t waitq_sleep_timeout_unsafe(waitq_t *wq, uint32_t usec, unsigned int flags, wait_guard_t guard)
+{
+	errno_t rc;
+
+	/*
+	 * If true, and this thread's sleep returns without a wakeup
+	 * (timed out or interrupted), waitq ignores the next wakeup.
+	 * This is necessary for futex to be able to handle those conditions.
+	 */
+	bool sleep_composable = (flags & SYNCH_FLAGS_FUTEX);
+	bool interruptible = (flags & SYNCH_FLAGS_INTERRUPTIBLE);
+
+	if (wq->closed) {
+		rc = EOK;
+		goto exit;
+	}
 
 	/* Checks whether to go to sleep at all */
-	if (wq->missed_wakeups) {
-		wq->missed_wakeups--;
-		return EOK;
-	} else {
-		if (PARAM_NON_BLOCKING(flags, usec)) {
-			/* Return immediately instead of going to sleep */
-			return EAGAIN;
+	if (wq->wakeup_balance > 0) {
+		wq->wakeup_balance--;
+
+		rc = EOK;
+		goto exit;
+	}
+
+	if (PARAM_NON_BLOCKING(flags, usec)) {
+		/* Return immediately instead of going to sleep */
+		rc = ETIMEOUT;
+		goto exit;
+	}
+
+	/* Just for debugging output. */
+	atomic_store_explicit(&THREAD->sleep_queue, wq, memory_order_relaxed);
+
+	/*
+	 * This thread_t field is synchronized exclusively via
+	 * waitq lock of the waitq currently listing it.
+	 */
+	list_append(&THREAD->wq_link, &wq->sleepers);
+
+	/* Needs to be run when interrupts are still disabled. */
+	deadline_t deadline = usec > 0 ?
+	    timeout_deadline_in_usec(usec) : DEADLINE_NEVER;
+
+	while (true) {
+		bool terminating = (thread_wait_start() == THREAD_TERMINATING);
+		if (terminating && interruptible) {
+			rc = EINTR;
+			goto exit;
 		}
-	}
-
-	/*
-	 * Now we are firmly decided to go to sleep.
-	 *
-	 */
-	irq_spinlock_lock(&THREAD->lock, false);
-
-	timeout_t timeout;
-	timeout_initialize(&timeout);
-
-	THREAD->sleep_composable = (flags & SYNCH_FLAGS_FUTEX);
-
-	if (flags & SYNCH_FLAGS_INTERRUPTIBLE) {
+
+		irq_spinlock_unlock(&wq->lock, false);
+
+		bool timed_out = (thread_wait_finish(deadline) == THREAD_WAIT_TIMEOUT);
+
 		/*
-		 * If the thread was already interrupted,
-		 * don't go to sleep at all.
+		 * We always need to re-lock the WQ, since concurrently running
+		 * waitq_wakeup() may still not have exitted.
+		 * If we didn't always do this, we'd risk waitq_wakeup() that woke us
+		 * up still running on another CPU even after this function returns,
+		 * and that would be an issue if the waitq is allocated locally to
+		 * wait for a one-off asynchronous event. We'd need more external
+		 * synchronization in that case, and that would be a pain.
+		 *
+		 * On the plus side, always regaining a lock simplifies cleanup.
 		 */
-		if (THREAD->interrupted) {
-			irq_spinlock_unlock(&THREAD->lock, false);
-			return EINTR;
+		irq_spinlock_lock(&wq->lock, false);
+
+		if (!link_in_use(&THREAD->wq_link)) {
+			/*
+			 * We were woken up by the desired event. Return success,
+			 * regardless of any concurrent timeout or interruption.
+			 */
+			rc = EOK;
+			goto exit;
 		}
 
-		/*
-		 * Set context that will be restored if the sleep
-		 * of this thread is ever interrupted.
-		 */
-		THREAD->sleep_interruptible = true;
-		if (!context_save(&THREAD->sleep_interruption_context)) {
-			/* Short emulation of scheduler() return code. */
-			THREAD->last_cycle = get_cycle();
-			irq_spinlock_unlock(&THREAD->lock, false);
-			if (usec) {
-				timeout_unregister(&timeout);
-			}
-			return EINTR;
+		if (timed_out) {
+			rc = ETIMEOUT;
+			goto exit;
 		}
-	} else
-		THREAD->sleep_interruptible = false;
-
-	if (usec) {
-		/* We use the timeout variant. */
-		if (!context_save(&THREAD->sleep_timeout_context)) {
-			/* Short emulation of scheduler() return code. */
-			THREAD->last_cycle = get_cycle();
-			irq_spinlock_unlock(&THREAD->lock, false);
-			return ETIMEOUT;
-		}
-
-		timeout_register(&timeout, (uint64_t) usec, waitq_sleep_timed_out, THREAD);
-	}
-
-	list_append(&THREAD->wq_link, &wq->sleepers);
-
-	/*
-	 * Suspend execution.
-	 *
-	 */
-	THREAD->state = Sleeping;
-	THREAD->sleep_queue = wq;
-
-	/*
-	 * Must be before entry to scheduler, because there are multiple
-	 * return vectors.
-	 */
-	*blocked = true;
-
-	irq_spinlock_unlock(&THREAD->lock, false);
-
-	/* wq->lock is released in scheduler_separated_stack() */
-	scheduler();
-
-	if (usec) {
-		timeout_unregister(&timeout);
-	}
-
-	return EOK;
-}
-
-/** Wake up first thread sleeping in a wait queue
- *
- * Wake up first thread sleeping in a wait queue. This is the SMP- and IRQ-safe
- * wrapper meant for general use.
- *
- * Besides its 'normal' wakeup operation, it attempts to unregister possible
- * timeout.
- *
- * @param wq   Pointer to wait queue.
- * @param mode Wakeup mode.
- *
- */
-void waitq_wakeup(waitq_t *wq, wakeup_mode_t mode)
+
+		/* Interrupted for some other reason. */
+	}
+
+exit:
+	if (THREAD)
+		list_remove(&THREAD->wq_link);
+
+	if (rc != EOK && sleep_composable)
+		wq->wakeup_balance--;
+
+	if (THREAD)
+		atomic_store_explicit(&THREAD->sleep_queue, NULL, memory_order_relaxed);
+
+	irq_spinlock_unlock(&wq->lock, false);
+	interrupts_restore(guard.ipl);
+	return rc;
+}
+
+static void _wake_one(waitq_t *wq)
+{
+	/* Pop one thread from the queue and wake it up. */
+	thread_t *thread = list_get_instance(list_first(&wq->sleepers), thread_t, wq_link);
+	list_remove(&thread->wq_link);
+	thread_wakeup(thread);
+}
+
+/**
+ * Meant for implementing condvar signal.
+ * Always wakes one thread if there are any sleeping,
+ * has no effect if no threads are waiting for wakeup.
+ */
+void waitq_signal(waitq_t *wq)
 {
 	irq_spinlock_lock(&wq->lock, true);
-	_waitq_wakeup_unsafe(wq, mode);
+
+	if (!list_empty(&wq->sleepers))
+		_wake_one(wq);
+
 	irq_spinlock_unlock(&wq->lock, true);
 }
 
-/** If there is a wakeup in progress actively waits for it to complete.
- *
- * The function returns once the concurrently running waitq_wakeup()
- * exits. It returns immediately if there are no concurrent wakeups
- * at the time.
- *
- * Interrupts must be disabled.
- *
- * Example usage:
- * @code
- * void callback(waitq *wq)
- * {
- *     // Do something and notify wait_for_completion() that we're done.
- *     waitq_wakeup(wq);
- * }
- * void wait_for_completion(void)
- * {
- *     waitq wg;
- *     waitq_initialize(&wq);
- *     // Run callback() in the background, pass it wq.
- *     do_asynchronously(callback, &wq);
- *     // Wait for callback() to complete its work.
- *     waitq_sleep(&wq);
- *     // callback() completed its work, but it may still be accessing
- *     // wq in waitq_wakeup(). Therefore it is not yet safe to return
- *     // from waitq_sleep() or it would clobber up our stack (where wq
- *     // is stored). waitq_sleep() ensures the wait queue is no longer
- *     // in use by invoking waitq_complete_wakeup() internally.
- *
- *     // waitq_sleep() returned, it is safe to free wq.
- * }
- * @endcode
- *
- * @param wq  Pointer to a wait queue.
- */
-static void waitq_complete_wakeup(waitq_t *wq)
-{
-	assert(interrupts_disabled());
-
-	irq_spinlock_lock(&wq->lock, false);
-	irq_spinlock_unlock(&wq->lock, false);
-}
-
-/** Internal SMP- and IRQ-unsafe version of waitq_wakeup()
- *
- * This is the internal SMP- and IRQ-unsafe version of waitq_wakeup(). It
- * assumes wq->lock is already locked and interrupts are already disabled.
- *
- * @param wq   Pointer to wait queue.
- * @param mode If mode is WAKEUP_FIRST, then the longest waiting
- *             thread, if any, is woken up. If mode is WAKEUP_ALL, then
- *             all waiting threads, if any, are woken up. If there are
- *             no waiting threads to be woken up, the missed wakeup is
- *             recorded in the wait queue.
- *
- */
-void _waitq_wakeup_unsafe(waitq_t *wq, wakeup_mode_t mode)
-{
-	size_t count = 0;
-
-	assert(interrupts_disabled());
-	assert(irq_spinlock_locked(&wq->lock));
-
-	if (wq->ignore_wakeups > 0) {
-		if (mode == WAKEUP_FIRST) {
-			wq->ignore_wakeups--;
-			return;
-		}
-		wq->ignore_wakeups = 0;
-	}
-
-loop:
-	if (list_empty(&wq->sleepers)) {
-		if (mode == WAKEUP_CLOSE) {
-			// FIXME: this can technically fail if we get two billion sleeps after the wakeup call.
-			wq->missed_wakeups = INT_MAX;
-		} else if (mode != WAKEUP_ALL) {
-			wq->missed_wakeups++;
-		}
-
-		return;
-	}
-
-	count++;
-	thread_t *thread = list_get_instance(list_first(&wq->sleepers),
-	    thread_t, wq_link);
-
-	/*
-	 * Lock the thread prior to removing it from the wq.
-	 * This is not necessary because of mutual exclusion
-	 * (the link belongs to the wait queue), but because
-	 * of synchronization with waitq_sleep_timed_out()
-	 * and thread_interrupt_sleep().
-	 *
-	 * In order for these two functions to work, the following
-	 * invariant must hold:
-	 *
-	 * thread->sleep_queue != NULL <=> thread sleeps in a wait queue
-	 *
-	 * For an observer who locks the thread, the invariant
-	 * holds only when the lock is held prior to removing
-	 * it from the wait queue.
-	 *
-	 */
-	irq_spinlock_lock(&thread->lock, false);
-	list_remove(&thread->wq_link);
-
-	thread->sleep_queue = NULL;
-	irq_spinlock_unlock(&thread->lock, false);
-
-	thread_ready(thread);
-
-	if (mode == WAKEUP_ALL)
-		goto loop;
+/**
+ * Wakes up one thread sleeping on this waitq.
+ * If there are no threads waiting, saves the wakeup so that the next sleep
+ * returns immediately. If a previous failure in sleep created a wakeup debt
+ * (see SYNCH_FLAGS_FUTEX) this debt is annulled and no thread is woken up.
+ */
+void waitq_wake_one(waitq_t *wq)
+{
+	irq_spinlock_lock(&wq->lock, true);
+
+	if (!wq->closed) {
+		if (wq->wakeup_balance < 0 || list_empty(&wq->sleepers))
+			wq->wakeup_balance++;
+		else
+			_wake_one(wq);
+	}
+
+	irq_spinlock_unlock(&wq->lock, true);
+}
+
+static void _wake_all(waitq_t *wq)
+{
+	while (!list_empty(&wq->sleepers))
+		_wake_one(wq);
+}
+
+/**
+ * Wakes up all threads currently waiting on this waitq
+ * and makes all future sleeps return instantly.
+ */
+void waitq_close(waitq_t *wq)
+{
+	irq_spinlock_lock(&wq->lock, true);
+	wq->wakeup_balance = 0;
+	wq->closed = true;
+	_wake_all(wq);
+	irq_spinlock_unlock(&wq->lock, true);
+}
+
+/**
+ * Wakes up all threads currently waiting on this waitq
+ */
+void waitq_wake_all(waitq_t *wq)
+{
+	irq_spinlock_lock(&wq->lock, true);
+	wq->wakeup_balance = 0;
+	_wake_all(wq);
+	irq_spinlock_unlock(&wq->lock, true);
 }
 
Index: kernel/generic/src/time/timeout.c
===================================================================
--- kernel/generic/src/time/timeout.c	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/src/time/timeout.c	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -71,26 +71,21 @@
 }
 
-/** Register timeout
- *
- * Insert timeout handler f (with argument arg)
- * to timeout list and make it execute in
- * time microseconds (or slightly more).
- *
- * @param timeout Timeout structure.
- * @param time    Number of usec in the future to execute the handler.
- * @param handler Timeout handler function.
- * @param arg     Timeout handler argument.
- *
- */
-void timeout_register(timeout_t *timeout, uint64_t time,
+/* Only call when interrupts are disabled. */
+deadline_t timeout_deadline_in_usec(uint32_t usec)
+{
+	if (usec == 0)
+		return 0;
+
+	return CPU->current_clock_tick + us2ticks(usec);
+}
+
+static void timeout_register_deadline_locked(timeout_t *timeout, deadline_t deadline,
     timeout_handler_t handler, void *arg)
 {
-	irq_spinlock_lock(&CPU->timeoutlock, true);
-
 	assert(!link_in_use(&timeout->link));
 
 	*timeout = (timeout_t) {
 		.cpu = CPU,
-		.deadline = CPU->current_clock_tick + us2ticks(time),
+		.deadline = deadline,
 		.handler = handler,
 		.arg = arg,
@@ -113,5 +108,31 @@
 		}
 	}
+}
 
+/** Register timeout
+ *
+ * Insert timeout handler f (with argument arg)
+ * to timeout list and make it execute in
+ * time microseconds (or slightly more).
+ *
+ * @param timeout Timeout structure.
+ * @param time    Number of usec in the future to execute the handler.
+ * @param handler Timeout handler function.
+ * @param arg     Timeout handler argument.
+ *
+ */
+void timeout_register(timeout_t *timeout, uint64_t time,
+    timeout_handler_t handler, void *arg)
+{
+	irq_spinlock_lock(&CPU->timeoutlock, true);
+	timeout_register_deadline_locked(timeout, timeout_deadline_in_usec(time), handler, arg);
+	irq_spinlock_unlock(&CPU->timeoutlock, true);
+}
+
+void timeout_register_deadline(timeout_t *timeout, deadline_t deadline,
+    timeout_handler_t handler, void *arg)
+{
+	irq_spinlock_lock(&CPU->timeoutlock, true);
+	timeout_register_deadline_locked(timeout, deadline, handler, arg);
 	irq_spinlock_unlock(&CPU->timeoutlock, true);
 }
Index: kernel/generic/src/udebug/udebug.c
===================================================================
--- kernel/generic/src/udebug/udebug.c	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/src/udebug/udebug.c	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -438,8 +438,8 @@
 				/*
 				 * thread's lock must not be held when calling
-				 * waitq_wakeup.
+				 * waitq_close.
 				 *
 				 */
-				waitq_wakeup(&thread->udebug.go_wq, WAKEUP_ALL);
+				waitq_close(&thread->udebug.go_wq);
 			}
 
Index: kernel/generic/src/udebug/udebug_ops.c
===================================================================
--- kernel/generic/src/udebug/udebug_ops.c	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/generic/src/udebug/udebug_ops.c	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -276,5 +276,5 @@
 	 *
 	 */
-	waitq_wakeup(&thread->udebug.go_wq, WAKEUP_ALL);
+	waitq_wake_all(&thread->udebug.go_wq);
 
 	_thread_op_end(thread);
Index: kernel/test/synch/semaphore1.c
===================================================================
--- kernel/test/synch/semaphore1.c	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/test/synch/semaphore1.c	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -107,5 +107,5 @@
 
 		thread_sleep(1);
-		waitq_wakeup(&can_start, WAKEUP_ALL);
+		waitq_wake_all(&can_start);
 
 		while ((items_consumed != consumers) || (items_produced != producers)) {
Index: kernel/test/synch/semaphore2.c
===================================================================
--- kernel/test/synch/semaphore2.c	(revision 76e17d7c924f512115312602d253baaf3ea42806)
+++ kernel/test/synch/semaphore2.c	(revision 111b9b97905c174ce8c3ed7e4e2a890da2be2d66)
@@ -99,5 +99,5 @@
 
 	thread_usleep(20000);
-	waitq_wakeup(&can_start, WAKEUP_ALL);
+	waitq_wake_all(&can_start);
 
 	return NULL;
