Index: kernel/generic/src/proc/thread.c
===================================================================
--- kernel/generic/src/proc/thread.c	(revision 83789ea27b9086d9e279dc3811225605763d64a3)
+++ kernel/generic/src/proc/thread.c	(revision 1871118c75a2375392bbb2054288202965beca01)
@@ -94,4 +94,7 @@
  *
  * Members are of type thread_t.
+ *
+ * This structure contains weak references. Any reference from it must not leave
+ * threads_lock critical section unless strengthened via thread_try_ref().
  */
 odict_t threads;
@@ -249,5 +252,5 @@
 /** Make thread ready
  *
- * Switch thread to the ready state.
+ * Switch thread to the ready state. Consumes reference passed by the caller.
  *
  * @param thread Thread to make ready.
@@ -320,4 +323,6 @@
 		return NULL;
 
+	refcount_init(&thread->refcount);
+
 	if (thread_create_arch(thread, flags) != EOK) {
 		slab_free(thread_cache, thread);
@@ -368,5 +373,4 @@
 
 	thread->interrupted = false;
-	thread->detached = false;
 	waitq_initialize(&thread->join_wq);
 
@@ -399,7 +403,9 @@
  *
  */
-void thread_destroy(thread_t *thread, bool irq_res)
-{
-	assert(irq_spinlock_locked(&thread->lock));
+static void thread_destroy(void *obj)
+{
+	thread_t *thread = (thread_t *) obj;
+
+	irq_spinlock_lock(&thread->lock, true);
 	assert((thread->state == Exiting) || (thread->state == Lingering));
 	assert(thread->task);
@@ -421,5 +427,5 @@
 	 */
 	list_remove(&thread->th_link);
-	irq_spinlock_unlock(&thread->task->lock, irq_res);
+	irq_spinlock_unlock(&thread->task->lock, true);
 
 	/*
@@ -430,4 +436,11 @@
 }
 
+void thread_put(thread_t *thread)
+{
+	if (refcount_down(&thread->refcount)) {
+		thread_destroy(thread);
+	}
+}
+
 /** Make the thread visible to the system.
  *
@@ -441,8 +454,10 @@
 void thread_attach(thread_t *thread, task_t *task)
 {
+	ipl_t ipl = interrupts_disable();
+
 	/*
 	 * Attach to the specified task.
 	 */
-	irq_spinlock_lock(&task->lock, true);
+	irq_spinlock_lock(&task->lock, false);
 
 	/* Hold a reference to the task. */
@@ -455,11 +470,14 @@
 	list_append(&thread->th_link, &task->threads);
 
-	irq_spinlock_pass(&task->lock, &threads_lock);
+	irq_spinlock_unlock(&task->lock, false);
 
 	/*
 	 * Register this thread in the system-wide dictionary.
 	 */
+	irq_spinlock_lock(&threads_lock, false);
 	odict_insert(&thread->lthreads, &threads, NULL);
-	irq_spinlock_unlock(&threads_lock, true);
+	irq_spinlock_unlock(&threads_lock, false);
+
+	interrupts_restore(ipl);
 }
 
@@ -513,11 +531,7 @@
  * blocking call was interruptable. See waitq_sleep_timeout().
  *
- * The caller must guarantee the thread object is valid during the entire
- * function, eg by holding the threads_lock lock.
- *
  * Interrupted threads automatically exit when returning back to user space.
  *
- * @param thread A valid thread object. The caller must guarantee it
- *               will remain valid until thread_interrupt() exits.
+ * @param thread A valid thread object.
  */
 void thread_interrupt(thread_t *thread, bool irq_dis)
@@ -534,4 +548,6 @@
 	if (sleeping)
 		waitq_interrupt_sleep(thread);
+
+	thread_put(thread);
 }
 
@@ -581,4 +597,5 @@
 
 /** Wait for another thread to exit.
+ * This function does not destroy the thread. Reference counting handles that.
  *
  * @param thread Thread to join on exit.
@@ -594,49 +611,14 @@
 		return EINVAL;
 
-	/*
-	 * Since thread join can only be called once on an undetached thread,
-	 * the thread pointer is guaranteed to be still valid.
-	 */
-
 	irq_spinlock_lock(&thread->lock, true);
-	assert(!thread->detached);
+	state_t state = thread->state;
 	irq_spinlock_unlock(&thread->lock, true);
 
-	return waitq_sleep_timeout(&thread->join_wq, usec, flags, NULL);
-
-	// FIXME: join should deallocate the thread.
-	//        Current code calls detach after join, that's contrary to how
-	//        join is used in other threading APIs.
-}
-
-/** Detach thread.
- *
- * Mark the thread as detached. If the thread is already
- * in the Lingering state, deallocate its resources.
- *
- * @param thread Thread to be detached.
- *
- */
-void thread_detach(thread_t *thread)
-{
-	/*
-	 * Since the thread is expected not to be already detached,
-	 * pointer to it must be still valid.
-	 */
-	irq_spinlock_lock(&thread->lock, true);
-	assert(!thread->detached);
-
-	if (thread->state == Lingering) {
-		/*
-		 * Unlock &thread->lock and restore
-		 * interrupts in thread_destroy().
-		 */
-		thread_destroy(thread, true);
-		return;
+	if (state == Exiting) {
+		return EOK;
 	} else {
-		thread->detached = true;
-	}
-
-	irq_spinlock_unlock(&thread->lock, true);
+		return waitq_sleep_timeout(&thread->join_wq, usec,
+		    SYNCH_FLAGS_NON_BLOCKING, NULL);
+	}
 }
 
@@ -702,5 +684,5 @@
 	thread_t *thread;
 
-	/* Messing with thread structures, avoid deadlock */
+	/* Accessing system-wide threads list through thread_first()/thread_next(). */
 	irq_spinlock_lock(&threads_lock, true);
 
@@ -730,21 +712,26 @@
 }
 
-/** Check whether thread exists.
- *
- * Note that threads_lock must be already held and
- * interrupts must be already disabled.
- *
- * @param thread Pointer to thread.
- *
- * @return True if thread t is known to the system, false otherwise.
- *
- */
-bool thread_exists(thread_t *thread)
-{
-	assert(interrupts_disabled());
-	assert(irq_spinlock_locked(&threads_lock));
-
+static bool thread_exists(thread_t *thread)
+{
 	odlink_t *odlink = odict_find_eq(&threads, thread, NULL);
 	return odlink != NULL;
+}
+
+/** Check whether the thread exists, and if so, return a reference to it.
+ */
+thread_t *thread_try_get(thread_t *thread)
+{
+	irq_spinlock_lock(&threads_lock, true);
+
+	if (thread_exists(thread)) {
+		/* Try to strengthen the reference. */
+		thread = thread_try_ref(thread);
+	} else {
+		thread = NULL;
+	}
+
+	irq_spinlock_unlock(&threads_lock, true);
+
+	return thread;
 }
 
@@ -777,4 +764,8 @@
  * interrupts must be disabled.
  *
+ * The returned reference is weak.
+ * If the caller needs to keep it, thread_try_ref() must be used to upgrade
+ * to a strong reference _before_ threads_lock is released.
+ *
  * @param id Thread ID.
  *
@@ -854,13 +845,11 @@
 {
 	irq_spinlock_lock(&threads_lock, true);
-
-	thread_t *thread = thread_find_by_id(thread_id);
+	thread_t *thread = thread_try_ref(thread_find_by_id(thread_id));
+	irq_spinlock_unlock(&threads_lock, true);
+
 	if (thread == NULL) {
 		printf("No such thread.\n");
-		irq_spinlock_unlock(&threads_lock, true);
 		return;
 	}
-
-	irq_spinlock_lock(&thread->lock, false);
 
 	/*
@@ -876,4 +865,6 @@
 	 */
 
+	irq_spinlock_lock(&thread->lock, true);
+
 	bool sleeping = false;
 	istate_t *istate = thread->udebug.uspace_state;
@@ -886,10 +877,10 @@
 		printf("Thread interrupt state not available.\n");
 
-	irq_spinlock_unlock(&thread->lock, false);
+	irq_spinlock_unlock(&thread->lock, true);
 
 	if (sleeping)
 		waitq_interrupt_sleep(thread);
 
-	irq_spinlock_unlock(&threads_lock, true);
+	thread_put(thread);
 }
 
