Index: kernel/generic/src/cap/cap.c
===================================================================
--- kernel/generic/src/cap/cap.c	(revision e5f5ce03f9fc114c279f31bd164daf4e6dd8425e)
+++ kernel/generic/src/cap/cap.c	(revision 9e875620ecd41735d8a5759a1e6986978cb55507)
@@ -35,7 +35,8 @@
 #include <cap/cap.h>
 #include <proc/task.h>
-#include <synch/spinlock.h>
+#include <synch/mutex.h>
 #include <abi/errno.h>
 #include <mm/slab.h>
+#include <adt/list.h>
 
 void cap_initialize(cap_t *cap, int handle)
@@ -44,34 +45,66 @@
 	cap->handle = handle;
 	cap->can_reclaim = NULL;
+	link_initialize(&cap->link);
 }
 
 void caps_task_alloc(task_t *task)
 {
-	task->caps = malloc(sizeof(cap_t) * MAX_CAPS, 0);
+	task->cap_info = (cap_info_t *) malloc(sizeof(cap_info_t), 0);
+	task->cap_info->caps = malloc(sizeof(cap_t) * MAX_CAPS, 0);
 }
 
 void caps_task_init(task_t *task)
 {
+	mutex_initialize(&task->cap_info->lock, MUTEX_PASSIVE);
+
+	for (int i = 0; i < CAP_TYPE_MAX; i++)
+		list_initialize(&task->cap_info->type_list[i]);
+
 	for (int i = 0; i < MAX_CAPS; i++)
-		cap_initialize(&task->caps[i], i);
+		cap_initialize(&task->cap_info->caps[i], i);
 }
 
 void caps_task_free(task_t *task)
 {
-	free(task->caps);
+	free(task->cap_info->caps);
+	free(task->cap_info);
+}
+
+bool caps_apply_to_all(task_t *task, cap_type_t type,
+    bool (*cb)(cap_t *, void *), void *arg)
+{
+	bool done = true;
+
+	mutex_lock(&task->cap_info->lock);
+	list_foreach_safe(task->cap_info->type_list[type], cur, next) {
+		cap_t *cap = list_get_instance(cur, cap_t, link);
+		done = cb(cap, arg);
+		if (!done)
+			break;
+	}
+	mutex_unlock(&task->cap_info->lock);
+
+	return done;
+}
+
+void caps_lock(task_t *task)
+{
+	mutex_lock(&task->cap_info->lock);
+}
+
+void caps_unlock(task_t *task)
+{
+	mutex_unlock(&task->cap_info->lock);
 }
 
 cap_t *cap_get(task_t *task, int handle, cap_type_t type)
 {
+	assert(mutex_locked(&task->cap_info->lock));
+
 	if ((handle < 0) || (handle >= MAX_CAPS))
 		return NULL;
-	if (task->caps[handle].type != type)
+	if (task->cap_info->caps[handle].type != type)
 		return NULL;
-	return &task->caps[handle];
-}
-
-cap_t *cap_get_current(int handle, cap_type_t type)
-{
-	return cap_get(TASK, handle, type);
+	return &task->cap_info->caps[handle];
 }
 
@@ -80,7 +113,7 @@
 	int handle;
 
-	irq_spinlock_lock(&task->lock, true);
+	mutex_lock(&task->cap_info->lock);
 	for (handle = 0; handle < MAX_CAPS; handle++) {
-		cap_t *cap = &task->caps[handle];
+		cap_t *cap = &task->cap_info->caps[handle];
 		if (cap->type > CAP_TYPE_ALLOCATED) {
 			if (cap->can_reclaim && cap->can_reclaim(cap))
@@ -89,11 +122,37 @@
 		if (cap->type == CAP_TYPE_INVALID) {
 			cap->type = CAP_TYPE_ALLOCATED;
-			irq_spinlock_unlock(&task->lock, true);
+			mutex_unlock(&task->cap_info->lock);
 			return handle;
 		}
 	}
-	irq_spinlock_unlock(&task->lock, true);
+	mutex_unlock(&task->cap_info->lock);
 
 	return ELIMIT;
+}
+
+void cap_publish(task_t *task, int handle, cap_type_t type, void *kobject)
+{
+	mutex_lock(&task->cap_info->lock);
+	cap_t *cap = cap_get(task, handle, CAP_TYPE_ALLOCATED);
+	assert(cap);
+	cap->type = type;
+	cap->kobject = kobject;
+	list_append(&cap->link, &task->cap_info->type_list[type]);
+	mutex_unlock(&task->cap_info->lock);
+}
+
+cap_t *cap_unpublish(task_t *task, int handle, cap_type_t type)
+{
+	cap_t *cap;
+
+	mutex_lock(&task->cap_info->lock);
+	cap = cap_get(task, handle, type);
+	if (cap) {
+		list_remove(&cap->link);
+		cap->type = CAP_TYPE_ALLOCATED;
+	}
+	mutex_unlock(&task->cap_info->lock);
+
+	return cap;
 }
 
@@ -102,9 +161,9 @@
 	assert(handle >= 0);
 	assert(handle < MAX_CAPS);
-	assert(task->caps[handle].type == CAP_TYPE_ALLOCATED);
+	assert(task->cap_info->caps[handle].type == CAP_TYPE_ALLOCATED);
 
-	irq_spinlock_lock(&task->lock, true);
-	cap_initialize(&task->caps[handle], handle);
-	irq_spinlock_unlock(&task->lock, true);
+	mutex_lock(&task->cap_info->lock);
+	cap_initialize(&task->cap_info->caps[handle], handle);
+	mutex_unlock(&task->cap_info->lock);
 }
 
Index: kernel/generic/src/ipc/ipc.c
===================================================================
--- kernel/generic/src/ipc/ipc.c	(revision e5f5ce03f9fc114c279f31bd164daf4e6dd8425e)
+++ kernel/generic/src/ipc/ipc.c	(revision 9e875620ecd41735d8a5759a1e6986978cb55507)
@@ -710,5 +710,5 @@
 		 * Nota bene: there may still be answers waiting for pick up.
 		 */
-		spinlock_unlock(&TASK->active_calls_lock);	
+		spinlock_unlock(&TASK->active_calls_lock);
 		return;	
 	}
@@ -723,5 +723,5 @@
 		 * call on the list.
 		 */
-		spinlock_unlock(&TASK->active_calls_lock);	
+		spinlock_unlock(&TASK->active_calls_lock);
 		goto restart;
 	}
@@ -730,4 +730,50 @@
 
 	goto restart;
+}
+
+static bool phone_cap_wait_cb(cap_t *cap, void *arg)
+{
+	phone_t *phone = (phone_t *) cap->kobject;
+	bool *restart = (bool *) arg;
+
+	mutex_lock(&phone->lock);
+	if ((phone->state == IPC_PHONE_HUNGUP) &&
+	    (atomic_get(&phone->active_calls) == 0)) {
+		phone->state = IPC_PHONE_FREE;
+		phone->callee = NULL;
+	}
+
+	/*
+	 * We might have had some IPC_PHONE_CONNECTING phones at the beginning
+	 * of ipc_cleanup(). Depending on whether these were forgotten or
+	 * answered, they will eventually enter the IPC_PHONE_FREE or
+	 * IPC_PHONE_CONNECTED states, respectively.  In the latter case, the
+	 * other side may slam the open phones at any time, in which case we
+	 * will get an IPC_PHONE_SLAMMED phone.
+	 */
+	if ((phone->state == IPC_PHONE_CONNECTED) ||
+	    (phone->state == IPC_PHONE_SLAMMED)) {
+		mutex_unlock(&phone->lock);
+		ipc_phone_hangup(phone);
+		/*
+		 * Now there may be one extra active call, which needs to be
+		 * forgotten.
+		 */
+		ipc_forget_all_active_calls();
+		*restart = true;
+		return false;
+	}
+
+	/*
+	 * If the hangup succeeded, it has sent a HANGUP message, the IPC is now
+	 * in HUNGUP state, we wait for the reply to come
+	 */
+	if (phone->state != IPC_PHONE_FREE) {
+		mutex_unlock(&phone->lock);
+		return false;
+	}
+
+	mutex_unlock(&phone->lock);
+	return true;
 }
 
@@ -736,5 +782,5 @@
 {
 	call_t *call;
-	bool all_clean;
+	bool restart;
 
 restart:
@@ -743,53 +789,13 @@
 	 * Locking is needed as there may be connection handshakes in progress.
 	 */
-	all_clean = true;
-	for_each_cap_current(cap, CAP_TYPE_PHONE) {
-		phone_t *phone = (phone_t *) cap->kobject;
-
-		mutex_lock(&phone->lock);
-		if ((phone->state == IPC_PHONE_HUNGUP) &&
-		    (atomic_get(&phone->active_calls) == 0)) {
-			phone->state = IPC_PHONE_FREE;
-			phone->callee = NULL;
-		}
-
-		/*
-		 * We might have had some IPC_PHONE_CONNECTING phones at the
-		 * beginning of ipc_cleanup(). Depending on whether these were
-		 * forgotten or answered, they will eventually enter the
-		 * IPC_PHONE_FREE or IPC_PHONE_CONNECTED states, respectively.
-		 * In the latter case, the other side may slam the open phones
-		 * at any time, in which case we will get an IPC_PHONE_SLAMMED
-		 * phone.
-		 */
-		if ((phone->state == IPC_PHONE_CONNECTED) ||
-		    (phone->state == IPC_PHONE_SLAMMED)) {
-			mutex_unlock(&phone->lock);
-			ipc_phone_hangup(phone);
-			/*
-			 * Now there may be one extra active call, which needs
-			 * to be forgotten.
-			 */
-			ipc_forget_all_active_calls();
-			goto restart;
-		}
-
-		/*
-		 * If the hangup succeeded, it has sent a HANGUP message, the
-		 * IPC is now in HUNGUP state, we wait for the reply to come
-		 */
-		if (phone->state != IPC_PHONE_FREE) {
-			mutex_unlock(&phone->lock);
-			all_clean = false;
-			break;
-		}
-
-		mutex_unlock(&phone->lock);
-	}
-		
-	/* Got into cleanup */
-	if (all_clean)
+	restart = false;
+	if (caps_apply_to_all(TASK, CAP_TYPE_PHONE, phone_cap_wait_cb,
+	    &restart)) {
+		/* Got into cleanup */
 		return;
-		
+	}
+	if (restart)
+		goto restart;
+	
 	call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
 	    SYNCH_FLAGS_NONE);
@@ -800,4 +806,17 @@
 	ipc_call_free(call);
 	goto restart;
+}
+
+static bool phone_cap_cleanup_cb(cap_t *cap, void *arg)
+{
+	phone_t *phone = (phone_t *) cap->kobject;
+	ipc_phone_hangup(phone);
+	return true;
+}
+
+static bool irq_cap_cleanup_cb(cap_t *cap, void *arg)
+{
+	ipc_irq_unsubscribe(&TASK->answerbox, cap->handle);
+	return true;
 }
 
@@ -821,8 +840,5 @@
 
 	/* Disconnect all our phones ('ipc_phone_hangup') */
-	for_each_cap_current(cap, CAP_TYPE_PHONE) {
-		phone_t *phone = (phone_t *) cap->kobject;
-		ipc_phone_hangup(phone);
-	}
+	caps_apply_to_all(TASK, CAP_TYPE_PHONE, phone_cap_cleanup_cb, NULL);
 	
 	/* Unsubscribe from any event notifications. */
@@ -830,7 +846,5 @@
 	
 	/* Disconnect all connected IRQs */
-	for_each_cap_current(cap, CAP_TYPE_IRQ) {
-		ipc_irq_unsubscribe(&TASK->answerbox, cap->handle);
-	}
+	caps_apply_to_all(TASK, CAP_TYPE_IRQ, irq_cap_cleanup_cb, NULL);
 	
 	/* Disconnect all phones connected to our regular answerbox */
@@ -896,4 +910,39 @@
 }
 
+static bool print_task_phone_cb(cap_t *cap, void *arg)
+{
+	phone_t *phone = (phone_t *) cap->kobject;
+
+	mutex_lock(&phone->lock);
+	if (phone->state != IPC_PHONE_FREE) {
+		printf("%-11d %7" PRIun " ", cap->handle,
+		    atomic_get(&phone->active_calls));
+		
+		switch (phone->state) {
+		case IPC_PHONE_CONNECTING:
+			printf("connecting");
+			break;
+		case IPC_PHONE_CONNECTED:
+			printf("connected to %" PRIu64 " (%s)",
+			    phone->callee->task->taskid,
+			    phone->callee->task->name);
+			break;
+		case IPC_PHONE_SLAMMED:
+			printf("slammed by %p", phone->callee);
+			break;
+		case IPC_PHONE_HUNGUP:
+			printf("hung up by %p", phone->callee);
+			break;
+		default:
+			break;
+		}
+		
+		printf("\n");
+	}
+	mutex_unlock(&phone->lock);
+
+	return true;
+}
+
 /** List answerbox contents.
  *
@@ -905,52 +954,16 @@
 	irq_spinlock_lock(&tasks_lock, true);
 	task_t *task = task_find_by_id(taskid);
-	
 	if (!task) {
 		irq_spinlock_unlock(&tasks_lock, true);
 		return;
 	}
-	
-	/* Hand-over-hand locking */
-	irq_spinlock_exchange(&tasks_lock, &task->lock);
+	task_hold(task);
+	irq_spinlock_unlock(&tasks_lock, true);
 	
 	printf("[phone cap] [calls] [state\n");
 	
-	for_each_cap(task, cap, CAP_TYPE_PHONE) {
-		phone_t *phone = (phone_t *) cap->kobject;
-	
-		if (SYNCH_FAILED(mutex_trylock(&phone->lock))) {
-			printf("%-11d (mutex busy)\n", cap->handle);
-			continue;
-		}
-		
-		if (phone->state != IPC_PHONE_FREE) {
-			printf("%-11d %7" PRIun " ", cap->handle,
-			    atomic_get(&phone->active_calls));
-			
-			switch (phone->state) {
-			case IPC_PHONE_CONNECTING:
-				printf("connecting");
-				break;
-			case IPC_PHONE_CONNECTED:
-				printf("connected to %" PRIu64 " (%s)",
-				    phone->callee->task->taskid,
-				    phone->callee->task->name);
-				break;
-			case IPC_PHONE_SLAMMED:
-				printf("slammed by %p", phone->callee);
-				break;
-			case IPC_PHONE_HUNGUP:
-				printf("hung up by %p", phone->callee);
-				break;
-			default:
-				break;
-			}
-			
-			printf("\n");
-		}
-		
-		mutex_unlock(&phone->lock);
-	}
-	
+	caps_apply_to_all(task, CAP_TYPE_PHONE, print_task_phone_cb, NULL);
+	
+	irq_spinlock_lock(&task->lock, true);
 	irq_spinlock_lock(&task->answerbox.lock, false);
 	
@@ -974,4 +987,6 @@
 	irq_spinlock_unlock(&task->answerbox.lock, false);
 	irq_spinlock_unlock(&task->lock, true);
+
+	task_release(task);
 }
 
Index: kernel/generic/src/ipc/ipcrsc.c
===================================================================
--- kernel/generic/src/ipc/ipcrsc.c	(revision e5f5ce03f9fc114c279f31bd164daf4e6dd8425e)
+++ kernel/generic/src/ipc/ipcrsc.c	(revision 9e875620ecd41735d8a5759a1e6986978cb55507)
@@ -175,9 +175,14 @@
 phone_t *phone_get(task_t *task, int handle)
 {
+	phone_t *phone;
+
+	caps_lock(task);
 	cap_t *cap = cap_get(task, handle, CAP_TYPE_PHONE);
+	phone = (phone_t *) cap->kobject;
+	caps_unlock(task);
 	if (!cap)
 		return NULL;
 	
-	return (phone_t *) cap->kobject;
+	return phone;
 }
 
@@ -217,10 +222,11 @@
 		phone->state = IPC_PHONE_CONNECTING;
 		
-		irq_spinlock_lock(&task->lock, true);
+		// FIXME: phase this out eventually
+		mutex_lock(&task->cap_info->lock);
 		cap_t *cap = cap_get(task, handle, CAP_TYPE_ALLOCATED);
-		cap->type = CAP_TYPE_PHONE;
-		cap->kobject = (void *) phone;
 		cap->can_reclaim = phone_can_reclaim;
-		irq_spinlock_unlock(&task->lock, true);
+		mutex_unlock(&task->cap_info->lock);
+
+		cap_publish(task, handle, CAP_TYPE_PHONE, phone);
 	}
 	
@@ -237,9 +243,6 @@
 void phone_dealloc(int handle)
 {
-	irq_spinlock_lock(&TASK->lock, true);
-	cap_t *cap = cap_get_current(handle, CAP_TYPE_PHONE);
+	cap_t *cap = cap_unpublish(TASK, handle, CAP_TYPE_PHONE);
 	assert(cap);
-	cap->type = CAP_TYPE_ALLOCATED;
-	irq_spinlock_unlock(&TASK->lock, true);
 	
 	phone_t *phone = (phone_t *) cap->kobject;
Index: kernel/generic/src/ipc/irq.c
===================================================================
--- kernel/generic/src/ipc/irq.c	(revision e5f5ce03f9fc114c279f31bd164daf4e6dd8425e)
+++ kernel/generic/src/ipc/irq.c	(revision 9e875620ecd41735d8a5759a1e6986978cb55507)
@@ -314,7 +314,4 @@
 	}
 	
-	cap_t *cap = cap_get_current(handle, CAP_TYPE_ALLOCATED);
-	assert(cap);
-	
 	irq_initialize(irq);
 	irq->inr = inr;
@@ -327,20 +324,16 @@
 	irq->notif_cfg.counter = 0;
 	
-	cap->kobject = (void *) irq;
-	
 	/*
-	 * Insert the IRQ structure into the uspace IRQ hash table and retype
-	 * the capability. By retyping the capability inside the critical
-	 * section, we make sure another thread cannot attempt to unregister the
-	 * IRQ before it is inserted into the hash table.
+	 * Insert the IRQ structure into the uspace IRQ hash table.
 	 */
 	irq_spinlock_lock(&irq_uspace_hash_table_lock, true);
 	irq_spinlock_lock(&irq->lock, false);
 	
-	cap->type = CAP_TYPE_IRQ;
 	hash_table_insert(&irq_uspace_hash_table, key, &irq->link);
 	
 	irq_spinlock_unlock(&irq->lock, false);
 	irq_spinlock_unlock(&irq_uspace_hash_table_lock, true);
+
+	cap_publish(TASK, handle, CAP_TYPE_IRQ, irq);
 	
 	return handle;
@@ -357,13 +350,7 @@
 int ipc_irq_unsubscribe(answerbox_t *box, int handle)
 {
-	irq_spinlock_lock(&TASK->lock, true);
-	cap_t *cap = cap_get_current(handle, CAP_TYPE_IRQ);
-	if (!cap) {
-		irq_spinlock_unlock(&TASK->lock, true);
+	cap_t *cap = cap_unpublish(TASK, handle, CAP_TYPE_IRQ);
+	if (!cap)
 		return ENOENT;
-	}
-	/* Make sure only one thread can win the race to unsubscribe. */
-	cap->type = CAP_TYPE_ALLOCATED;
-	irq_spinlock_unlock(&TASK->lock, true);
 	
 	irq_t *irq = (irq_t *) cap->kobject;
Index: kernel/generic/src/proc/task.c
===================================================================
--- kernel/generic/src/proc/task.c	(revision e5f5ce03f9fc114c279f31bd164daf4e6dd8425e)
+++ kernel/generic/src/proc/task.c	(revision 9e875620ecd41735d8a5759a1e6986978cb55507)
@@ -616,5 +616,5 @@
 	if (*additional)
 		printf("%-8" PRIu64 " %9" PRIu64 "%c %9" PRIu64 "%c "
-		    "%9" PRIua, task->taskid, ucycles, usuffix, kcycles,
+		    "%9" PRIua "\n", task->taskid, ucycles, usuffix, kcycles,
 		    ksuffix, atomic_get(&task->refcount));
 	else
@@ -622,13 +622,4 @@
 		    task->taskid, task->name, task->container, task, task->as);
 #endif
-	
-	if (*additional) {
-		for_each_cap(task, cap, CAP_TYPE_PHONE) {
-			phone_t *phone = (phone_t *) cap->kobject;
-			if (phone->callee)
-				printf(" %d:%p", cap->handle, phone->callee);
-		}
-		printf("\n");
-	}
 	
 	irq_spinlock_unlock(&task->lock, false);
