Index: abi/include/abi/cap.h
===================================================================
--- abi/include/abi/cap.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
+++ abi/include/abi/cap.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2017 Jakub Jermar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup generic
+ * @{
+ */
+/** @file
+ */
+
+#ifndef ABI_CAP_H_
+#define ABI_CAP_H_
+
+#define CAP_NIL		0
+
+#endif
+
+/** @}
+ */
Index: abi/include/abi/ipc/methods.h
===================================================================
--- abi/include/abi/ipc/methods.h	(revision 0851a3ddbbf4ba5ef46cc8620a6dea43b2b1756a)
+++ abi/include/abi/ipc/methods.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -36,6 +36,8 @@
 #define ABI_IPC_METHODS_H_
 
+#include <abi/cap.h>
+
 /* Well known phone descriptors */
-#define PHONE_NS  0
+#define PHONE_NS  (CAP_NIL + 1)
 
 /** Kernel IPC interfaces
Index: kernel/generic/include/cap/cap.h
===================================================================
--- kernel/generic/include/cap/cap.h	(revision 0851a3ddbbf4ba5ef46cc8620a6dea43b2b1756a)
+++ kernel/generic/include/cap/cap.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -53,12 +53,15 @@
 
 typedef enum {
+	KOBJECT_TYPE_CALL,
+	KOBJECT_TYPE_IRQ,
 	KOBJECT_TYPE_PHONE,
-	KOBJECT_TYPE_IRQ,
 	KOBJECT_TYPE_MAX
 } kobject_type_t;
 
 struct task;
+
+struct call;
+struct irq;
 struct phone;
-struct irq;
 
 struct kobject;
@@ -79,6 +82,7 @@
 	union {
 		void *raw;
+		struct call *call;
+		struct irq *irq;
 		struct phone *phone;
-		struct irq *irq;
 	};
 } kobject_t;
Index: kernel/generic/include/ipc/ipc.h
===================================================================
--- kernel/generic/include/ipc/ipc.h	(revision 0851a3ddbbf4ba5ef46cc8620a6dea43b2b1756a)
+++ kernel/generic/include/ipc/ipc.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -106,7 +106,13 @@
 	/** Phone which made or last masqueraded this call. */
 	phone_t *phone;
+	/** Flags */
+	unsigned flags;
+	/** User-defined label */
+	sysarg_t label;
 } ipc_data_t;
 
 typedef struct {
+	kobject_t *kobject;
+
 	/**
 	 * Task link.
@@ -115,6 +121,4 @@
 	 */
 	link_t ta_link;
-
-	atomic_t refcnt;
 
 	/** Answerbox link. */
Index: kernel/generic/include/ipc/sysipc.h
===================================================================
--- kernel/generic/include/ipc/sysipc.h	(revision 0851a3ddbbf4ba5ef46cc8620a6dea43b2b1756a)
+++ kernel/generic/include/ipc/sysipc.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -44,5 +44,5 @@
 extern sysarg_t sys_ipc_call_async_fast(sysarg_t, sysarg_t, sysarg_t,
     sysarg_t, sysarg_t, sysarg_t);
-extern sysarg_t sys_ipc_call_async_slow(sysarg_t, ipc_data_t *);
+extern sysarg_t sys_ipc_call_async_slow(sysarg_t, ipc_data_t *, sysarg_t);
 extern sysarg_t sys_ipc_answer_fast(sysarg_t, sysarg_t, sysarg_t, sysarg_t,
     sysarg_t, sysarg_t);
Index: kernel/generic/src/cap/cap.c
===================================================================
--- kernel/generic/src/cap/cap.c	(revision 0851a3ddbbf4ba5ef46cc8620a6dea43b2b1756a)
+++ kernel/generic/src/cap/cap.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -73,4 +73,5 @@
 
 #include <cap/cap.h>
+#include <abi/cap.h>
 #include <proc/task.h>
 #include <synch/mutex.h>
@@ -81,5 +82,7 @@
 #include <stdint.h>
 
-#define MAX_CAPS	INT_MAX
+#define CAPS_START	(CAP_NIL + 1)
+#define CAPS_SIZE	(INT_MAX - CAPS_START)
+#define CAPS_LAST	(CAPS_SIZE - 1)
 
 static slab_cache_t *cap_slab;
@@ -129,5 +132,5 @@
 	if (!task->cap_info->handles)
 		goto error_handles;
-	if (!ra_span_add(task->cap_info->handles, 0, MAX_CAPS))
+	if (!ra_span_add(task->cap_info->handles, CAPS_START, CAPS_SIZE))
 		goto error_span;
 	if (!hash_table_create(&task->cap_info->caps, 0, 0, &caps_ops))
@@ -220,5 +223,5 @@
 	assert(mutex_locked(&task->cap_info->lock));
 
-	if ((handle < 0) || (handle >= MAX_CAPS))
+	if ((handle < CAPS_START) || (handle > CAPS_LAST))
 		return NULL;
 	ht_link_t *link = hash_table_find(&task->cap_info->caps, &handle);
@@ -357,6 +360,6 @@
 void cap_free(task_t *task, cap_handle_t handle)
 {
-	assert(handle >= 0);
-	assert(handle < MAX_CAPS);
+	assert(handle >= CAPS_START);
+	assert(handle <= CAPS_LAST);
 
 	mutex_lock(&task->cap_info->lock);
Index: kernel/generic/src/ipc/ipc.c
===================================================================
--- kernel/generic/src/ipc/ipc.c	(revision 0851a3ddbbf4ba5ef46cc8620a6dea43b2b1756a)
+++ kernel/generic/src/ipc/ipc.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -87,19 +87,18 @@
 }
 
-void ipc_call_hold(call_t *call)
-{
-	atomic_inc(&call->refcnt);
-}
-
-void ipc_call_release(call_t *call)
-{
-	if (atomic_predec(&call->refcnt) == 0) {
-		if (call->buffer)
-			free(call->buffer);
-		if (call->caller_phone)
-			kobject_put(call->caller_phone->kobject);
-		slab_free(call_slab, call);
-	}
-}
+static void call_destroy(void *arg)
+{
+	call_t *call = (call_t *) arg;
+
+	if (call->buffer)
+		free(call->buffer);
+	if (call->caller_phone)
+		kobject_put(call->caller_phone->kobject);
+	slab_free(call_slab, call);
+}
+
+static kobject_ops_t call_kobject_ops = {
+	.destroy = call_destroy
+};
 
 /** Allocate and initialize a call structure.
@@ -117,20 +116,17 @@
 {
 	call_t *call = slab_alloc(call_slab, flags);
-	if (call) {
-		_ipc_call_init(call);
-		ipc_call_hold(call);
-	}
+	if (!call)
+		return NULL;
+	kobject_t *kobj = (kobject_t *) malloc(sizeof(kobject_t), flags);
+	if (!kobj) {
+		slab_free(call_slab, call);
+		return NULL;
+	}
+
+	_ipc_call_init(call);
+	kobject_initialize(kobj, KOBJECT_TYPE_CALL, call, &call_kobject_ops);
+	call->kobject = kobj;
 	
 	return call;
-}
-
-/** Deallocate a call structure.
- *
- * @param call Call structure to be freed.
- *
- */
-void ipc_call_free(call_t *call)
-{
-	ipc_call_release(call);
 }
 
@@ -290,5 +286,5 @@
 		/* This is a forgotten call and call->sender is not valid. */
 		spinlock_unlock(&call->forget_lock);
-		ipc_call_free(call);
+		kobject_put(call->kobject);
 		return;
 	} else {
@@ -705,5 +701,5 @@
 	 * must hold a reference to it.
 	 */
-	ipc_call_hold(call);
+	kobject_add_ref(call->kobject);
 
 	spinlock_unlock(&call->forget_lock);
@@ -714,5 +710,5 @@
 	SYSIPC_OP(request_forget, call);
 
-	ipc_call_release(call);
+	kobject_put(call->kobject);
 }
 
@@ -822,5 +818,5 @@
 	SYSIPC_OP(answer_process, call);
 
-	ipc_call_free(call);
+	kobject_put(call->kobject);
 	goto restart;
 }
Index: kernel/generic/src/ipc/sysipc.c
===================================================================
--- kernel/generic/src/ipc/sysipc.c	(revision 0851a3ddbbf4ba5ef46cc8620a6dea43b2b1756a)
+++ kernel/generic/src/ipc/sysipc.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -286,10 +286,10 @@
 #endif
 
-		ipc_call_hold(call);
+		kobject_add_ref(call->kobject);
 		rc = ipc_call_sync(kobj->phone, call);
 		spinlock_lock(&call->forget_lock);
 		bool forgotten = call->forget;
 		spinlock_unlock(&call->forget_lock);
-		ipc_call_release(call);
+		kobject_put(call->kobject);
 
 #ifdef CONFIG_UDEBUG
@@ -306,5 +306,5 @@
 				 * deallocation.
 				 */
-				ipc_call_free(call);
+				kobject_put(call->kobject);
 			} else {
 				/*
@@ -323,5 +323,5 @@
 	
 	memcpy(data->args, call->data.args, sizeof(data->args));
-	ipc_call_free(call);
+	kobject_put(call->kobject);
 	kobject_put(kobj);
 	
@@ -347,5 +347,5 @@
 /** Make a fast asynchronous call over IPC.
  *
- * This function can only handle four arguments of payload, but is faster than
+ * This function can only handle three arguments of payload, but is faster than
  * the generic function sys_ipc_call_async_slow().
  *
@@ -355,5 +355,5 @@
  * @param arg2     Service-defined payload argument.
  * @param arg3     Service-defined payload argument.
- * @param arg4     Service-defined payload argument.
+ * @param label    User-defined label.
  *
  * @return Call hash on success.
@@ -362,5 +362,5 @@
  */
 sysarg_t sys_ipc_call_async_fast(sysarg_t handle, sysarg_t imethod,
-    sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
+    sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t label)
 {
 	kobject_t *kobj = kobject_get(TASK, handle, KOBJECT_TYPE_PHONE);
@@ -378,5 +378,4 @@
 	IPC_SET_ARG2(call->data, arg2);
 	IPC_SET_ARG3(call->data, arg3);
-	IPC_SET_ARG4(call->data, arg4);
 	
 	/*
@@ -385,4 +384,7 @@
 	 */
 	IPC_SET_ARG5(call->data, 0);
+
+	/* Set the user-defined label */
+	call->data.label = label;
 	
 	int res = request_preprocess(call, kobj->phone);
@@ -401,9 +403,11 @@
  * @param handle  Phone capability for the call.
  * @param data    Userspace address of call data with the request.
+ * @param label   User-defined label.
  *
  * @return See sys_ipc_call_async_fast().
  *
  */
-sysarg_t sys_ipc_call_async_slow(sysarg_t handle, ipc_data_t *data)
+sysarg_t sys_ipc_call_async_slow(sysarg_t handle, ipc_data_t *data,
+    sysarg_t label)
 {
 	kobject_t *kobj = kobject_get(TASK, handle, KOBJECT_TYPE_PHONE);
@@ -420,8 +424,11 @@
 	    sizeof(call->data.args));
 	if (rc != 0) {
-		ipc_call_free(call);
+		kobject_put(call->kobject);
 		kobject_put(kobj);
 		return (sysarg_t) rc;
 	}
+
+	/* Set the user-defined label */
+	call->data.label = label;
 	
 	int res = request_preprocess(call, kobj->phone);
@@ -721,8 +728,4 @@
  *
  * @return Hash of the call.
- *         If IPC_CALLID_NOTIFICATION bit is set in the hash, the
- *         call is a notification. IPC_CALLID_ANSWERED denotes an
- *         answer.
- *
  */
 sysarg_t sys_ipc_wait_for_call(ipc_data_t *calldata, uint32_t usec,
@@ -751,9 +754,10 @@
 		call->data.phone = (void *) call->priv;
 		
+		call->data.flags = IPC_CALLID_NOTIFICATION;
+
 		STRUCT_TO_USPACE(calldata, &call->data);
+		kobject_put(call->kobject);
 		
-		ipc_call_free(call);
-		
-		return ((sysarg_t) call) | IPC_CALLID_NOTIFICATION;
+		return (sysarg_t) call;
 	}
 	
@@ -762,12 +766,14 @@
 		
 		if (call->flags & IPC_CALL_DISCARD_ANSWER) {
-			ipc_call_free(call);
+			kobject_put(call->kobject);
 			goto restart;
 		}
+
+		call->data.flags = IPC_CALLID_ANSWERED;
 		
 		STRUCT_TO_USPACE(calldata, &call->data);
-		ipc_call_free(call);
+		kobject_put(call->kobject);
 		
-		return ((sysarg_t) call) | IPC_CALLID_ANSWERED;
+		return (sysarg_t) call;
 	}
 	
Index: uspace/app/trace/ipcp.c
===================================================================
--- uspace/app/trace/ipcp.c	(revision 0851a3ddbbf4ba5ef46cc8620a6dea43b2b1756a)
+++ uspace/app/trace/ipcp.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -323,5 +323,6 @@
 	pending_call_t *pcall;
 	
-	if ((hash & IPC_CALLID_ANSWERED) == 0 && hash != IPCP_CALLID_SYNC) {
+	if ((call->flags & IPC_CALLID_ANSWERED) == 0 &&
+	    hash != IPCP_CALLID_SYNC) {
 		/* Not a response */
 		if ((display_mask & DM_IPC) != 0) {
@@ -331,6 +332,4 @@
 	}
 	
-	hash = hash & ~IPC_CALLID_ANSWERED;
-	
 	item = hash_table_find(&pending_calls, &hash);
 	if (item == NULL)
Index: uspace/app/trace/syscalls.c
===================================================================
--- uspace/app/trace/syscalls.c	(revision 0851a3ddbbf4ba5ef46cc8620a6dea43b2b1756a)
+++ uspace/app/trace/syscalls.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -54,5 +54,5 @@
 
     [SYS_IPC_CALL_ASYNC_FAST] = { "ipc_call_async_fast", 6,	V_HASH },
-    [SYS_IPC_CALL_ASYNC_SLOW] = { "ipc_call_async_slow", 2,	V_HASH },
+    [SYS_IPC_CALL_ASYNC_SLOW] = { "ipc_call_async_slow", 3,	V_HASH },
 
     [SYS_IPC_ANSWER_FAST] = { "ipc_answer_fast",	6,	V_ERRNO },
Index: uspace/lib/c/generic/async.c
===================================================================
--- uspace/lib/c/generic/async.c	(revision 0851a3ddbbf4ba5ef46cc8620a6dea43b2b1756a)
+++ uspace/lib/c/generic/async.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -1341,5 +1341,5 @@
 	
 	/* Kernel notification */
-	if ((callid & IPC_CALLID_NOTIFICATION)) {
+	if (call->flags & IPC_CALLID_NOTIFICATION) {
 		fibril_t *fibril = (fibril_t *) __tcb_get()->fibril_data;
 		unsigned oldsw = fibril->switches;
@@ -1495,5 +1495,5 @@
 		}
 		
-		if (callid & IPC_CALLID_ANSWERED)
+		if (call.flags & IPC_CALLID_ANSWERED)
 			continue;
 		
Index: uspace/lib/c/generic/ipc.c
===================================================================
--- uspace/lib/c/generic/ipc.c	(revision 0851a3ddbbf4ba5ef46cc8620a6dea43b2b1756a)
+++ uspace/lib/c/generic/ipc.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2006 Ondrej Palkovsky
+ * Copyright (c) 2017 Jakub Jermar
  * All rights reserved.
  *
@@ -50,49 +51,15 @@
 
 /**
- * Structures of this type are used for keeping track
- * of sent asynchronous calls and queing unsent calls.
- */
-typedef struct {
-	link_t list;
-	
+ * Structures of this type are used for keeping track of sent asynchronous calls.
+ */
+typedef struct async_call {
 	ipc_async_callback_t callback;
 	void *private;
 	
-	union {
-		ipc_callid_t callid;
-		struct {
-			ipc_call_t data;
-			int phoneid;
-		} msg;
-	} u;
-	
-	/** Fibril waiting for sending this call. */
-	fid_t fid;
+	struct {
+		ipc_call_t data;
+		int phoneid;
+	} msg;
 } async_call_t;
-
-LIST_INITIALIZE(dispatched_calls);
-
-/** List of asynchronous calls that were not accepted by kernel.
- *
- * Protected by async_futex, because if the call is not accepted
- * by the kernel, the async framework is used automatically.
- *
- */
-LIST_INITIALIZE(queued_calls);
-
-static futex_t ipc_futex = FUTEX_INITIALIZER;
-
-/** Send asynchronous message via syscall.
- *
- * @param phoneid Phone handle for the call.
- * @param data    Call data with the request.
- *
- * @return Hash of the call or an error code.
- *
- */
-static ipc_callid_t ipc_call_async_internal(int phoneid, ipc_call_t *data)
-{
-	return __SYSCALL2(SYS_IPC_CALL_ASYNC_SLOW, phoneid, (sysarg_t) data);
-}
 
 /** Prologue for ipc_call_async_*() functions.
@@ -133,11 +100,8 @@
 	if (!call) {
 		/* Nothing to do regardless if failed or not */
-		futex_unlock(&ipc_futex);
 		return;
 	}
 	
 	if (callid == (ipc_callid_t) IPC_CALLRET_FATAL) {
-		futex_unlock(&ipc_futex);
-		
 		/* Call asynchronous handler with error code */
 		if (call->callback)
@@ -147,15 +111,9 @@
 		return;
 	}
-	
-	call->u.callid = callid;
-	
-	/* Add call to the list of dispatched calls */
-	list_append(&call->list, &dispatched_calls);
-	futex_unlock(&ipc_futex);
 }
 
 /** Fast asynchronous call.
  *
- * This function can only handle four arguments of payload. It is, however,
+ * This function can only handle three arguments of payload. It is, however,
  * faster than the more generic ipc_call_async_slow().
  *
@@ -171,28 +129,16 @@
  * @param arg2        Service-defined payload argument.
  * @param arg3        Service-defined payload argument.
- * @param arg4        Service-defined payload argument.
  * @param private     Argument to be passed to the answer/error callback.
  * @param callback    Answer or error callback.
  */
 void ipc_call_async_fast(int phoneid, sysarg_t imethod, sysarg_t arg1,
-    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, void *private,
-    ipc_async_callback_t callback)
-{
-	async_call_t *call = NULL;
-	
-	if (callback) {
-		call = ipc_prepare_async(private, callback);
-		if (!call)
-			return;
-	}
-	
-	/*
-	 * We need to make sure that we get callid
-	 * before another thread accesses the queue again.
-	 */
-	
-	futex_lock(&ipc_futex);
+    sysarg_t arg2, sysarg_t arg3, void *private, ipc_async_callback_t callback)
+{
+	async_call_t *call = ipc_prepare_async(private, callback);
+	if (!call)
+		return;
+	
 	ipc_callid_t callid = __SYSCALL6(SYS_IPC_CALL_ASYNC_FAST, phoneid,
-	    imethod, arg1, arg2, arg3, arg4);
+	    imethod, arg1, arg2, arg3, (sysarg_t) call);
 	
 	ipc_finish_async(callid, phoneid, call);
@@ -225,19 +171,13 @@
 		return;
 	
-	IPC_SET_IMETHOD(call->u.msg.data, imethod);
-	IPC_SET_ARG1(call->u.msg.data, arg1);
-	IPC_SET_ARG2(call->u.msg.data, arg2);
-	IPC_SET_ARG3(call->u.msg.data, arg3);
-	IPC_SET_ARG4(call->u.msg.data, arg4);
-	IPC_SET_ARG5(call->u.msg.data, arg5);
-	
-	/*
-	 * We need to make sure that we get callid
-	 * before another threadaccesses the queue again.
-	 */
-	
-	futex_lock(&ipc_futex);
-	ipc_callid_t callid =
-	    ipc_call_async_internal(phoneid, &call->u.msg.data);
+	IPC_SET_IMETHOD(call->msg.data, imethod);
+	IPC_SET_ARG1(call->msg.data, arg1);
+	IPC_SET_ARG2(call->msg.data, arg2);
+	IPC_SET_ARG3(call->msg.data, arg3);
+	IPC_SET_ARG4(call->msg.data, arg4);
+	IPC_SET_ARG5(call->msg.data, arg5);
+	
+	ipc_callid_t callid = __SYSCALL3(SYS_IPC_CALL_ASYNC_SLOW, phoneid,
+	    (sysarg_t) &call->msg.data, (sysarg_t) call);
 	
 	ipc_finish_async(callid, phoneid, call);
@@ -296,88 +236,19 @@
 }
 
-/** Try to dispatch queued calls from the async queue.
- *
- */
-static void dispatch_queued_calls(void)
-{
-	/** @todo
-	 * Integrate intelligently ipc_futex so that it is locked during
-	 * ipc_call_async_*() until it is added to dispatched_calls.
-	 */
-	
-	futex_down(&async_futex);
-	
-	while (!list_empty(&queued_calls)) {
-		async_call_t *call =
-		    list_get_instance(list_first(&queued_calls), async_call_t, list);
-		ipc_callid_t callid =
-		    ipc_call_async_internal(call->u.msg.phoneid, &call->u.msg.data);
-		
-		list_remove(&call->list);
-		
-		futex_up(&async_futex);
-		
-		assert(call->fid);
-		fibril_add_ready(call->fid);
-		
-		if (callid == (ipc_callid_t) IPC_CALLRET_FATAL) {
-			if (call->callback)
-				call->callback(call->private, ENOENT, NULL);
-			
-			free(call);
-		} else {
-			call->u.callid = callid;
-			
-			futex_lock(&ipc_futex);
-			list_append(&call->list, &dispatched_calls);
-			futex_unlock(&ipc_futex);
-		}
-		
-		futex_down(&async_futex);
-	}
-	
-	futex_up(&async_futex);
-}
-
 /** Handle received answer.
- *
- * Find the hash of the answer and call the answer callback.
- *
- * The answer has the same hash as the request OR'ed with
- * the IPC_CALLID_ANSWERED bit.
- *
- * @todo Use hash table.
  *
  * @param callid Hash of the received answer.
  * @param data   Call data of the answer.
- *
  */
 static void handle_answer(ipc_callid_t callid, ipc_call_t *data)
 {
-	callid &= ~IPC_CALLID_ANSWERED;
-	
-	futex_lock(&ipc_futex);
-	
-	link_t *item;
-	for (item = dispatched_calls.head.next; item != &dispatched_calls.head;
-	    item = item->next) {
-		async_call_t *call =
-		    list_get_instance(item, async_call_t, list);
-		
-		if (call->u.callid == callid) {
-			list_remove(&call->list);
-			
-			futex_unlock(&ipc_futex);
-			
-			if (call->callback)
-				call->callback(call->private,
-				    IPC_GET_RETVAL(*data), data);
-			
-			free(call);
-			return;
-		}
-	}
-	
-	futex_unlock(&ipc_futex);
+	async_call_t *call = data->label;
+
+	if (!call)
+		return;
+
+	if (call->callback)
+		call->callback(call->private, IPC_GET_RETVAL(*data), data);
+	free(call);
 }
 
@@ -388,8 +259,5 @@
  * @param flags Flags passed to SYS_IPC_WAIT (blocking, nonblocking).
  *
- * @return Hash of the call. Note that certain bits have special
- *         meaning: IPC_CALLID_ANSWERED is set in an answer
- *         and IPC_CALLID_NOTIFICATION is used for notifications.
- *
+ * @return Hash of the call.
  */
 ipc_callid_t ipc_wait_cycle(ipc_call_t *call, sysarg_t usec,
@@ -400,8 +268,6 @@
 	
 	/* Handle received answers */
-	if (callid & IPC_CALLID_ANSWERED) {
+	if (callid && (call->flags & IPC_CALLID_ANSWERED))
 		handle_answer(callid, call);
-		dispatch_queued_calls();
-	}
 	
 	return callid;
@@ -432,5 +298,5 @@
 	do {
 		callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE);
-	} while (callid & IPC_CALLID_ANSWERED);
+	} while (callid && (call->flags & IPC_CALLID_ANSWERED));
 	
 	return callid;
@@ -453,5 +319,5 @@
 		callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT,
 		    SYNCH_FLAGS_NON_BLOCKING);
-	} while (callid & IPC_CALLID_ANSWERED);
+	} while (callid && (call->flags & IPC_CALLID_ANSWERED));
 	
 	return callid;
Index: uspace/lib/c/include/ipc/common.h
===================================================================
--- uspace/lib/c/include/ipc/common.h	(revision 0851a3ddbbf4ba5ef46cc8620a6dea43b2b1756a)
+++ uspace/lib/c/include/ipc/common.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -43,8 +43,12 @@
 #define IPC_FLAG_BLOCKING  0x01
 
+struct async_call;
+
 typedef struct {
 	sysarg_t args[IPC_CALL_LEN];
 	task_id_t in_task_id;
 	sysarg_t in_phone_hash;
+	unsigned flags;
+	struct async_call *label;
 } ipc_call_t;
 
Index: uspace/lib/c/include/ipc/ipc.h
===================================================================
--- uspace/lib/c/include/ipc/ipc.h	(revision 0851a3ddbbf4ba5ef46cc8620a6dea43b2b1756a)
+++ uspace/lib/c/include/ipc/ipc.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -89,19 +89,18 @@
 
 #define ipc_call_async_0(phoneid, method, private, callback) \
-	ipc_call_async_fast((phoneid), (method), 0, 0, 0, 0, (private), \
-	    (callback))
+	ipc_call_async_fast((phoneid), (method), 0, 0, 0, (private), (callback))
 #define ipc_call_async_1(phoneid, method, arg1, private, callback) \
-	ipc_call_async_fast((phoneid), (method), (arg1), 0, 0, 0, (private), \
+	ipc_call_async_fast((phoneid), (method), (arg1), 0, 0, (private), \
 	    (callback))
 #define ipc_call_async_2(phoneid, method, arg1, arg2, private, callback) \
-	ipc_call_async_fast((phoneid), (method), (arg1), (arg2), 0, 0, \
+	ipc_call_async_fast((phoneid), (method), (arg1), (arg2), 0, \
 	    (private), (callback))
 #define ipc_call_async_3(phoneid, method, arg1, arg2, arg3, private, callback) \
-	ipc_call_async_fast((phoneid), (method), (arg1), (arg2), (arg3), 0, \
+	ipc_call_async_fast((phoneid), (method), (arg1), (arg2), (arg3), \
 	    (private), (callback))
 #define ipc_call_async_4(phoneid, method, arg1, arg2, arg3, arg4, private, \
     callback) \
-	ipc_call_async_fast((phoneid), (method), (arg1), (arg2), (arg3), \
-	    (arg4), (private), (callback))
+	ipc_call_async_slow((phoneid), (method), (arg1), (arg2), (arg3), \
+	    (arg4), 0, (private), (callback))
 #define ipc_call_async_5(phoneid, method, arg1, arg2, arg3, arg4, arg5, \
     private, callback) \
@@ -110,5 +109,5 @@
 
 extern void ipc_call_async_fast(int, sysarg_t, sysarg_t, sysarg_t, sysarg_t,
-    sysarg_t, void *, ipc_async_callback_t);
+    void *, ipc_async_callback_t);
 extern void ipc_call_async_slow(int, sysarg_t, sysarg_t, sysarg_t, sysarg_t,
     sysarg_t, sysarg_t, void *, ipc_async_callback_t);
