Index: generic/src/proc/task.c
===================================================================
--- generic/src/proc/task.c	(revision 88169d9bf73e6750e103020b2e1780dfc973724b)
+++ generic/src/proc/task.c	(revision 85d24f6104e1d4052c519b698e793a5b17168ab8)
@@ -72,5 +72,5 @@
 
 static void ktaskclnp(void *arg);
-static void ktaskkill(void *arg);
+static void ktaskgc(void *arg);
 
 /** Initialize tasks
@@ -222,5 +222,5 @@
 	 * Create killer thread for the new task.
 	 */
-	t2 = thread_create(ktaskkill, t1, task, 0, "ktaskkill");
+	t2 = thread_create(ktaskgc, t1, task, 0, "ktaskgc");
 	ASSERT(t2);
 	thread_ready(t2);
@@ -373,4 +373,5 @@
 	thread_t *t = NULL, *main_thread;
 	link_t *cur;
+	bool again;
 
 	thread_detach(THREAD);
@@ -385,12 +386,25 @@
 	 * Find a thread to join.
 	 */
+	again = false;
 	for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
 		t = list_get_instance(cur, thread_t, th_link);
-		if (t == THREAD)
+
+		spinlock_lock(&t->lock);
+		if (t == THREAD) {
+			spinlock_unlock(&t->lock);
 			continue;
-		else if (t == main_thread)
+		} else if (t == main_thread) {
+			spinlock_unlock(&t->lock);
 			continue;
-		else
+		} else if (t->join_type != None) {
+			spinlock_unlock(&t->lock);
+			again = true;
+			continue;
+		} else {
+			t->join_type = TaskClnp;
+			spinlock_unlock(&t->lock);
+			again = false;
 			break;
+		}
 	}
 	
@@ -398,6 +412,14 @@
 	interrupts_restore(ipl);
 	
+	if (again) {
+		/*
+		 * Other cleanup (e.g. ktaskgc) is in progress.
+		 */
+		scheduler();
+		goto loop;
+	}
+	
 	if (t != THREAD) {
-		ASSERT(t != main_thread);	/* uninit is joined and detached in ktaskkill */
+		ASSERT(t != main_thread);	/* uninit is joined and detached in ktaskgc */
 		thread_join(t);
 		thread_detach(t);
@@ -418,17 +440,51 @@
  *
  * This thread waits until the main userspace thread (i.e. uninit) exits.
- * When this happens, the task is killed.
+ * When this happens, the task is killed. In the meantime, exited threads
+ * are garbage collected.
  *
  * @param arg Pointer to the thread structure of the task's main thread.
  */
-void ktaskkill(void *arg)
+void ktaskgc(void *arg)
 {
 	thread_t *t = (thread_t *) arg;
-	
+loop:	
 	/*
 	 * Userspace threads cannot detach themselves,
 	 * therefore the thread pointer is guaranteed to be valid.
 	 */
-	thread_join(t);	/* sleep uninterruptibly here! */
+	if (thread_join_timeout(t, 1000000, SYNCH_FLAGS_NONE) == ESYNCH_TIMEOUT) {	/* sleep uninterruptibly here! */
+		ipl_t ipl;
+		link_t *cur;
+		thread_t *thr = NULL;
+	
+		/*
+		 * The join timed out. Try to do some garbage collection of Undead threads.
+		 */
+more_gc:		
+		ipl = interrupts_disable();
+		spinlock_lock(&TASK->lock);
+		
+		for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
+			thr = list_get_instance(cur, thread_t, th_link);
+			spinlock_lock(&thr->lock);
+			if (thr->state == Undead && thr->join_type == None) {
+				thr->join_type = TaskGC;
+				spinlock_unlock(&thr->lock);
+				break;
+			}
+			spinlock_unlock(&thr->lock);
+			thr = NULL;
+		}
+		spinlock_unlock(&TASK->lock);
+		
+		if (thr) {
+			thread_join(thr);
+			thread_detach(thr);
+			scheduler();
+			goto more_gc;
+		}
+			
+		goto loop;
+	}
 	thread_detach(t);
 	task_kill(TASK->taskid);
Index: generic/src/proc/thread.c
===================================================================
--- generic/src/proc/thread.c	(revision 88169d9bf73e6750e103020b2e1780dfc973724b)
+++ generic/src/proc/thread.c	(revision 85d24f6104e1d4052c519b698e793a5b17168ab8)
@@ -337,4 +337,5 @@
 
 	t->interrupted = false;	
+	t->join_type = None;
 	t->detached = false;
 	waitq_initialize(&t->join_wq);
