Index: generic/src/ipc/ipc.c
===================================================================
--- generic/src/ipc/ipc.c	(revision ba81cab60a5926ed0eb9888e74ef22a77ec9792e)
+++ generic/src/ipc/ipc.c	(revision b4b45210fc5bef84b7f8456d96320f10dd3f5245)
@@ -124,26 +124,4 @@
 }
 
-/** Disconnect phone from answerbox
- *
- * It is allowed to call disconnect on already disconnected phone\
- */
-void ipc_phone_destroy(phone_t *phone)
-{
-	answerbox_t *box = phone->callee;
-	
-	ASSERT(box);
-
-	spinlock_lock(&phone->lock);
-	spinlock_lock(&box->lock);
-
-	if (phone->callee) {
-		list_remove(&phone->list);
-		phone->callee = NULL;
-	}
-
-	spinlock_unlock(&box->lock);
-	spinlock_unlock(&phone->lock);
-}
-
 /** Helper function to facilitate synchronous calls */
 void ipc_call_sync(phone_t *phone, call_t *request)
@@ -191,4 +169,16 @@
 }
 
+/* Unsafe unchecking ipc_call */
+static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
+{
+	atomic_inc(&phone->active_calls);
+	call->data.phone = phone;
+
+	spinlock_lock(&box->lock);
+	list_append(&call->list, &box->calls);
+	spinlock_unlock(&box->lock);
+	waitq_wakeup(&box->wq, 0);
+}
+
 /** Send a asynchronous request using phone to answerbox
  *
@@ -201,18 +191,53 @@
 
 	spinlock_lock(&phone->lock);
+
 	box = phone->callee;
 	if (!box) {
 		/* Trying to send over disconnected phone */
+		spinlock_unlock(&phone->lock);
+
+		call->data.phone = phone;
 		IPC_SET_RETVAL(call->data, ENOENT);
 		_ipc_answer_free_call(call);
 		return;
 	}
-
-	spinlock_lock(&box->lock);
-	list_append(&call->list, &box->calls);
-	spinlock_unlock(&box->lock);
-	waitq_wakeup(&box->wq, 0);
+	_ipc_call(phone, box, call);
 	
 	spinlock_unlock(&phone->lock);
+}
+
+/** Disconnect phone from answerbox
+ *
+ * It is allowed to call disconnect on already disconnected phone
+ *
+ * @return 0 - phone disconnected, -1 - the phone was already disconnected
+ */
+int ipc_phone_hangup(phone_t *phone)
+{
+	answerbox_t *box;
+	call_t *call;
+	
+	spinlock_lock(&phone->lock);
+	box = phone->callee;
+	if (!box) {
+		spinlock_unlock(&phone->lock);
+		return -1;
+	}
+
+	spinlock_lock(&box->lock);
+	list_remove(&phone->list);
+	phone->callee = NULL;
+	spinlock_unlock(&box->lock);
+
+	call = ipc_call_alloc();
+	IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
+	call->flags |= IPC_CALL_DISCARD_ANSWER;
+	_ipc_call(phone, box, call);
+
+	phone->busy = 0;
+
+	spinlock_unlock(&phone->lock);
+
+	return 0;
 }
 
@@ -226,4 +251,5 @@
 {
 	spinlock_lock(&oldbox->lock);
+	atomic_dec(&call->data.phone->active_calls);
 	list_remove(&call->list);
 	spinlock_unlock(&oldbox->lock);
@@ -242,28 +268,30 @@
 	call_t *request;
 
-	spinlock_lock(&box->lock);
-	while (1) { 
-		if (!list_empty(&box->answers)) {
-			/* Handle asynchronous answers */
-			request = list_get_instance(box->answers.next, call_t, list);
-			list_remove(&request->list);
-		} else if (!list_empty(&box->calls)) {
-			/* Handle requests */
-			request = list_get_instance(box->calls.next, call_t, list);
-			list_remove(&request->list);
-			/* Append request to dispatch queue */
-			list_append(&request->list, &box->dispatched_calls);
-			request->flags |= IPC_CALL_DISPATCHED;
-		} else {
-			if (!(flags & IPC_WAIT_NONBLOCKING)) {
-				/* Wait for event to appear */
-				spinlock_unlock(&box->lock);
-				waitq_sleep(&box->wq);
-				spinlock_lock(&box->lock);
-				continue;
-			}
-			request = NULL;
-		}
-		break;
+restart:      
+	if (flags & IPC_WAIT_NONBLOCKING) {
+		if (waitq_sleep_timeout(&box->wq,0,1) == ESYNCH_WOULD_BLOCK)
+			return NULL;
+	} else 
+		waitq_sleep(&box->wq);
+	
+	spinlock_lock(&box->lock);
+	if (!list_empty(&box->answers)) {
+		/* Handle asynchronous answers */
+		request = list_get_instance(box->answers.next, call_t, list);
+		list_remove(&request->list);
+		printf("%d %P\n", IPC_GET_METHOD(request->data), 
+		       request->data.phone);
+		atomic_dec(&request->data.phone->active_calls);
+	} else if (!list_empty(&box->calls)) {
+		/* Handle requests */
+		request = list_get_instance(box->calls.next, call_t, list);
+		list_remove(&request->list);
+		/* Append request to dispatch queue */
+		list_append(&request->list, &box->dispatched_calls);
+		request->flags |= IPC_CALL_DISPATCHED;
+	} else {
+		printf("WARNING: Spurious IPC wakeup.\n");
+		spinlock_unlock(&box->lock);
+		goto restart;
 	}
 	spinlock_unlock(&box->lock);
Index: generic/src/ipc/ipcrsc.c
===================================================================
--- generic/src/ipc/ipcrsc.c	(revision ba81cab60a5926ed0eb9888e74ef22a77ec9792e)
+++ generic/src/ipc/ipcrsc.c	(revision b4b45210fc5bef84b7f8456d96320f10dd3f5245)
@@ -37,4 +37,6 @@
  * - find phone in slot and send a message using phone
  * - answer message to phone
+ * - hangup phone (the caller has hung up)
+ * - hangup phone (the answerbox is exiting)
  * 
  * Locking strategy
@@ -51,15 +53,6 @@
  *   and proper reply will be generated.
  *
- * - There may be objection that a race may occur when the syscall finds
- *   an appropriate call and before executing ipc_send, the phone call might
- *   be disconnected and connected elsewhere. As there is no easy solution,
- *   the application will be notified by an  'PHONE_DISCONNECTED' message
- *   and the phone will not be allocated before the application notifies
- *   the kernel subsystem that it does not have any pending calls regarding
- *   this phone call.
- *
  * Locking order
  *
- * There are 2 possibilities
  * - first phone, then answerbox
  *   + Easy locking on calls
@@ -68,26 +61,31 @@
  *     The only possibility is try_lock with restart of list traversal.
  *
- * - first answerbox, then phone(s)
- *   + Easy phone disconnect
- *   - Multiple checks needed when sending message
+ * Destroying is less frequent, this approach is taken.
  *
- * Because the answerbox destroyal is much less frequent operation, 
- * the first method is chosen.
+ * Phone hangup
+ * 
+ * *** The caller hangs up (sys_ipc_hangup) ***
+ * - The phone is disconnected (no more messages can be sent over this phone),
+ *   all in-progress messages are correctly handled. The anwerbox receives
+ *   IPC_M_PHONE_HUNGUP call from the phone that hung up. When all async
+ *   calls are answered, the phone is deallocated.
+ *
+ * *** The answerbox hangs up (ipc_answer(ESLAM))
+ * - The phone is disconnected. IPC_M_ANSWERBOX_HUNGUP notification
+ *   is sent to source task, the calling process is expected to
+ *   send an sys_ipc_hangup after cleaning up it's internal structures.
  *
  * Cleanup strategy
  * 
- * 1) Disconnect all phones.
+ * 1) Disconnect all our phones ('sys_ipc_hangup')
+ *
+ * 2) Disconnect all phones connected to answerbox.
  *    * Send message 'PHONE_DISCONNECTED' to the target application 
  * - Once all phones are disconnected, no further calls can arrive
  *
- * 2) Answer all messages in 'calls' and 'dispatched_calls' queues with
+ * 3) Answer all messages in 'calls' and 'dispatched_calls' queues with
  *    appropriate error code.
  *
- * 3) Wait for all async answers to arrive
- * Alternatively - we might try to invalidate all messages by setting some
- * flag, that would dispose of the message once it is answered. This
- * would need to link all calls in one big list, which we don't currently
- * do.
- * 
+ * 4) Wait for all async answers to arrive
  * 
  */
@@ -119,5 +117,5 @@
 	
 	for (i=0; i < IPC_MAX_PHONES; i++) {
-		if (!TASK->phones[i].busy) {
+		if (!TASK->phones[i].busy && !atomic_get(&TASK->phones[i].active_calls)) {
 			TASK->phones[i].busy = 1;
 			break;
@@ -131,5 +129,8 @@
 }
 
-/** Disconnect phone */
+/** Disconnect phone a free the slot
+ *
+ * All already sent messages will be correctly processed
+ */
 void phone_dealloc(int phoneid)
 {
@@ -137,7 +138,5 @@
 
 	ASSERT(TASK->phones[phoneid].busy);
-
-	if (TASK->phones[phoneid].callee)
-		ipc_phone_destroy(&TASK->phones[phoneid]);
+	ASSERT(! TASK->phones[phoneid].callee);
 
 	TASK->phones[phoneid].busy = 0;
Index: generic/src/ipc/sysipc.c
===================================================================
--- generic/src/ipc/sysipc.c	(revision ba81cab60a5926ed0eb9888e74ef22a77ec9792e)
+++ generic/src/ipc/sysipc.c	(revision b4b45210fc5bef84b7f8456d96320f10dd3f5245)
@@ -43,9 +43,4 @@
 #include <proc/thread.h>
 
-/* TODO: multi-threaded connect-to-me can cause race condition
- * on phone, add counter + thread_kill??
- *
- */
-
 #define GET_CHECK_PHONE(phone,phoneid,err) { \
       if (phoneid > IPC_MAX_PHONES) { err; } \
@@ -53,4 +48,5 @@
 }
 
+#define STRUCT_TO_USPACE(dst,src) copy_to_uspace(dst,src,sizeof(*src))
 
 /** Return true if the method is a system method */
@@ -69,4 +65,6 @@
 static inline int is_forwardable(__native method)
 {
+	if (method == IPC_M_PHONE_HUNGUP)
+		return 0; /* This message is meant only for the receiver */
 	return 1;
 }
@@ -82,7 +80,7 @@
 static inline int answer_will_preprocess(call_t *call)
 {
-	if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME)
+	if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
 		return 1;
-	if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTMETO)
+	if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_ME_TO)
 		return 1;
 	return 0;
@@ -94,5 +92,5 @@
 	int phoneid;
 
-	if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECTTOME) {
+	if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_TO_ME) {
 		phoneid = IPC_GET_ARG3(*olddata);
 		if (IPC_GET_RETVAL(answer->data)) {
@@ -103,5 +101,5 @@
 			phone_connect(phoneid,&answer->sender->answerbox);
 		}
-	} else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECTMETO) {
+	} else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME_TO) {
 		/* If the users accepted call, connect */
 		if (!IPC_GET_RETVAL(answer->data)) {
@@ -131,5 +129,5 @@
 	int phoneid;
 
-	if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME) {
+	if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME) {
 		phoneid = phone_alloc();
 		if (phoneid < 0) { /* Failed to allocate phone */
@@ -149,5 +147,5 @@
  */
 __native sys_ipc_call_sync_fast(__native phoneid, __native method, 
-				__native arg1, __native *data)
+				__native arg1, ipc_data_t *data)
 {
 	call_t call;
@@ -165,5 +163,5 @@
 	ipc_call_sync(phone, &call);
 
-	copy_to_uspace(data, &call.data, sizeof(call.data));
+	STRUCT_TO_USPACE(&data->args, &call.data.args);
 
 	return 0;
@@ -171,6 +169,6 @@
 
 /** Synchronous IPC call allowing to send whole message */
-__native sys_ipc_call_sync(__native phoneid, __native *question, 
-			   __native *reply)
+__native sys_ipc_call_sync(__native phoneid, ipc_data_t *question, 
+			   ipc_data_t *reply)
 {
 	call_t call;
@@ -178,5 +176,5 @@
 
 	ipc_call_static_init(&call);
-	copy_from_uspace(&call.data, question, sizeof(call.data));
+	copy_from_uspace(&call.data.args, &question->args, sizeof(call.data.args));
 
 	if (is_system_method(IPC_GET_METHOD(call.data)))
@@ -187,5 +185,5 @@
 	ipc_call_sync(phone, &call);
 
-	copy_to_uspace(reply, &call.data, sizeof(call.data));
+	STRUCT_TO_USPACE(&reply->args, &call.data.args);
 
 	return 0;
@@ -238,5 +236,5 @@
  * @return The same as sys_ipc_call_async
  */
-__native sys_ipc_call_async(__native phoneid, __native *data)
+__native sys_ipc_call_async(__native phoneid, ipc_data_t *data)
 {
 	call_t *call;
@@ -249,5 +247,5 @@
 
 	call = ipc_call_alloc();
-	copy_from_uspace(&call->data, data, sizeof(call->data));
+	copy_from_uspace(&call->data.args, &data->args, sizeof(call->data.args));
 
 	if (is_system_method(IPC_GET_METHOD(call->data))) {
@@ -335,5 +333,5 @@
 
 /** Send IPC answer */
-inline __native sys_ipc_answer(__native callid, __native *data)
+__native sys_ipc_answer(__native callid, ipc_data_t *data)
 {
 	call_t *call;
@@ -349,5 +347,6 @@
 		preprocess = 1;
 	}
-	copy_from_uspace(&call->data, data, sizeof(call->data));
+	copy_from_uspace(&call->data.args, &data->args, 
+			 sizeof(call->data.args));
 
 	if (preprocess)
@@ -370,5 +369,5 @@
 
 	ipc_call_static_init(&call);
-	IPC_SET_METHOD(call.data, IPC_M_CONNECTTOME);
+	IPC_SET_METHOD(call.data, IPC_M_CONNECT_TO_ME);
 	IPC_SET_ARG1(call.data, arg1);
 	IPC_SET_ARG2(call.data, arg2);
@@ -379,7 +378,5 @@
 
 	if (!IPC_GET_RETVAL(call.data) && taskid)
-		copy_to_uspace(taskid, 
-			       &phone->callee->task->taskid,
-			       sizeof(TASK->taskid));
+		STRUCT_TO_USPACE(taskid, &phone->callee->task->taskid);
 
 	return IPC_GET_RETVAL(call.data);
@@ -404,5 +401,5 @@
 
 	ipc_call_static_init(&call);
-	IPC_SET_METHOD(call.data, IPC_M_CONNECTMETO);
+	IPC_SET_METHOD(call.data, IPC_M_CONNECT_ME_TO);
 	IPC_SET_ARG1(call.data, arg1);
 	IPC_SET_ARG2(call.data, arg2);
@@ -419,17 +416,31 @@
 }
 
+/** Hang up the phone
+ *
+ * 
+ *
+ */
+__native sys_ipc_hangup(int phoneid)
+{
+	phone_t *phone;
+
+	GET_CHECK_PHONE(phone, phoneid, return ENOENT);
+
+	if (ipc_phone_hangup(phone))
+		return -1;
+
+	return 0;
+}
+
 /** Wait for incoming ipc call or answer
  *
- * Generic function - can serve either as inkernel or userspace call
- * - inside kernel does probably unnecessary copying of data (TODO)
- *
- * @param result 
- * @param taskid 
+ * @param calldata Pointer to buffer where the call/answer data is stored 
+ * @param taskid On 'CONNECT_ME_TO' call it is filled with 'taskid' of
+ *               the caller.
  * @param flags
  * @return Callid, if callid & 1, then the call is answer
  */
-inline __native sys_ipc_wait_for_call(ipc_data_t *calldata, 
-				      task_id_t *taskid,
-				      __native flags)
+__native sys_ipc_wait_for_call(ipc_data_t *calldata, task_id_t *taskid,
+			       __native flags)
 					     
 {
@@ -443,16 +454,26 @@
 			goto restart;
 
-		copy_to_uspace(calldata, &call->data, sizeof(call->data));
+		ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
+
 		atomic_dec(&TASK->active_calls);
-		ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
+
+		if (call->flags & IPC_CALL_DISCARD_ANSWER) {
+			ipc_call_free(call);
+			goto restart;
+		}
+
+		STRUCT_TO_USPACE(&calldata->args, &call->data.args);
 		ipc_call_free(call);
 
 		return ((__native)call) | IPC_CALLID_ANSWERED;
 	}
+
 	if (process_request(&TASK->answerbox, call))
 		goto restart;
-	copy_to_uspace(calldata, &call->data, sizeof(call->data));
-	copy_to_uspace(taskid, (void *)&TASK->taskid, sizeof(TASK->taskid));
+
+	/* Include phone address('id') of the caller in the request */
+	STRUCT_TO_USPACE(calldata, &call->data);
+	if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_ME_TO)
+		STRUCT_TO_USPACE(taskid, &TASK->taskid);
 	return (__native)call;
 }
-
