Index: uspace/srv/taskman/task.c
===================================================================
--- uspace/srv/taskman/task.c	(revision 62273d171a119afa521ee4f4dcb650b5fd54e131)
+++ uspace/srv/taskman/task.c	(revision 2f44fafd40f8f973f11708ad012462452388764f)
@@ -1,33 +1,38 @@
 /*
- * 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
+ * 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
+ * - 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
+ * - 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
+ * - 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.
+ * 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
+/**
+ * locking order:
+ * - task_hash_table_lock,
+ * - pending_wait_lock.
+ *
+ * @addtogroup taskman
  * @{
  */
@@ -37,8 +42,10 @@
 #include <async.h>
 #include <errno.h>
+#include <fibril_synch.h>
 #include <macros.h>
 #include <malloc.h>
 #include <stdbool.h>
 #include <stdio.h>
+#include <task.h>
 #include <types/task.h>
 
@@ -46,13 +53,19 @@
 #include "taskman.h"
 
-
-/** Task hash table item. */
+/** what type of retval from the task we have */
+typedef enum {
+	RVAL_UNSET,     /**< unset */
+	RVAL_SET,       /**< retval set, e.g. by server */
+	RVAL_SET_EXIT   /**< retval set, wait for expected task exit */
+} retval_t;
+
+/** task hash table item. */
 typedef struct {
 	ht_link_t link;
 	
-	task_id_t id;    /**< Task ID. */
-	task_exit_t exit;/**< Task is done. */
-	bool have_rval;  /**< Task returned a value. */
-	int retval;      /**< The return value. */
+	task_id_t id;        /**< task id. */
+	task_exit_t exit;    /**< task is done. */
+	retval_t retval_type;  /**< task returned a value. */
+	int retval;          /**< the return value. */
 } hashed_task_t;
 
@@ -75,5 +88,5 @@
 }
 
-/** Perform actions after removal of item from the hash table. */
+/** perform actions after removal of item from the hash table. */
 static void task_remove(ht_link_t *item)
 {
@@ -81,5 +94,5 @@
 }
 
-/** Operations for task hash table. */
+/** operations for task hash table. */
 static hash_table_ops_t task_hash_table_ops = {
 	.hash = task_hash,
@@ -92,55 +105,5 @@
 /** 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;
+static FIBRIL_RWLOCK_INITIALIZE(task_hash_table_lock);
 
 /** Pending task wait structure. */
@@ -149,8 +112,9 @@
 	task_id_t id;         /**< Task ID. */
 	ipc_callid_t callid;  /**< Call ID waiting for the connection */
-	int flags;            /**< Wait flags TODO */
+	int flags;            /**< Wait flags */
 } pending_wait_t;
 
 static list_t pending_wait;
+static FIBRIL_RWLOCK_INITIALIZE(pending_wait_lock);
 
 int task_init(void)
@@ -161,36 +125,52 @@
 	}
 	
-	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 */
+/** Process pending wait requests
+ *
+ * Assumes task_hash_table_lock is hold (at least read)
+ */
 void process_pending_wait(void)
 {
-	task_exit_t texit;
-	
+	fibril_rwlock_write_lock(&pending_wait_lock);
 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->exit == TASK_EXIT_RUNNING)
-			continue;
-		
-		if (!(pr->callid & IPC_CALLID_NOTIFICATION)) {
-			texit = ht->exit;
-			async_answer_2(pr->callid, EOK, texit,
-			    ht->retval);
+		int notify_flags = 0;
+		if (ht->exit != TASK_EXIT_RUNNING) {
+			notify_flags |= TASK_WAIT_EXIT;
+			if (ht->retval_type == RVAL_SET_EXIT) {
+				notify_flags |= TASK_WAIT_RETVAL;
+			}
 		}
+		if (ht->retval_type == RVAL_SET) {
+			notify_flags |= TASK_WAIT_RETVAL;
+		}
+
+		int match = notify_flags & pr->flags;
+		bool answer = !(pr->callid & IPC_CALLID_NOTIFICATION);
+
+		if (match == 0) {
+			if (notify_flags & TASK_WAIT_EXIT) {
+				/* Nothing to wait for anymore */
+				if (answer) {
+					async_answer_0(pr->callid, EINVAL);
+				}
+			} else {
+				/* Maybe later */
+				continue;
+			}
+		} else if (answer) {
+			/* Send both exit status and retval, caller should know
+			 * what is valid */
+			async_answer_2(pr->callid, EOK, ht->exit, ht->retval);
+		}
+
 		
 		list_remove(&pr->link);
@@ -198,12 +178,13 @@
 		goto loop;
 	}
-	// W unlock
+	fibril_rwlock_write_unlock(&pending_wait_lock);
 }
 
 void wait_for_task(task_id_t id, int flags, ipc_callid_t callid, ipc_call_t *call)
 {
-	// R lock
+	fibril_rwlock_read_lock(&task_hash_table_lock);
 	ht_link_t *link = hash_table_find(&task_hash_table, &id);
-	// R unlock
+	fibril_rwlock_read_unlock(&task_hash_table_lock);
+
 	hashed_task_t *ht = (link != NULL) ?
 	    hash_table_get_inst(link, hashed_task_t, link) : NULL;
@@ -235,21 +216,27 @@
 	pr->flags = flags;
 	pr->callid = callid;
-	// W lock
+
+	fibril_rwlock_write_lock(&pending_wait_lock);
 	list_append(&pr->link, &pending_wait);
-	// W unlock
-}
-
-int task_id_intro(ipc_call_t *call)
-{
-	// TODO think about task_id reuse and this
-	// R lock
+	fibril_rwlock_write_unlock(&pending_wait_lock);
+}
+
+int task_intro(ipc_call_t *call, bool check_unique)
+{
+	int rc = EOK;
+
+	fibril_rwlock_write_lock(&task_hash_table_lock);
+
 	ht_link_t *link = hash_table_find(&task_hash_table, &call->in_task_id);
-	// R unlock
-	if (link != NULL)
-		return EEXISTS;
+	if (link != NULL) {
+		rc = EEXISTS;
+		goto finish;
+	}
 	
 	hashed_task_t *ht = (hashed_task_t *) malloc(sizeof(hashed_task_t));
-	if (ht == NULL)
-		return ENOMEM;
+	if (ht == NULL) {
+		rc = ENOMEM;
+		goto finish;
+	}
 
 	/*
@@ -258,33 +245,38 @@
 	ht->id = call->in_task_id;
 	ht->exit = TASK_EXIT_RUNNING;
-	ht->have_rval = false;
+	ht->retval_type = RVAL_UNSET;
 	ht->retval = -1;
-	// W lock
+
 	hash_table_insert(&task_hash_table, &ht->link);
-	// W unlock
-	
-	return EOK;
+	
+finish:
+	fibril_rwlock_write_unlock(&task_hash_table_lock);
+	return rc;
 }
 
 int task_set_retval(ipc_call_t *call)
 {
+	int rc = EOK;
 	task_id_t id = call->in_task_id;
 	
-	// R lock
+	fibril_rwlock_read_lock(&task_hash_table_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->exit != TASK_EXIT_RUNNING))
-		return EINVAL;
-	
-	// TODO process additional flag to retval
-	ht->have_rval = true;
+	if ((ht == NULL) || (ht->exit != TASK_EXIT_RUNNING)) {
+		rc = EINVAL;
+		goto finish;
+	}
+	
 	ht->retval = IPC_GET_ARG1(*call);
+	ht->retval_type = IPC_GET_ARG2(*call) ? RVAL_SET_EXIT : RVAL_SET;
 	
 	process_pending_wait();
 	
-	return EOK;
+finish:
+	fibril_rwlock_read_unlock(&task_hash_table_lock);
+	return rc;
 }
 
@@ -292,18 +284,22 @@
 {
 	/* Mark task as finished. */
-	// R lock
+	fibril_rwlock_write_lock(&task_hash_table_lock);
 	ht_link_t *link = hash_table_find(&task_hash_table, &id);
-	// R unlock
-	if (link == NULL)
-		return;
+	if (link == NULL) {
+		goto finish;
+	}
 
 	hashed_task_t *ht = hash_table_get_inst(link, hashed_task_t, link);
 	
-	ht->exit = texit;
+	if (ht->retval_type == RVAL_UNSET) {
+		ht->exit = TASK_EXIT_UNEXPECTED;
+	} else {
+		ht->exit = texit;
+	}
 	process_pending_wait();
 
-	// W lock
 	hash_table_remove_item(&task_hash_table, &ht->link);
-	// W unlock
+finish:
+	fibril_rwlock_write_unlock(&task_hash_table_lock);
 }
 
