Index: uspace/app/trace/trace.c
===================================================================
--- uspace/app/trace/trace.c	(revision fe86d9dd86bd9efd701da76a506f6b00631d3b7c)
+++ uspace/app/trace/trace.c	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -887,5 +887,5 @@
 		printf("Waiting for task to exit.\n");
 
-		rc = task_wait_task_id(task_id, &texit, &retval);
+		rc = task_wait_task_id(task_id, TASK_WAIT_EXIT, &texit, &retval);
 		if (rc != EOK) {
 			printf("Failed waiting for task.\n");
Index: uspace/lib/c/generic/async/client.c
===================================================================
--- uspace/lib/c/generic/async/client.c	(revision fe86d9dd86bd9efd701da76a506f6b00631d3b7c)
+++ uspace/lib/c/generic/async/client.c	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -174,4 +174,5 @@
 
 	if (session != NULL) {
+		// TODO extract common part with async_connect_me_to
 		session_ns->iface = 0;
 		session->mgmt = EXCHANGE_ATOMIC;
@@ -195,15 +196,16 @@
 
 /** Initialize the async framework.
- *
- */
-void __async_client_init(async_sess_t *session)
+ * @param arg_session_primary Primary session (to naming service).
+ *
+ */
+void __async_client_init(async_sess_t *arg_session_primary)
 {
 	if (fibril_rmutex_initialize(&message_mutex) != EOK)
 		abort();
 
-	if (session == NULL) {
+	if (arg_session_primary == NULL) {
 		session_primary = create_session_primary();
 	} else {
-		session_primary = session;
+		session_primary = arg_session_primary;
 	}
 
Index: uspace/lib/c/generic/libc.c
===================================================================
--- uspace/lib/c/generic/libc.c	(revision fe86d9dd86bd9efd701da76a506f6b00631d3b7c)
+++ uspace/lib/c/generic/libc.c	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -48,7 +48,10 @@
 #include "private/libc.h"
 #include "private/async.h"
-#include "private/malloc.h"
 #include "private/io.h"
 #include "private/fibril.h"
+#include "private/malloc.h"
+#include "private/ns.h" // TODO maybe better filename for session_primary
+#include "private/task.h"
+
 
 #ifdef CONFIG_RTLD
@@ -108,6 +111,8 @@
 	if (__pcb == NULL) {
 		__async_client_init(NULL);
+		__task_init(NULL);
 	} else {
 		__async_client_init(__pcb->session_primary);
+		__task_init(__pcb->session_taskman);
 	}
 	__async_ports_init();
Index: uspace/lib/c/generic/private/task.h
===================================================================
--- uspace/lib/c/generic/private/task.h	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
+++ uspace/lib/c/generic/private/task.h	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015 Michal Koutny
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_PRIVATE_TASK_H_
+#define LIBC_PRIVATE_TASK_H_
+
+#include <async.h>
+
+void __task_init(async_sess_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/generic/task.c
===================================================================
--- uspace/lib/c/generic/task.c	(revision fe86d9dd86bd9efd701da76a506f6b00631d3b7c)
+++ uspace/lib/c/generic/task.c	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -35,10 +35,4 @@
  */
 
-#include <task.h>
-#include <loader/loader.h>
-#include <stdarg.h>
-#include <str.h>
-#include <ipc/ns.h>
-#include <macros.h>
 #include <assert.h>
 #include <async.h>
@@ -46,7 +40,17 @@
 #include <ns.h>
 #include <stdlib.h>
+#include <ipc/taskman.h>
 #include <libc.h>
+#include <loader/loader.h>
+#include <macros.h>
+#include <malloc.h>
+#include <stdarg.h>
+#include <str.h>
+#include <task.h>
+#include <vfs/vfs.h>
 #include "private/ns.h"
-#include <vfs/vfs.h>
+#include "private/task.h"
+
+static async_sess_t *session_taskman = NULL;
 
 task_id_t task_get_id(void)
@@ -64,4 +68,28 @@
 }
 
+static async_exch_t *taskman_exchange_begin(void)
+{
+	/* Lazy connection */
+	if (session_taskman == NULL) {
+		// TODO unify exchange mgmt with taskman_handshake/__init
+		session_taskman = service_connect_blocking(EXCHANGE_SERIALIZE,
+		    SERVICE_TASKMAN,
+		    TASKMAN_CONTROL,
+		    0);
+	}
+
+	if (session_taskman == NULL) {
+		return NULL;
+	}
+
+	async_exch_t *exch = async_exchange_begin(session_taskman);
+	return exch;
+}
+
+static void taskman_exchange_end(async_exch_t *exch)
+{
+	async_exchange_end(exch);
+}
+
 /** Set the task name.
  *
@@ -88,4 +116,28 @@
 {
 	return (errno_t) __SYSCALL1(SYS_TASK_KILL, (sysarg_t) &task_id);
+}
+
+/** Setup waiting for a task.
+ *
+ * If the task finishes after this call succeeds, it is guaranteed that
+ * task_wait(wait, &texit, &retval) will return correct return value for
+ * the task.
+ *
+ * @param id   ID of the task to setup waiting for.
+ * @param wait Information necessary for the later task_wait call is stored here.
+ *
+ * @return EOK on success, else error code.
+ */
+static errno_t task_setup_wait(task_id_t id, task_wait_t *wait)
+{
+	async_exch_t *exch = taskman_exchange_begin();
+	if (exch == NULL)
+			return EIO;
+
+	wait->aid = async_send_3(exch, TASKMAN_WAIT, LOWER32(id), UPPER32(id),
+	    wait->flags, &wait->result);
+	taskman_exchange_end(exch);
+
+	return EOK;
 }
 
@@ -312,30 +364,4 @@
 }
 
-/** Setup waiting for a task.
- *
- * If the task finishes after this call succeeds, it is guaranteed that
- * task_wait(wait, &texit, &retval) will return correct return value for
- * the task.
- *
- * @param id   ID of the task to setup waiting for.
- * @param wait Information necessary for the later task_wait call is stored here.
- *
- * @return EOK on success, else error code.
- */
-errno_t task_setup_wait(task_id_t id, task_wait_t *wait)
-{
-	async_sess_t *sess_ns = get_session_primary();
-	if (sess_ns == NULL)
-		return EIO;
-
-	async_exch_t *exch = async_exchange_begin(sess_ns);
-
-	wait->aid = async_send_2(exch, NS_TASK_WAIT, LOWER32(id), UPPER32(id),
-	    &wait->result);
-	async_exchange_end(exch);
-
-	return EOK;
-}
-
 /** Cancel waiting for a task.
  *
@@ -391,4 +417,5 @@
  *
  * @param id ID of the task to wait for.
+ * @param flags  Specify for which task output we wait
  * @param texit  Store type of task exit here.
  * @param retval Store return value of the task here.
@@ -396,8 +423,10 @@
  * @return EOK on success, else error code.
  */
-errno_t task_wait_task_id(task_id_t id, task_exit_t *texit, int *retval)
+errno_t task_wait_task_id(task_id_t id, int flags, task_exit_t *texit, int *retval)
 {
 	task_wait_t wait;
+	wait.flags = flags;
 	errno_t rc = task_setup_wait(id, &wait);
+
 	if (rc != EOK)
 		return rc;
@@ -408,15 +437,21 @@
 errno_t task_retval(int val)
 {
-	async_sess_t *sess_ns = get_session_primary();
-	if (sess_ns == NULL)
+	async_exch_t *exch = taskman_exchange_begin();
+	if (exch == NULL)
 		return EIO;
 
-	async_exch_t *exch = async_exchange_begin(sess_ns);
-	errno_t rc = (errno_t) async_req_1_0(exch, NS_RETVAL, val);
-	async_exchange_end(exch);
-
+	int rc = (int) async_req_1_0(exch, TASKMAN_RETVAL, val);
+	taskman_exchange_end(exch);
+	
 	return rc;
 }
 
+
+void __task_init(async_sess_t *sess)
+{
+	assert(session_taskman == NULL);
+	session_taskman = sess;
+}
+
 /** @}
  */
Index: uspace/lib/c/include/ipc/ns.h
===================================================================
--- uspace/lib/c/include/ipc/ns.h	(revision fe86d9dd86bd9efd701da76a506f6b00631d3b7c)
+++ uspace/lib/c/include/ipc/ns.h	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -42,7 +42,5 @@
 	NS_REGISTER,
 	NS_REGISTER_BROKER,
-	NS_TASK_WAIT,
-	NS_ID_INTRO,
-	NS_RETVAL
+	NS_ID_INTRO
 } ns_request_t;
 
Index: uspace/lib/c/include/ipc/taskman.h
===================================================================
--- uspace/lib/c/include/ipc/taskman.h	(revision fe86d9dd86bd9efd701da76a506f6b00631d3b7c)
+++ uspace/lib/c/include/ipc/taskman.h	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -40,5 +40,6 @@
 
 typedef enum {
-	TASKMAN_HELLO = IPC_FIRST_USER_METHOD,
+	TASKMAN_WAIT = IPC_FIRST_USER_METHOD,
+	TASKMAN_RETVAL
 } taskman_request_t;
 
Index: uspace/lib/c/include/loader/pcb.h
===================================================================
--- uspace/lib/c/include/loader/pcb.h	(revision fe86d9dd86bd9efd701da76a506f6b00631d3b7c)
+++ uspace/lib/c/include/loader/pcb.h	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -64,4 +64,7 @@
 	async_sess_t *session_primary;
 	
+	/** Session to taskman (typically spawn parent) */
+	async_sess_t *session_taskman;
+	
 	/** Current working directory. */
 	char *cwd;
Index: uspace/lib/c/include/task.h
===================================================================
--- uspace/lib/c/include/task.h	(revision fe86d9dd86bd9efd701da76a506f6b00631d3b7c)
+++ uspace/lib/c/include/task.h	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -42,11 +42,13 @@
 #include <types/task.h>
 
+#define TASK_WAIT_EXIT   0x1
+#define TASK_WAIT_RETVAL 0x2
+
 typedef struct {
+	int flags;
 	ipc_call_t result;
 	aid_t aid;
 } task_wait_t;
 
-struct _TASK;
-typedef struct _TASK task_t;
 
 extern task_id_t task_get_id(void);
@@ -63,9 +65,21 @@
     __attribute__((sentinel));
 
-extern errno_t task_setup_wait(task_id_t, task_wait_t *);
+// if there is possibility for further wait, modify task_wait
+extern errno_t task_wait(task_wait_t *, task_exit_t *, int *);
+extern errno_t task_wait_task_id(task_id_t, int, task_exit_t *, int *);
+// similar to listen and socket duplication
+extern errno_t task_wait_any(task_wait_t *, task_id_t *, task_exit_t *, int *,
+    task_wait_t *);
+
+//extern int task_wait_any(int, task_exit_t *, int *);
+// alternative
+// task_wait_t is output param, actual result is obtained via task_wait call
+//extern int task_wait_any(task_wait_t *, int);
+
 extern void task_cancel_wait(task_wait_t *);
-extern errno_t task_wait(task_wait_t *, task_exit_t *, int *);
-extern errno_t task_wait_task_id(task_id_t, task_exit_t *, int *);
+
 extern errno_t task_retval(int);
+//TODO
+//extern int task_exit(int);
 
 #endif
Index: uspace/lib/posix/src/sys/wait.c
===================================================================
--- uspace/lib/posix/src/sys/wait.c	(revision fe86d9dd86bd9efd701da76a506f6b00631d3b7c)
+++ uspace/lib/posix/src/sys/wait.c	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -101,8 +101,10 @@
 	assert(options == 0 /* None of the options are supported. */);
 
+	int flags = TASK_WAIT_RETVAL | TASK_WAIT_EXIT;
 	task_exit_t texit;
 	int retval;
 
-	if (failed(task_wait_task_id((task_id_t) pid, &texit, &retval))) {
+	// TODO repeat wait for both retval and exit
+	if (failed(task_wait_task_id((task_id_t) pid, flags, &texit, &retval))) {
 		/* Unable to retrieve status. */
 		return (pid_t) -1;
Index: uspace/srv/loader/main.c
===================================================================
--- uspace/srv/loader/main.c	(revision fe86d9dd86bd9efd701da76a506f6b00631d3b7c)
+++ uspace/srv/loader/main.c	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -84,4 +84,7 @@
 static async_sess_t *session_primary = NULL;
 
+/** Session to taskman (typically our spawner) */
+static async_sess_t *session_taskman = NULL;
+
 /** Current working directory */
 static char *cwd = NULL;
@@ -335,4 +338,5 @@
 
 	pcb.session_primary = session_primary;
+	pcb.session_taskman = session_taskman;
 
 	pcb.cwd = cwd;
@@ -452,6 +456,17 @@
 }
 
+/** Handshake with taskman
+ *
+ * Taskman is our spawn parent, i.e. PHONE_INITIAL is connected to it.
+ * Goal of the handshake is to obtain phone to naming service and also keep the
+ * session to taskman.
+ *
+ * @return EOK on success, for errors see taskman_handshake()
+ */
 static errno_t ldr_taskman_handshake(void)
 {
+	assert(session_primary == NULL);
+	assert(session_taskman == NULL);
+
 	errno_t retval = EOK;
 
@@ -463,6 +478,5 @@
 	}
 
-	async_sess_t *session_tm = async_session_primary_swap(session_primary);
-	(void)async_hangup(session_tm);
+	session_taskman = async_session_primary_swap(session_primary);
 
 	handshake_complete = true;
Index: uspace/srv/ns/ns.c
===================================================================
--- uspace/srv/ns/ns.c	(revision fe86d9dd86bd9efd701da76a506f6b00631d3b7c)
+++ uspace/srv/ns/ns.c	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -43,5 +43,4 @@
 #include <stdio.h>
 #include <errno.h>
-#include <macros.h>
 #include "ns.h"
 #include "service.h"
@@ -96,16 +95,6 @@
 			retval = EOK;
 			break;
-		case NS_TASK_WAIT:
-			id = (task_id_t)
-			    MERGE_LOUP32(ipc_get_arg1(&call), ipc_get_arg2(&call));
-			wait_for_task(id, &call);
-			continue;
 		case NS_ID_INTRO:
 			retval = ns_task_id_intro(&call);
-			break;
-		case NS_RETVAL:
-			// TODO move to taskman
-			retval = EOK;
-			//retval = ns_task_retval(&call);
 			break;
 		default:
Index: uspace/srv/taskman/Makefile
===================================================================
--- uspace/srv/taskman/Makefile	(revision fe86d9dd86bd9efd701da76a506f6b00631d3b7c)
+++ uspace/srv/taskman/Makefile	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -32,5 +32,6 @@
 
 SOURCES = \
-	main.c
+	main.c \
+	task.c
 
 include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/taskman/main.c
===================================================================
--- uspace/srv/taskman/main.c	(revision fe86d9dd86bd9efd701da76a506f6b00631d3b7c)
+++ uspace/srv/taskman/main.c	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -34,9 +34,11 @@
 #include <ipc/taskman.h>
 #include <loader/loader.h>
+#include <macros.h>
 #include <ns.h>
 #include <stdio.h>
 #include <stdlib.h>
 
-#define NAME "taskman"
+#include "task.h"
+#include "taskman.h"
 
 //TODO move to appropriate header file
@@ -56,4 +58,5 @@
 static void connect_to_loader(ipc_callid_t iid, ipc_call_t *icall)
 {
+	//TODO explain why we don't explicitly accept connection request
 	/* Spawn a loader. */
 	int rc = loader_spawn("loader");
@@ -99,4 +102,53 @@
 }
 
+static void taskman_ctl_wait(ipc_callid_t iid, ipc_call_t *icall)
+{
+	task_id_t id = (task_id_t)
+	    MERGE_LOUP32(IPC_GET_ARG1(*icall), IPC_GET_ARG2(*icall));
+	int flags = IPC_GET_ARG3(*icall);
+
+	wait_for_task(id, flags, iid, icall);
+}
+
+static void taskman_ctl_retval(ipc_callid_t iid, ipc_call_t *icall)
+{
+	printf("%s:%i\n", __func__, __LINE__);
+	int rc = task_set_retval(icall);
+	async_answer_0(iid, rc);
+}
+
+static void control_connection_loop(void)
+{
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+
+		if (!IPC_GET_IMETHOD(call)) {
+			/* Client disconnected */
+			break;
+		}
+
+		switch (IPC_GET_IMETHOD(call)) {
+		case TASKMAN_WAIT:
+			taskman_ctl_wait(callid, &call);
+			break;
+		case TASKMAN_RETVAL:
+			taskman_ctl_retval(callid, &call);
+			break;
+		default:
+			async_answer_0(callid, ENOENT);
+		}
+	}
+}
+
+static void control_connection(ipc_callid_t iid, ipc_call_t *icall)
+{
+	/* First, accept connection */
+	async_answer_0(iid, EOK);
+
+	// TODO register task to hash table
+	control_connection_loop();
+}
+
 static void loader_callback(ipc_callid_t iid, ipc_call_t *icall)
 {
@@ -134,4 +186,10 @@
 		loader_to_ns(iid, icall);
 		break;
+	case TASKMAN_CONTROL:
+		control_connection(iid, icall);
+		// ---- interrupt here ----
+		//   implement control connection body (setup wait)
+		// ------------------------
+		break;
 	default:
 		/* Unknown interface */
@@ -146,4 +204,6 @@
 	case TASKMAN_LOADER_CALLBACK:
 		loader_callback(iid, icall);
+		// TODO register task to hashtable
+		control_connection_loop();
 		break;
 	default:
@@ -161,7 +221,11 @@
 
 	prodcons_initialize(&sess_queue);
+	int rc = task_init();
+	if (rc != EOK) {
+		return rc;
+	}
 
 	/* We're service too */
-	int rc = service_register(SERVICE_TASKMAN);
+	rc = service_register(SERVICE_TASKMAN);
 	if (rc != EOK) {
 		printf("Cannot register at naming service (%i).", rc);
Index: uspace/srv/taskman/task.c
===================================================================
--- uspace/srv/taskman/task.c	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
+++ uspace/srv/taskman/task.c	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2009 Martin Decky
+ * Copyright (c) 2009 Jiri Svoboda
+ * Copyright (c) 2015 Michal Koutny
+ * 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 taskman
+ * @{
+ */
+
+#include <adt/hash_table.h>
+#include <assert.h>
+#include <async.h>
+#include <errno.h>
+#include <macros.h>
+#include <malloc.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <types/task.h>
+
+#include "task.h"
+#include "taskman.h"
+
+
+/** Task hash table item. */
+typedef struct {
+	ht_link_t link;
+	
+	task_id_t id;    /**< Task ID. */
+	bool finished;   /**< Task is done. */
+	bool have_rval;  /**< Task returned a value. */
+	int retval;      /**< The return value. */
+} hashed_task_t;
+
+
+static size_t task_key_hash(void *key)
+{
+	return *(task_id_t*)key;
+}
+
+static size_t task_hash(const ht_link_t  *item)
+{
+	hashed_task_t *ht = hash_table_get_inst(item, hashed_task_t, link);
+	return ht->id;
+}
+
+static bool task_key_equal(void *key, const ht_link_t *item)
+{
+	hashed_task_t *ht = hash_table_get_inst(item, hashed_task_t, link);
+	return ht->id == *(task_id_t*)key;
+}
+
+/** Perform actions after removal of item from the hash table. */
+static void task_remove(ht_link_t *item)
+{
+	free(hash_table_get_inst(item, hashed_task_t, link));
+}
+
+/** Operations for task hash table. */
+static hash_table_ops_t task_hash_table_ops = {
+	.hash = task_hash,
+	.key_hash = task_key_hash,
+	.key_equal = task_key_equal,
+	.equal = NULL,
+	.remove_callback = task_remove
+};
+
+/** Task hash table structure. */
+static hash_table_t task_hash_table;
+
+typedef struct {
+	ht_link_t link;
+	sysarg_t in_phone_hash;  /**< Incoming phone hash. */
+	task_id_t id;            /**< Task ID. */
+} p2i_entry_t;
+
+/* phone-to-id hash table operations */
+
+static size_t p2i_key_hash(void *key)
+{
+	sysarg_t in_phone_hash = *(sysarg_t*)key;
+	return in_phone_hash;
+}
+
+static size_t p2i_hash(const ht_link_t *item)
+{
+	p2i_entry_t *entry = hash_table_get_inst(item, p2i_entry_t, link);
+	return entry->in_phone_hash;
+}
+
+static bool p2i_key_equal(void *key, const ht_link_t *item)
+{
+	sysarg_t in_phone_hash = *(sysarg_t*)key;
+	p2i_entry_t *entry = hash_table_get_inst(item, p2i_entry_t, link);
+	
+	return (in_phone_hash == entry->in_phone_hash);
+}
+
+/** Perform actions after removal of item from the hash table.
+ *
+ * @param item Item that was removed from the hash table.
+ *
+ */
+static void p2i_remove(ht_link_t *item)
+{
+	assert(item);
+	free(hash_table_get_inst(item, p2i_entry_t, link));
+}
+
+/** Operations for task hash table. */
+static hash_table_ops_t p2i_ops = {
+	.hash = p2i_hash,
+	.key_hash = p2i_key_hash,
+	.key_equal = p2i_key_equal,
+	.equal = NULL,
+	.remove_callback = p2i_remove
+};
+
+/** Map phone hash to task ID */
+static hash_table_t phone_to_id;
+
+/** Pending task wait structure. */
+typedef struct {
+	link_t link;
+	task_id_t id;         /**< Task ID. */
+	ipc_callid_t callid;  /**< Call ID waiting for the connection */
+	int flags;            /**< Wait flags TODO */
+} pending_wait_t;
+
+static list_t pending_wait;
+
+int task_init(void)
+{
+	if (!hash_table_create(&task_hash_table, 0, 0, &task_hash_table_ops)) {
+		printf(NAME ": No memory available for tasks\n");
+		return ENOMEM;
+	}
+	
+	if (!hash_table_create(&phone_to_id, 0, 0, &p2i_ops)) {
+		printf(NAME ": No memory available for tasks\n");
+		return ENOMEM;
+	}
+	
+	list_initialize(&pending_wait);
+	return EOK;
+}
+
+/** Process pending wait requests */
+void process_pending_wait(void)
+{
+	task_exit_t texit;
+	
+loop:
+	// W lock
+	list_foreach(pending_wait, link, pending_wait_t, pr) {
+		// R lock task_hash_table
+		ht_link_t *link = hash_table_find(&task_hash_table, &pr->id);
+		// R unlock task_hash_table
+		if (!link)
+			continue;
+		
+		hashed_task_t *ht = hash_table_get_inst(link, hashed_task_t, link);
+		if (!ht->finished)
+			continue;
+		
+		if (!(pr->callid & IPC_CALLID_NOTIFICATION)) {
+			texit = ht->have_rval ? TASK_EXIT_NORMAL :
+			    TASK_EXIT_UNEXPECTED;
+			async_answer_2(pr->callid, EOK, texit,
+			    ht->retval);
+		}
+		
+		list_remove(&pr->link);
+		free(pr);
+		goto loop;
+	}
+	// W unlock
+}
+
+void wait_for_task(task_id_t id, int flags, ipc_callid_t callid, ipc_call_t *call)
+{
+	// R lock
+	ht_link_t *link = hash_table_find(&task_hash_table, &id);
+	// R unlock
+	hashed_task_t *ht = (link != NULL) ?
+	    hash_table_get_inst(link, hashed_task_t, link) : NULL;
+	
+	if (ht == NULL) {
+		/* No such task exists. */
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	if (ht->finished) {
+		task_exit_t texit = ht->have_rval ? TASK_EXIT_NORMAL :
+		    TASK_EXIT_UNEXPECTED;
+		async_answer_2(callid, EOK, texit, ht->retval);
+		return;
+	}
+	
+	/* Add to pending list */
+	pending_wait_t *pr =
+	    (pending_wait_t *) malloc(sizeof(pending_wait_t));
+	if (!pr) {
+		// TODO why IPC_CALLID_NOTIFICATION? explain!
+		if (!(callid & IPC_CALLID_NOTIFICATION))
+			async_answer_0(callid, ENOMEM);
+		return;
+	}
+	
+	link_initialize(&pr->link);
+	pr->id = id;
+	pr->flags = flags;
+	pr->callid = callid;
+	// W lock
+	list_append(&pr->link, &pending_wait);
+	// W unlock
+}
+
+int ns_task_id_intro(ipc_call_t *call)
+{
+	
+	task_id_t id = MERGE_LOUP32(IPC_GET_ARG1(*call), IPC_GET_ARG2(*call));
+
+	ht_link_t *link = hash_table_find(&phone_to_id, &call->in_phone_hash);
+	if (link != NULL)
+		return EEXISTS;
+	
+	p2i_entry_t *entry = (p2i_entry_t *) malloc(sizeof(p2i_entry_t));
+	if (entry == NULL)
+		return ENOMEM;
+	
+	hashed_task_t *ht = (hashed_task_t *) malloc(sizeof(hashed_task_t));
+	if (ht == NULL)
+		return ENOMEM;
+	
+	/*
+	 * Insert into the phone-to-id map.
+	 */
+	
+	entry->in_phone_hash = call->in_phone_hash;
+	entry->id = id;
+	hash_table_insert(&phone_to_id, &entry->link);
+	
+	/*
+	 * Insert into the main table.
+	 */
+	
+	ht->id = id;
+	ht->finished = false;
+	ht->have_rval = false;
+	ht->retval = -1;
+	hash_table_insert(&task_hash_table, &ht->link);
+	
+	return EOK;
+}
+
+static int get_id_by_phone(sysarg_t phone_hash, task_id_t *id)
+{
+	ht_link_t *link = hash_table_find(&phone_to_id, &phone_hash);
+	if (link == NULL)
+		return ENOENT;
+	
+	p2i_entry_t *entry = hash_table_get_inst(link, p2i_entry_t, link);
+	*id = entry->id;
+	
+	return EOK;
+}
+
+int task_set_retval(ipc_call_t *call)
+{
+	task_id_t id = call->in_task_id;
+	
+	// R lock
+	ht_link_t *link = hash_table_find(&task_hash_table, &id);
+	// R unlock
+	hashed_task_t *ht = (link != NULL) ?
+	    hash_table_get_inst(link, hashed_task_t, link) : NULL;
+	
+	if ((ht == NULL) || (ht->finished))
+		return EOK; // TODO EINVAL when registration works;
+	
+	ht->finished = true;
+	ht->have_rval = true;
+	ht->retval = IPC_GET_ARG1(*call);
+	
+	process_pending_wait();
+	
+	return EOK;
+}
+
+int ns_task_disconnect(ipc_call_t *call)
+{
+	task_id_t id;
+	int rc = get_id_by_phone(call->in_phone_hash, &id);
+	if (rc != EOK)
+		return rc;
+	
+	/* Delete from phone-to-id map. */
+	hash_table_remove(&phone_to_id, &call->in_phone_hash);
+	
+	/* Mark task as finished. */
+	ht_link_t *link = hash_table_find(&task_hash_table, &id);
+	if (link == NULL)
+		return EOK;
+
+	hashed_task_t *ht = hash_table_get_inst(link, hashed_task_t, link);
+	
+	ht->finished = true;
+	
+	process_pending_wait();
+	hash_table_remove(&task_hash_table, &id);
+	
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/taskman/task.h
===================================================================
--- uspace/srv/taskman/task.h	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
+++ uspace/srv/taskman/task.h	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009 Martin Decky
+ * Copyright (c) 2015 Michal Koutny
+ * 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 taskman
+ * @{
+ */
+
+#ifndef TASKMAN_TASK_H__
+#define TASKMAN_TASK_H__
+
+#include <ipc/common.h>
+#include <abi/proc/task.h>
+
+extern int task_init(void);
+extern void process_pending_wait(void);
+
+extern void wait_for_task(task_id_t, int, ipc_callid_t, ipc_call_t *);
+extern int task_set_retval(ipc_call_t *);
+
+extern int ns_task_id_intro(ipc_call_t *);
+extern int ns_task_disconnect(ipc_call_t *);
+
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/taskman/taskman.h
===================================================================
--- uspace/srv/taskman/taskman.h	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
+++ uspace/srv/taskman/taskman.h	(revision 1be7bee8078be21f432895af4f51f96ea24ceda6)
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015 Michal Koutny
+ * 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 taskman
+ * @{
+ */
+
+#ifndef TASKMAN_TASKMAN_H__
+#define TASKMAN_TASKMAN_H__
+
+#define NAME  "taskman"
+
+#endif
+
+/**
+ * @}
+ */
