Index: uspace/app/tester/Makefile
===================================================================
--- uspace/app/tester/Makefile	(revision b8d6783a99ca20a9a760d0859f69a1b5a920212a)
+++ uspace/app/tester/Makefile	(revision f76696fc11f7c97060cb5bfd3e184a0f78e483f9)
@@ -50,4 +50,5 @@
 	vfs/vfs1.c \
 	ipc/ping_pong.c \
+	ipc/starve.c \
 	loop/loop1.c \
 	mm/common.c \
Index: uspace/app/tester/ipc/starve.c
===================================================================
--- uspace/app/tester/ipc/starve.c	(revision f76696fc11f7c97060cb5bfd3e184a0f78e483f9)
+++ uspace/app/tester/ipc/starve.c	(revision f76696fc11f7c97060cb5bfd3e184a0f78e483f9)
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2012 Vojtech Horky
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <io/console.h>
+#include <async.h>
+#include "../tester.h"
+
+#define DURATION_SECS      30
+
+const char *test_starve_ipc(void)
+{
+	const char *err = NULL;
+	console_ctrl_t *console = console_init(stdin, stdout);
+	if (console == NULL) {
+		return "Failed to init connection with console.";
+	}
+	
+	struct timeval start;
+	if (gettimeofday(&start, NULL) != 0) {
+		err = "Failed getting the time";
+		goto leave;
+	}
+	
+	TPRINTF("Intensive computation shall be imagined (for %ds)...\n", DURATION_SECS);
+	TPRINTF("Press a key to terminate prematurely...\n");
+	while (true) {
+		struct timeval now;
+		if (gettimeofday(&now, NULL) != 0) {
+			err = "Failed getting the time";
+			goto leave;
+		}
+		
+		if (tv_sub(&now, &start) >= DURATION_SECS * 1000000L)
+			break;
+		
+		kbd_event_t ev;
+		suseconds_t timeout = 0;
+		bool has_event = console_get_kbd_event_timeout(console, &ev, &timeout);
+		if (has_event && (ev.type == KEY_PRESS)) {
+			TPRINTF("Key %d pressed, terminating.\n", ev.key);
+			break;
+		}
+	}
+
+	// FIXME - unless a key was pressed, the answer leaked as no one
+	// will wait for it.
+	// We cannot use async_forget() directly, though. Something like
+	// console_forget_pending_kbd_event() shall come here.
+
+	TPRINTF("Terminating...\n");
+
+leave:
+	console_done(console);
+
+	return err;
+}
Index: uspace/app/tester/ipc/starve.def
===================================================================
--- uspace/app/tester/ipc/starve.def	(revision f76696fc11f7c97060cb5bfd3e184a0f78e483f9)
+++ uspace/app/tester/ipc/starve.def	(revision f76696fc11f7c97060cb5bfd3e184a0f78e483f9)
@@ -0,0 +1,6 @@
+{
+	"starve",
+	"Demonstrate starving IPC",
+	&test_starve_ipc,
+	true
+},
Index: uspace/app/tester/tester.c
===================================================================
--- uspace/app/tester/tester.c	(revision b8d6783a99ca20a9a760d0859f69a1b5a920212a)
+++ uspace/app/tester/tester.c	(revision f76696fc11f7c97060cb5bfd3e184a0f78e483f9)
@@ -60,4 +60,5 @@
 #include "vfs/vfs1.def"
 #include "ipc/ping_pong.def"
+#include "ipc/starve.def"
 #include "loop/loop1.def"
 #include "mm/malloc1.def"
Index: uspace/app/tester/tester.h
===================================================================
--- uspace/app/tester/tester.h	(revision b8d6783a99ca20a9a760d0859f69a1b5a920212a)
+++ uspace/app/tester/tester.h	(revision f76696fc11f7c97060cb5bfd3e184a0f78e483f9)
@@ -93,4 +93,5 @@
 extern const char *test_vfs1(void);
 extern const char *test_ping_pong(void);
+extern const char *test_starve_ipc(void);
 extern const char *test_loop1(void);
 extern const char *test_malloc1(void);
Index: uspace/lib/c/generic/async.c
===================================================================
--- uspace/lib/c/generic/async.c	(revision b8d6783a99ca20a9a760d0859f69a1b5a920212a)
+++ uspace/lib/c/generic/async.c	(revision f76696fc11f7c97060cb5bfd3e184a0f78e483f9)
@@ -249,5 +249,5 @@
 static void to_event_initialize(to_event_t *to)
 {
-	struct timeval tv = { 0 };
+	struct timeval tv = { 0, 0 };
 
 	to->inlist = false;
@@ -1017,4 +1017,5 @@
 		
 		suseconds_t timeout;
+		unsigned int flags = SYNCH_FLAGS_NONE;
 		if (!list_empty(&timeout_list)) {
 			awaiter_t *waiter = list_get_instance(
@@ -1027,17 +1028,30 @@
 				futex_up(&async_futex);
 				handle_expired_timeouts();
-				continue;
-			} else
+				/*
+				 * Notice that even if the event(s) already
+				 * expired (and thus the other fibril was
+				 * supposed to be running already),
+				 * we check for incoming IPC.
+				 *
+				 * Otherwise, a fibril that continuously
+				 * creates (almost) expired events could
+				 * prevent IPC retrieval from the kernel.
+				 */
+				timeout = 0;
+				flags = SYNCH_FLAGS_NON_BLOCKING;
+
+			} else {
 				timeout = tv_sub(&waiter->to_event.expires, &tv);
-		} else
+				futex_up(&async_futex);
+			}
+		} else {
+			futex_up(&async_futex);
 			timeout = SYNCH_NO_TIMEOUT;
-		
-		futex_up(&async_futex);
+		}
 		
 		atomic_inc(&threads_in_ipc_wait);
 		
 		ipc_call_t call;
-		ipc_callid_t callid = ipc_wait_cycle(&call, timeout,
-		    SYNCH_FLAGS_NONE);
+		ipc_callid_t callid = ipc_wait_cycle(&call, timeout, flags);
 		
 		atomic_dec(&threads_in_ipc_wait);
@@ -1298,9 +1312,5 @@
 	
 	amsg_t *msg = (amsg_t *) amsgid;
-	
-	/* TODO: Let it go through the event read at least once */
-	if (timeout < 0)
-		return ETIMEOUT;
-	
+
 	futex_down(&async_futex);
 
@@ -1313,7 +1323,31 @@
 	}
 	
+	/*
+	 * Negative timeout is converted to zero timeout to avoid
+	 * using tv_add with negative augmenter.
+	 */
+	if (timeout < 0)
+		timeout = 0;
+
 	gettimeofday(&msg->wdata.to_event.expires, NULL);
 	tv_add(&msg->wdata.to_event.expires, timeout);
 	
+	/*
+	 * Current fibril is inserted as waiting regardless of the
+	 * "size" of the timeout.
+	 *
+	 * Checking for msg->done and immediately bailing out when
+	 * timeout == 0 would mean that the manager fibril would never
+	 * run (consider single threaded program).
+	 * Thus the IPC answer would be never retrieved from the kernel.
+	 *
+	 * Notice that the actual delay would be very small because we
+	 * - switch to manager fibril
+	 * - the manager sees expired timeout
+	 * - and thus adds us back to ready queue
+	 * - manager switches back to some ready fibril
+	 *   (prior it, it checks for incoming IPC).
+	 *
+	 */
 	msg->wdata.fid = fibril_get_id();
 	msg->wdata.active = false;
