Index: uspace/lib/c/generic/async/server.c
===================================================================
--- uspace/lib/c/generic/async/server.c	(revision 808026296d66a63b501dc0af9d457077cd81e32b)
+++ uspace/lib/c/generic/async/server.c	(revision f787c8e603e6091f428e7d4b79342b95a4fc26f4)
@@ -124,10 +124,4 @@
 #define DPRINTF(...)  ((void) 0)
 
-/** Call data */
-typedef struct {
-	link_t link;
-	ipc_call_t call;
-} msg_t;
-
 /* Client connection data */
 typedef struct {
@@ -156,15 +150,9 @@
 	client_t *client;
 
-	/** Message event. */
-	fibril_event_t msg_arrived;
-
-	/** Messages that should be delivered to this fibril. */
-	list_t msg_queue;
+	/** Channel for messages that should be delivered to this fibril. */
+	mpsc_t *msg_channel;
 
 	/** Call data of the opening call. */
 	ipc_call_t call;
-
-	/** Identification of the closing call. */
-	cap_call_handle_t close_chandle;
 
 	/** Fibril function that will be used to handle the connection. */
@@ -423,12 +411,20 @@
 	async_client_put(client);
 
+	fibril_rmutex_lock(&conn_mutex);
+
 	/*
 	 * Remove myself from the connection hash table.
 	 */
-	fibril_rmutex_lock(&conn_mutex);
 	hash_table_remove(&conn_hash_table, &(conn_key_t){
 		.task_id = fibril_connection->in_task_id,
 		.phone_hash = fibril_connection->in_phone_hash
 	});
+
+	/*
+	 * Close the channel, if it isn't closed already.
+	 */
+	mpsc_t *c = fibril_connection->msg_channel;
+	mpsc_close(c);
+
 	fibril_rmutex_unlock(&conn_mutex);
 
@@ -436,21 +432,12 @@
 	 * Answer all remaining messages with EHANGUP.
 	 */
-	while (!list_empty(&fibril_connection->msg_queue)) {
-		msg_t *msg =
-		    list_get_instance(list_first(&fibril_connection->msg_queue),
-		    msg_t, link);
-
-		list_remove(&msg->link);
-		ipc_answer_0(msg->call.cap_handle, EHANGUP);
-		free(msg);
-	}
+	ipc_call_t call;
+	while (mpsc_receive(c, &call, NULL) == EOK)
+		ipc_answer_0(call.cap_handle, EHANGUP);
 
 	/*
-	 * If the connection was hung-up, answer the last call,
-	 * i.e. IPC_M_PHONE_HUNGUP.
+	 * Clean up memory.
 	 */
-	if (fibril_connection->close_chandle)
-		ipc_answer_0(fibril_connection->close_chandle, EOK);
-
+	mpsc_destroy(c);
 	free(fibril_connection);
 	return EOK;
@@ -488,7 +475,5 @@
 	conn->in_task_id = in_task_id;
 	conn->in_phone_hash = in_phone_hash;
-	conn->msg_arrived = FIBRIL_EVENT_INIT;
-	list_initialize(&conn->msg_queue);
-	conn->close_chandle = CAP_NIL;
+	conn->msg_channel = mpsc_create(sizeof(ipc_call_t));
 	conn->handler = handler;
 	conn->data = data;
@@ -503,4 +488,5 @@
 
 	if (conn->fid == 0) {
+		mpsc_destroy(conn->msg_channel);
 		free(conn);
 
@@ -606,9 +592,10 @@
  * @param call Data of the incoming call.
  *
- * @return False if the call doesn't match any connection.
- * @return True if the call was passed to the respective connection fibril.
- *
- */
-static bool route_call(ipc_call_t *call)
+ * @return EOK if the call was successfully passed to the respective fibril.
+ * @return ENOENT if the call doesn't match any connection.
+ * @return Other error code if routing failed for other reasons.
+ *
+ */
+static errno_t route_call(ipc_call_t *call)
 {
 	assert(call);
@@ -622,27 +609,22 @@
 	if (!link) {
 		fibril_rmutex_unlock(&conn_mutex);
-		return false;
+		return ENOENT;
 	}
 
 	connection_t *conn = hash_table_get_inst(link, connection_t, link);
 
-	// FIXME: malloc in critical section
-	msg_t *msg = malloc(sizeof(*msg));
-	if (!msg) {
-		fibril_rmutex_unlock(&conn_mutex);
-		return false;
-	}
-
-	msg->call = *call;
-	list_append(&msg->link, &conn->msg_queue);
-
-	if (IPC_GET_IMETHOD(*call) == IPC_M_PHONE_HUNGUP)
-		conn->close_chandle = call->cap_handle;
+	errno_t rc = mpsc_send(conn->msg_channel, call);
+
+	if (IPC_GET_IMETHOD(*call) == IPC_M_PHONE_HUNGUP) {
+		/* Close the channel, but let the connection fibril answer. */
+		mpsc_close(conn->msg_channel);
+		// FIXME: Ideally, we should be able to discard/answer the
+		//        hungup message here and just close the channel without
+		//        passing it out. Unfortunatelly, somehow that breaks
+		//        handling of CPU exceptions.
+	}
 
 	fibril_rmutex_unlock(&conn_mutex);
-
-	/* If the connection fibril is waiting for an event, activate it */
-	fibril_notify(&conn->msg_arrived);
-	return true;
+	return rc;
 }
 
@@ -939,13 +921,4 @@
 	assert(fibril_connection);
 
-	/*
-	 * Why doing this?
-	 * GCC 4.1.0 coughs on fibril_connection-> dereference.
-	 * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
-	 *           I would never expect to find so many errors in
-	 *           a compiler.
-	 */
-	connection_t *conn = fibril_connection;
-
 	struct timeval tv;
 	struct timeval *expires = NULL;
@@ -956,40 +929,19 @@
 	}
 
-	fibril_rmutex_lock(&conn_mutex);
-
-	/* If nothing in queue, wait until something arrives */
-	while (list_empty(&conn->msg_queue)) {
-		if (conn->close_chandle) {
-			/*
-			 * Handle the case when the connection was already
-			 * closed by the client but the server did not notice
-			 * the first IPC_M_PHONE_HUNGUP call and continues to
-			 * call async_get_call_timeout(). Repeat
-			 * IPC_M_PHONE_HUNGUP until the caller notices.
-			 */
-			memset(call, 0, sizeof(ipc_call_t));
-			IPC_SET_IMETHOD(*call, IPC_M_PHONE_HUNGUP);
-			fibril_rmutex_unlock(&conn_mutex);
-			return true;
-		}
-
-		// TODO: replace with cvar
-		fibril_rmutex_unlock(&conn_mutex);
-
-		errno_t rc = fibril_wait_timeout(&conn->msg_arrived, expires);
-		if (rc == ETIMEOUT)
-			return false;
-
-		fibril_rmutex_lock(&conn_mutex);
-	}
-
-	msg_t *msg = list_get_instance(list_first(&conn->msg_queue),
-	    msg_t, link);
-	list_remove(&msg->link);
-
-	*call = msg->call;
-	free(msg);
-
-	fibril_rmutex_unlock(&conn_mutex);
+	errno_t rc = mpsc_receive(fibril_connection->msg_channel,
+	    call, expires);
+
+	if (rc == ETIMEOUT)
+		return false;
+
+	if (rc != EOK) {
+		/*
+		 * The async_get_call_timeout() interface doesn't support
+		 * propagating errors. Return a null call instead.
+		 */
+
+		memset(call, 0, sizeof(ipc_call_t));
+	}
+
 	return true;
 }
@@ -1071,9 +1023,13 @@
 
 	/* Try to route the call through the connection hash table */
-	if (route_call(call))
+	errno_t rc = route_call(call);
+	if (rc == EOK)
 		return;
 
-	/* Unknown call from unknown phone - hang it up */
-	ipc_answer_0(call->cap_handle, EHANGUP);
+	// TODO: Log the error.
+
+	if (call->cap_handle != CAP_NIL)
+		/* Unknown call from unknown phone - hang it up */
+		ipc_answer_0(call->cap_handle, EHANGUP);
 }
 
