bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC] kern: simple futex for gnumach (version 6)


From: Marin Ramesa
Subject: [RFC] kern: simple futex for gnumach (version 6)
Date: Fri, 27 Dec 2013 16:28:33 +0100

Queue functions and macros are used to implement generic doubly-linked lists.
New structure futex_thread is defined since vm_map_lookup() is now per-thread.
Functions assert_wait(), thread_block(), thread_wakeup() and clear_wait() are
now used instead of thread_suspend() and thread_resume(). New file futex_i.h
is created to contain non-public parts of the futex.h header file. All functions
have been documented.

On Hurd tests fail with a segmentation fault in kalloc() (GDB says kalloc()
segfaults in vm_map_find_entry()). On Linux everything works except segfaults
in vm_map_lookup(), assert_wait(), thread_block() and kalloc() that are to be 
expected since gnumach is not running.

---
 Makefrag.am    |    3 +
 kern/futex.c   |  350 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 kern/futex.h   |   67 +++++++++++
 kern/futex_i.h |  188 ++++++++++++++++++++++++++++++
 4 files changed, 608 insertions(+)
 create mode 100644 kern/futex.c
 create mode 100644 kern/futex.h
 create mode 100644 kern/futex_i.h

diff --git a/Makefrag.am b/Makefrag.am
index c1387bd..a8f387c 100644
--- a/Makefrag.am
+++ b/Makefrag.am
@@ -145,6 +145,9 @@ libkernel_a_SOURCES += \
        kern/eventcount.h \
        kern/exception.c \
        kern/exception.h \
+       kern/futex.c \
+       kern/futex.h \
+       kern/futex_i.h \
        kern/host.c \
        kern/host.h \
        kern/ipc_host.c \
diff --git a/kern/futex.c b/kern/futex.c
new file mode 100644
index 0000000..6bea384
--- /dev/null
+++ b/kern/futex.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2013 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *     Author: Marin Ramesa
+ */
+
+/*
+ * Fast userspace locking
+ *
+ */
+
+#include <kern/futex.h>
+#include <kern/futex_i.h>
+#include <kern/sched_prim.h>
+#include <kern/kalloc.h>
+
+struct futex_hash_table {
+       
+       /* Futex hash table lock. */
+       decl_simple_lock_data( , futex_hash_table_lock);
+
+       /* Size of the hash table. */
+       size_t size;
+
+       /* Array of futexes. */
+       futex_t futex_elements;
+};
+
+typedef struct futex_hash_table *futex_hash_table_t;
+
+static futex_hash_table_t table = NULL; 
+static unsigned int max_hash_val = 0;
+
+int futex_init(futex_t futex, int *address)
+{
+       if (table == NULL) {
+               futex->chain.next = &futex->chain;
+               futex->chain.prev = &futex->chain;
+               if (futex_hash_table_init() == FUTEX_RESOURCE_SHORTAGE) {
+                       return FUTEX_RESOURCE_SHORTAGE;
+               }
+       }
+
+       if ((table->futex_elements = 
(futex_t)kalloc((max_hash_val+1)*sizeof(futex_t))) == NULL) {
+               return FUTEX_RESOURCE_SHORTAGE;
+       }
+
+       simple_lock_init(&futex->futex_wait_lock);
+
+       futex->address = address;
+       futex->num_futexed_threads = 0;
+       
+       queue_enter_first(&futex->chain, futex, futex_t, chain);
+       
+       return 0;       
+}
+
+int futex_wait(int *address, int value)
+{
+       futex_t futex;
+       futex_thread_t thread = NULL; 
+
+       lookup:
+
+       futex = futex_hash_table_lookup_address(address);
+
+       if (futex != NULL) {
+
+               simple_lock(&futex->futex_wait_lock);
+
+               /* If the value is still the same. */
+               if (*address == value) {
+
+                       /* Add the thread to the futex. */                      
+                       int ret;
+
+                       if ((ret = futex_thread_init(futex, thread)) != 0)
+                               return ret;
+
+                       goto suspend;
+               } else 
+                       goto no_suspend;
+
+       } else {
+               
+               int i = futex_hash_table_add_address(address); 
+
+               if (i == 1) return FUTEX_RESOURCE_SHORTAGE;
+               if (i != 0) return i;
+               
+               goto lookup;
+       }
+
+       suspend:
+               assert_wait(&thread->thread->state , FALSE);
+               simple_unlock(&futex->futex_wait_lock);
+               thread_block(NULL);
+               thread_wakeup(&thread->thread->state);
+               return FUTEX_RESUMED_BY_WAKE;
+
+       no_suspend:
+               simple_unlock(&futex->futex_wait_lock);
+               return FUTEX_NO_THREAD_SUSPENDED;
+}              
+
+int futex_wake(int *address, boolean_t wake_all)
+{
+       futex_t futex, temp_futex;
+       
+       temp_futex = futex_hash_table_lookup_address(address);
+       
+       if (temp_futex != NULL) 
+               futex_cross_address_space_wake(temp_futex, wake_all);
+       else
+               goto no_thread;
+       
+       futex = futex_hash_table_lookup_address(address);
+
+       if (futex != NULL) {
+
+               simple_lock(&futex->futex_wait_lock);
+
+               futex_wake_threads(futex, wake_all);
+       
+               if (futex->num_futexed_threads == 0)
+                       futex_hash_table_delete_futex(futex);
+
+               simple_unlock(&futex->futex_wait_lock);
+
+               return FUTEX_SOME_NUMBER_RESUMED;
+
+       } else {
+               no_thread:
+                       return FUTEX_NO_THREAD;
+       }
+}      
+
+void futex_wake_one_thread(futex_t futex, int thread_num)
+{
+       
clear_wait((&(futex->futexed_threads[futex->num_futexed_threads]))->thread, 
THREAD_AWAKENED, FALSE);
+       futex->num_futexed_threads--;
+       
kfree((vm_offset_t)&(futex->futexed_threads[futex->num_futexed_threads]), 
sizeof(futex_thread_t));
+}
+
+void futex_cross_address_space_wake(futex_t futex, boolean_t wake_all)
+{
+       #define min(x, y) (x <= y ? x : y)
+
+       queue_iterate(&futex->chain, futex, futex_t, chain) {
+
+               simple_lock(&futex->futex_wait_lock);
+
+               int i;
+
+               for (i = 0; i < min(futex->num_futexed_threads, 
+                       ((futex_t)futex->chain.next)->num_futexed_threads); 
i++) {
+
+                       if (*(futex->futexed_threads[i].offset) == 
+                               
*(((futex_t)futex->chain.next)->futexed_threads[i].offset)) {
+
+                               
simple_lock(&((futex_t)futex->chain.next)->futex_wait_lock);
+                       
+                               
futex_wake_one_thread((futex_t)futex->chain.next, i);
+                               
+                               if 
(((futex_t)futex->chain.next)->num_futexed_threads == 0) {
+                                       simple_unlock(&futex->futex_wait_lock);
+                                       
simple_unlock(&((futex_t)futex->chain.next)->futex_wait_lock);
+                                       
futex_hash_table_delete_futex((futex_t)futex->chain.next);
+                                       #undef min
+                                       return;                         
+                               }
+                       }
+               }
+       
+               simple_unlock(&((futex_t)futex->chain.next)->futex_wait_lock);
+               simple_unlock(&futex->futex_wait_lock);
+               
+       }
+
+#undef min
+}
+
+void futex_wake_threads(futex_t futex, boolean_t wake_all)
+{
+       if (wake_all) {
+               int i;
+               for (i = 0; i < futex->num_futexed_threads; i++) 
+                       futex_wake_one_thread(futex, i);
+       } else 
+               futex_wake_one_thread(futex, futex->num_futexed_threads-1);
+}
+
+int futex_hash_table_init(void)
+{
+       if ((table = (futex_hash_table_t)kalloc(sizeof(struct 
futex_hash_table))) == NULL) {
+               return FUTEX_RESOURCE_SHORTAGE;
+       }
+
+       simple_lock_init(&table->futex_hash_table_lock);
+
+       table->size = 0;
+
+       return 0;
+}
+
+unsigned int futex_hash(int *address)
+{
+       unsigned int hash_val = 0;
+
+       hash_val = (unsigned int)address + (hash_val << 5) - hash_val;
+
+       if (table != NULL) {
+               unsigned int ret = hash_val % table->size; 
+               if (ret > max_hash_val)
+                       max_hash_val = ret;             
+               return ret;
+       } else
+               return 0;
+}
+
+futex_t futex_hash_table_lookup_address(int *address)
+{
+       futex_t futex;
+
+       if (table == NULL) {
+               return NULL;
+       }
+
+       unsigned int hash_val = futex_hash(address);
+
+       if (table->size == 1)
+               hash_val = 0;
+
+       simple_lock(&table->futex_hash_table_lock);
+
+       for (futex = &(table->futex_elements[hash_val]); futex != NULL; futex = 
(futex_t)futex->chain.next) {
+
+               if (address == futex->address) {
+                       simple_unlock(&table->futex_hash_table_lock);
+                       return futex;
+
+               }
+       }
+
+       for (futex = &(table->futex_elements[hash_val]); futex != NULL; futex = 
(futex_t)futex->chain.prev) {
+
+               if (address == futex->address) {
+                       simple_unlock(&table->futex_hash_table_lock);
+                       return futex;
+
+               }
+       }
+
+       simple_unlock(&table->futex_hash_table_lock);
+
+       return NULL;
+}
+
+int futex_hash_table_add_address(int *address)
+{
+       futex_t new_futex;
+
+       unsigned int hash_val = futex_hash(address);
+
+       if (table != NULL) {
+               simple_lock(&table->futex_hash_table_lock);
+       }
+
+       if ((new_futex = (futex_t)kalloc(sizeof(struct futex))) == NULL) {
+               if (table != NULL)
+                       simple_unlock(&table->futex_hash_table_lock);
+               return 1;
+       }
+
+       int ret;
+
+       if ((ret = futex_init(new_futex, address)) != 0) {
+               if (table != NULL)
+                       simple_unlock(&table->futex_hash_table_lock);
+               return ret;
+       }
+
+       new_futex->hash_val = hash_val;
+       table->futex_elements[hash_val] = *new_futex;
+       table->size++;
+
+       if (table != NULL)
+               simple_unlock(&table->futex_hash_table_lock);
+
+       return 0;
+}      
+
+void futex_hash_table_delete_futex(futex_t futex)
+{
+       simple_lock(&table->futex_hash_table_lock);
+
+       queue_remove(&futex->chain, futex, futex_t, chain);
+
+       kfree((vm_offset_t)futex, sizeof(struct futex));
+       kfree((vm_offset_t)&(table->futex_elements[futex->hash_val]), 
sizeof(futex_t));
+
+       table->size--;
+
+       if (table->size == 0) {
+               simple_unlock(&table->futex_hash_table_lock);
+               kfree((vm_offset_t)table, sizeof(struct futex_hash_table));
+               table = NULL;
+       }
+
+       if (table != NULL)
+               simple_unlock(&table->futex_hash_table_lock);
+}
+
+int futex_thread_init(futex_t futex, futex_thread_t thread)
+{
+       if ((thread = (futex_thread_t)kalloc(sizeof(futex_thread_t))) == NULL)
+               return FUTEX_RESOURCE_SHORTAGE;
+       
+       thread->thread = current_thread();
+       thread->futex = futex;
+
+       *thread->map = current_map();
+       if ((vm_map_lookup(thread->map, (vm_offset_t)futex->address, 
VM_PROT_READ, thread->version, thread->object,
+                               thread->offset, thread->out_prot, 
thread->wired) != KERN_SUCCESS)) {
+               kfree((vm_offset_t)thread, sizeof(struct futex_thread));
+               return FUTEX_MEMORY_ERROR;
+       }
+
+       if ((futex->futexed_threads = 
(futex_thread_t)kalloc((futex->num_futexed_threads++)*sizeof(futex_thread_t))) 
== NULL) {
+               return FUTEX_RESOURCE_SHORTAGE;
+       }
+
+       futex->futexed_threads[futex->num_futexed_threads-1] = *thread;
+
+       return 0;
+}
diff --git a/kern/futex.h b/kern/futex.h
new file mode 100644
index 0000000..6dfeff8
--- /dev/null
+++ b/kern/futex.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *     Author: Marin Ramesa
+ */
+
+#ifndef _KERN_FUTEX_H_
+#define _KERN_FUTEX_H_
+
+#include <mach/boolean.h>
+
+/* Definitions for return values. */
+#define FUTEX_RESUMED_BY_WAKE 0
+#define FUTEX_NO_THREAD_SUSPENDED 1
+#define FUTEX_SOME_NUMBER_RESUMED 2
+#define FUTEX_NO_THREAD 3
+#define FUTEX_MEMORY_ERROR 4
+#define FUTEX_RESOURCE_SHORTAGE 6
+
+/*
+ * Function: futex_wait()
+ *
+ * This is a method for a thread to wait for a change of value at a given 
address.
+ * 
+ * Takes address and value arguments. Returns:
+ *     FUTEX_RESUMED_BY_WAKE - thread has been resumed by futex_wake()
+ *     FUTEX_NO_THREAD_SUSPENDED - value at the address has been changed while 
inside 
+ *                                 futex_wait() and no thread has been 
suspended
+ *     FUTEX_RESOURCE_SHORTAGE - allocation of the new futex, hash table or 
array of futex 
+ *                               pointers has failed
+ *
+ */
+extern int futex_wait(int *address, int value);
+
+
+/*
+ * Function: futex_wake()
+ *
+ * This is a method for waking up one or all threads waiting for a change of 
+ * value at a given address.
+ * 
+ * Takes address and wake_all arguments. If wake_all is TRUE, all threads are
+ * awakened. If it is FALSE, only one thread is awakened. 
+ *
+ * Returns:
+ *     FUTEX_SOME_NUMBER_RESUMED - threads have been resumed by futex_wake()
+ *     FUTEX_NO_THREAD - futex_wake() did not found a suspended thread at the 
+ *                       passed address
+ *
+ */
+extern int futex_wake(int *address, boolean_t wake_all);
+
+#endif /* _KERN_FUTEX_H_ */
diff --git a/kern/futex_i.h b/kern/futex_i.h
new file mode 100644
index 0000000..3f611f1
--- /dev/null
+++ b/kern/futex_i.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2013 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *     Author: Marin Ramesa
+ */
+
+#ifndef _KERN_FUTEX_I_H_
+#define _KERN_FUTEX_I_H_
+
+#include <kern/futex.h>
+#include <kern/thread.h>
+#include <vm/vm_map.h>
+#include <kern/lock.h>
+#include <kern/task.h>
+#include <kern/queue.h>
+
+typedef struct futex *futex_t;
+
+struct futex_thread {
+
+       thread_t thread;
+
+       futex_t futex;
+
+       /* Map thread is in. */
+       vm_map_t *map;
+       
+       vm_map_version_t *version;
+       vm_object_t *object;
+       vm_offset_t *offset;
+       vm_prot_t *out_prot;
+       boolean_t *wired;
+};
+
+typedef struct futex_thread *futex_thread_t;
+
+struct futex {
+       /* Queue of futexes. */ 
+       queue_chain_t chain;
+
+       /* Futex lock. */
+       decl_simple_lock_data( , futex_wait_lock);
+
+       /* Futex address. */
+       int *address;
+
+       /* Number of futexed threads at the same address. */
+       unsigned int num_futexed_threads;
+
+       /* Array of futexed threads at the same address. */
+       futex_thread_t futexed_threads;
+
+       /* Hash value in the hash table. */
+       unsigned int hash_val;
+};
+
+/*
+ * Function: futex_init()
+ *
+ * Initialization of a new futex.
+ * 
+ * Takes new futex and address arguments. Returns:
+ *     FUTEX_RESOURCE_SHORTAGE - allocation of the hash table or the array of
+ *                               futex pointers has failed
+ *     0 - success
+ *
+ */
+int futex_init(futex_t futex, int *address);
+
+/*
+ * Function: futex_wake_one_thread()
+ *
+ * Wake one futex thread.
+ * 
+ * Takes the futex and thread number in the array of futex threads arguments. 
+ *
+ */
+void futex_wake_one_thread(futex_t futex, int thread_num);
+
+/*
+ * Function: futex_cross_address_space_wake()
+ *
+ * Cross address space wake. Wakes all threads with the same offset.
+ * 
+ * Takes the futex and wake_all arguments. Meaning of wake_all is the same as 
in
+ * futex_wake(). 
+ *
+ */
+void futex_cross_address_space_wake(futex_t futex, boolean_t wake_all);
+
+/*
+ * Function: futex_wake_threads()
+ *
+ * Wake one or all threads in the futex.
+ * 
+ * Takes the futex and wake_all arguments. Meaning of wake_all is the same as 
in
+ * futex_wake(). 
+ *
+ */
+void futex_wake_threads(futex_t futex, boolean_t wake_all);
+
+/*
+ * Function: futex_hash_table_init()
+ *
+ * Initialization of the hash table.
+ * 
+ * Returns:
+ *     FUTEX_RESOURCE_SHORTAGE - allocation of the hash table has failed
+ *     0 - success
+ *
+ */
+int futex_hash_table_init(void);
+
+/*
+ * Function: futex_hash()
+ *
+ * Generate a hash value. 
+ * 
+ * Takes the address argument. Returns hash value or 0 if hash table is NULL.
+ *
+ */
+unsigned int futex_hash(int *address);
+
+/*
+ * Function: futex_hash_table_lookup_address()
+ *
+ * Finds the futex with the specified address.
+ * 
+ * Takes the address argument. Returns the futex with the address or NULL if 
it's not
+ * found. 
+ *
+ */
+futex_t futex_hash_table_lookup_address(int *address);
+
+/*
+ * Function: futex_hash_table_add_address()
+ *
+ * Add a new address in the hash table.
+ * 
+ * Takes the address argument. Returns:
+ *     1 - allocation of the new futex has failed
+ *     FUTEX_RESOURCE_SHORTAGE - allocation of the hash table or the array of
+ *                               futex pointers has failed
+ *     0 - success 
+ *
+ */
+int futex_hash_table_add_address(int *address);
+
+/*
+ * Function: futex_hash_table_delete_futex()
+ *
+ * Delete a futex from the hash table.
+ * 
+ * Takes the futex to be deleted as an argument. 
+ *
+ */
+void futex_hash_table_delete_futex(futex_t futex);
+
+/*
+ * Function: futex_thread_init()
+ *
+ * Initialization of the new futex thread.
+ * 
+ * Takes the futex and the thread as an argument. Thread is updated in
+ * the function. Returns:
+ *     FUTEX_RESOURCE_SHORTAGE - allocation of the thread or the array of
+ *                               thread pointers has failed
+ *     FUTEX_MEMORY_ERROR - vm_map_lookup() has failed
+ *     0 - success 
+ *
+ */
+int futex_thread_init(futex_t futex, futex_thread_t thread);
+
+#endif /* _KERN_FUTEX_I_H_ */
-- 
1.7.10.4




reply via email to

[Prev in Thread] Current Thread [Next in Thread]