bug-hurd
[Top][All Lists]
Advanced

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

[PATCH] kern: simple futex for gnumach (version 4)


From: Marin Ramesa
Subject: [PATCH] kern: simple futex for gnumach (version 4)
Date: Sun, 22 Dec 2013 15:58:55 +0100

GPL license included, more bugs fixed and RPCs defined.

---
 Makefrag.am               |   2 +
 include/mach/gnumach.defs |  26 +++
 kern/futex.c              | 405 ++++++++++++++++++++++++++++++++++++++++++++++
 kern/futex.h              | 108 +++++++++++++
 4 files changed, 541 insertions(+)
 create mode 100644 kern/futex.c
 create mode 100644 kern/futex.h

diff --git a/Makefrag.am b/Makefrag.am
index c1387bd..e43f882 100644
--- a/Makefrag.am
+++ b/Makefrag.am
@@ -145,6 +145,8 @@ libkernel_a_SOURCES += \
        kern/eventcount.h \
        kern/exception.c \
        kern/exception.h \
+       kern/futex.c \
+       kern/futex.h \
        kern/host.c \
        kern/host.h \
        kern/ipc_host.c \
diff --git a/include/mach/gnumach.defs b/include/mach/gnumach.defs
index 12c4e99..8c5ea4b 100644
--- a/include/mach/gnumach.defs
+++ b/include/mach/gnumach.defs
@@ -63,3 +63,29 @@ simpleroutine thread_terminate_release(
                reply_port      : mach_port_name_t;
                address         : vm_address_t;
                size            : vm_size_t);
+
+/*
+ * When operation equals FUTEX_WAIT, this is a method for a thread to wait for 
a 
+ * change of value at a given address. 
+ *
+ * When operation equals FUTEX_WAKE, this is a method for waking up a number
+ * (specified by the argument value) of threads waiting for a change of value
+ * at a given address (threads that are in FUTEX_WAIT).
+ */
+
+simpleroutine futex_rpc(
+               thread          : thread_t;
+               address         : pointer_t;
+               value           : int;
+               operation       : int);
+
+simpleroutine futex_wait_rpc(
+               thread          : thread_t;
+               address         : pointer_t;
+               value           : int);
+
+simpleroutine futex_wake_rpc(
+               thread          : thread_t;
+               address         : pointer_t;
+               thread_num      : int);
+
diff --git a/kern/futex.c b/kern/futex.c
new file mode 100644
index 0000000..7860265
--- /dev/null
+++ b/kern/futex.c
@@ -0,0 +1,405 @@
+/*
+ * 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/kalloc.h>
+
+static futex_hash_table_t table = NULL;
+static unsigned int prev_hash_val = 0; 
+
+/*
+ * When operation equals FUTEX_WAIT, this is a method for a thread to wait for 
a 
+ * change of value at a given address. 
+ *
+ * When operation equals FUTEX_WAKE, this is a method for waking up a number
+ * (specified by the argument value) of threads waiting for a change of value
+ * at a given address (threads that are in FUTEX_WAIT).
+ *
+ * Return values:
+ *     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_SOME_NUMBER_RESUMED - value threads have been resumed by 
FUTEX_WAKE
+ *     FUTEX_NO_THREAD - FUTEX_WAKE did not found a suspended thread at the 
passed address
+ *     FUTEX_EXISTS - futex already exists at the new address
+ *     FUTEX_MEMORY_ERROR - memory error
+ *     FUTEX_UNKNOWN_OPERATION - unknown operation
+ *     FUTEX_RESOURCE_SHORTAGE - no virtual memory or the mutex is not 
per-process
+ *
+ */
+int futex(int *address, int value, int operation)
+{
+       switch (operation) {
+               case FUTEX_WAIT:
+                       return futex_wait(address, value);
+               case FUTEX_WAKE:
+                       return futex_wake(address, value);
+               default:
+                       return FUTEX_UNKNOWN_OPERATION;
+       }       
+}
+
+int futex_init(int *address, futex_t futex, futex_hash_table_t hash_table, 
unsigned int hash_val)
+{
+       simple_lock_init(&futex->futex_wait_lock);
+
+       if (hash_table == NULL) {
+               hash_table = futex_hash_table_init(INITIAL_HASH_TABLE_SIZE, 
current_thread()->task);
+               if (hash_table == NULL)
+                       return FUTEX_RESOURCE_SHORTAGE;
+       }
+
+       if ((hash_table->futex_elements = 
+               (futex_t)kalloc((hash_table->size + 1) * sizeof(struct futex))) 
== NULL) {
+
+               return FUTEX_RESOURCE_SHORTAGE;
+
+       }
+
+       futex->address = address;
+       futex->num_futexed_threads = 0;
+       futex->next_futex = &(hash_table->futex_elements[hash_val]);
+       futex->prev_futex = &(hash_table->futex_elements[prev_hash_val]);
+
+       if ((vm_map_lookup(futex->map, (vm_offset_t)address, VM_PROT_READ, 
futex->version, futex->object,
+                               futex->offset, futex->out_prot, futex->wired) 
!= KERN_SUCCESS)) {
+               hash_table->size++;
+               unsigned int temp_hash_val = futex_hash(hash_table, address);
+               
kfree((vm_offset_t)&(hash_table->futex_elements[temp_hash_val]), sizeof(struct 
futex));
+               hash_table->size--;
+               return FUTEX_MEMORY_ERROR;
+       }
+
+       hash_table->size++;
+
+       return 0;       
+}
+
+int futex_wait(int *address, int value)
+{
+       futex_t futex;
+       boolean_t per_process_mutex;
+
+       lookup:
+
+       per_process_mutex = TRUE;
+
+       futex = futex_hash_table_lookup_address(table, address, 
&per_process_mutex);
+
+       if (per_process_mutex == FALSE)
+               return FUTEX_RESOURCE_SHORTAGE;
+
+       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. */
+                       if ((futex->futexed_threads = 
+                               
(thread_t)kalloc((futex->num_futexed_threads+1)*sizeof(struct thread))) == 
NULL) {
+                               simple_unlock(&futex->futex_wait_lock);
+                               return FUTEX_RESOURCE_SHORTAGE;
+                       }
+
+                       futex->futexed_threads[++futex->num_futexed_threads] = 
*(current_thread());
+
+                       goto suspend;
+               } else 
+                       goto no_suspend;
+
+       } else {
+               
+               int i = futex_hash_table_add_address(table, address); 
+
+               if (i == 1) return FUTEX_RESOURCE_SHORTAGE;
+               if (i == 2) return FUTEX_EXISTS;
+               if (i != 0) return i;
+               
+               goto lookup;
+
+       }
+
+       suspend:
+               simple_unlock(&futex->futex_wait_lock);
+               thread_suspend(current_thread());
+               return FUTEX_RESUMED_BY_WAKE;
+
+       no_suspend:
+               simple_unlock(&futex->futex_wait_lock);
+               return FUTEX_NO_THREAD_SUSPENDED;
+}              
+
+int futex_wake(int *address, int thread_num)
+{
+       futex_t futex, temp_futex;
+
+       unsigned int hash_val = futex_hash(table, address);
+       boolean_t per_process_mutex = TRUE;
+
+       temp_futex = futex_hash_table_lookup_address(table, address, 
&per_process_mutex);
+
+       if (per_process_mutex == FALSE)
+               return FUTEX_RESOURCE_SHORTAGE;
+
+       if (temp_futex != NULL) {
+
+               for (futex = &(table->futex_elements[hash_val]); futex != NULL; 
+                       futex = futex->next_futex) {
+
+                       if ((*(futex->offset) == *(futex->next_futex->offset)) 
&&
+                               (futex->object == futex->next_futex->object)) {
+                       
+                               (void) futex_wake(futex->next_futex->address, 
thread_num);
+
+                       }
+               }
+
+               for (futex = &(table->futex_elements[hash_val]); futex != NULL; 
+                       futex = futex->prev_futex) {
+
+                       if ((*(futex->offset) == *(futex->prev_futex->offset)) 
&&
+                               (futex->object == futex->prev_futex->object)) {
+
+                               (void) futex_wake(futex->prev_futex->address, 
thread_num);
+
+                       }
+               }
+
+       } else
+               goto no_thread;
+
+       futex = futex_hash_table_lookup_address(table, address, 
&per_process_mutex);
+
+       if (per_process_mutex == FALSE)
+               return FUTEX_RESOURCE_SHORTAGE;
+
+       if (futex != NULL) {
+
+               simple_lock(&futex->futex_wait_lock);
+
+               /* If the number of threads to be awakened is greater then the 
number
+                * of threads in the futex, wake all.
+                */
+               if (thread_num > futex->num_futexed_threads)
+                       thread_num = futex->num_futexed_threads - 1;
+               int i;
+               vm_size_t size_free = 0;
+               for (i = 0; i < thread_num; i++) {
+                       thread_resume(&(futex->futexed_threads[i]));
+                       futex->num_futexed_threads--;
+                       size_free++;
+               }
+               kfree((vm_offset_t)&(futex->futexed_threads[i]), size_free * 
sizeof(struct thread));
+       
+               if (futex->num_futexed_threads == 0)
+                       if (futex_hash_table_delete_futex(futex, table, 
address) == -1) {
+                               simple_unlock(&futex->futex_wait_lock);
+                               return FUTEX_RESOURCE_SHORTAGE;
+                       }
+
+               simple_unlock(&futex->futex_wait_lock);
+
+               return FUTEX_SOME_NUMBER_RESUMED;
+       } else {
+               no_thread:
+                       return FUTEX_NO_THREAD;
+       }
+}      
+
+futex_hash_table_t futex_hash_table_init(size_t size, task_t task)
+{
+       /* If we're still in the same task. */
+       if (current_thread()->task == task) {
+
+               futex_hash_table_t new_table;
+
+               simple_lock_init(&new_table->futex_hash_table_lock);
+
+               if ((new_table = 
+                       (futex_hash_table_t)kalloc(sizeof(struct 
futex_hash_table))) == NULL)
+                       return NULL;
+
+               if ((new_table->futex_elements = 
+                       (futex_t)kalloc(size * sizeof(struct futex))) == NULL)
+                       return NULL;
+
+               new_table->size = size;
+
+               new_table->task = task;
+
+               return new_table;
+       } else
+               return NULL;
+}
+
+unsigned int futex_hash(futex_hash_table_t hash_table, int *address)
+{
+       unsigned int hash_val = 0;
+
+       hash_val = (unsigned int)address + (hash_val << 5) - hash_val;
+
+       if (hash_table != NULL)
+               return hash_val % hash_table->size;
+       else
+               return 0;
+}
+
+futex_t futex_hash_table_lookup_address(futex_hash_table_t hash_table, int 
*address, 
+                                        boolean_t *per_process_mutex)
+{
+       futex_t futex;
+
+       if (hash_table == NULL)
+               return NULL;
+
+       if (current_thread()->task == hash_table->task) {
+
+               simple_lock(&hash_table->futex_hash_table_lock);
+
+               unsigned int hash_val = futex_hash(hash_table, address);
+
+               for (futex = &(hash_table->futex_elements[hash_val]); futex != 
NULL; 
+                       futex = futex->next_futex) {
+
+                       if (address == futex->address) {
+                               
simple_unlock(&hash_table->futex_hash_table_lock);
+                               return futex;
+
+                       }
+               }
+
+               for (futex = &(hash_table->futex_elements[hash_val]); futex != 
NULL; 
+                       futex = futex->prev_futex) {
+
+                       if (address == futex->address) {
+                               
simple_unlock(&hash_table->futex_hash_table_lock);
+                               return futex;
+
+                       }
+               }
+
+               simple_unlock(&hash_table->futex_hash_table_lock);
+       
+       } else
+               *per_process_mutex = FALSE;
+
+       return NULL;
+}
+
+int futex_hash_table_add_address(futex_hash_table_t hash_table, int *address)
+{
+       futex_t new_futex;
+       futex_t current_futex;
+
+       unsigned int hash_val = futex_hash(hash_table, address);
+
+       if (hash_table != NULL) {
+       
+               simple_lock(&hash_table->futex_hash_table_lock);
+       }
+
+       if ((new_futex = (futex_t)kalloc(sizeof(struct futex))) == NULL) {
+               if (hash_table != NULL)
+                       simple_unlock(&hash_table->futex_hash_table_lock);
+               return 1;
+       }
+
+       boolean_t per_process_mutex = TRUE;
+
+       current_futex = futex_hash_table_lookup_address(hash_table, address, 
&per_process_mutex);
+       if (current_futex != NULL) {
+               simple_unlock(&hash_table->futex_hash_table_lock);
+               return 2;
+       }
+       if (per_process_mutex == FALSE) return 1;
+
+       int ret;
+
+       if ((ret = futex_init(address, new_futex, hash_table, hash_val)) != 0) {
+               simple_unlock(&hash_table->futex_hash_table_lock);
+               return ret;
+       }
+
+       hash_table->futex_elements[hash_val] = *new_futex;
+
+       prev_hash_val = hash_val;
+
+       if (hash_table != NULL)
+               simple_unlock(&hash_table->futex_hash_table_lock);
+
+       return 0;
+}      
+
+int futex_hash_table_delete_futex(futex_t futex, futex_hash_table_t 
hash_table, int *address)
+{
+       if (current_thread()->task == hash_table->task) {
+
+               /* New next futex in queue. */
+               futex_t new_next_futex = futex->next_futex;
+
+               simple_lock(&hash_table->futex_hash_table_lock);
+
+               unsigned int hash_val = futex_hash(hash_table, address);
+
+               hash_table->futex_elements[hash_val].prev_futex->next_futex = 
new_next_futex;
+
+               /* New previous futex of the new next futex in queue. */
+               
hash_table->futex_elements[hash_val].prev_futex->next_futex->prev_futex = 
+                       hash_table->futex_elements[hash_val].prev_futex;
+
+               kfree((vm_offset_t)&(hash_table->futex_elements[hash_val]), 
sizeof(struct futex));
+
+               hash_table->size--;
+
+               if (hash_table->size == 0) {
+                       kfree((vm_offset_t)hash_table, sizeof(struct 
futex_hash_table));
+                       hash_table = NULL;
+               }
+
+               simple_unlock(&hash_table->futex_hash_table_lock);
+
+               return 0;
+
+       } else
+               return -1;
+}
+
+int futex_rpc(thread_t thread, int *address, int value, int operation)
+{
+       return futex(address, value, operation);
+}
+
+int futex_wait_rpc(thread_t thread, int *address, int value)
+{
+       return futex_wait(address, value);
+}
+
+int futex_wake_rpc(thread_t thread, int *address, int thread_num)
+{
+       return futex_wake(address, thread_num);
+}
+
diff --git a/kern/futex.h b/kern/futex.h
new file mode 100644
index 0000000..0a22663
--- /dev/null
+++ b/kern/futex.h
@@ -0,0 +1,108 @@
+/*
+ * 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 <kern/thread.h>
+#include <vm/vm_map.h>
+#include <kern/lock.h>
+#include <kern/task.h>
+
+/* Definitions for futex operations. */
+#define FUTEX_WAIT     0
+#define FUTEX_WAKE     1
+
+/* Definitions for return values. */
+#define FUTEX_RESUMED_BY_WAKE 0
+#define FUTEX_NO_THREAD_SUSPENDED -1
+#define FUTEX_SOME_NUMBER_RESUMED 1
+#define FUTEX_NO_THREAD -2
+#define FUTEX_EXISTS -3
+#define FUTEX_MEMORY_ERROR -4
+#define FUTEX_UNKNOWN_OPERATION -5
+#define FUTEX_RESOURCE_SHORTAGE -6
+
+/* Initial size of the hash table. */
+#define INITIAL_HASH_TABLE_SIZE 8
+
+typedef struct futex *futex_t;
+
+struct futex {
+       
+       /* Futex lock. */
+       decl_simple_lock_data( , futex_wait_lock);
+
+       /* Futex address. */
+       int *address;
+
+       /* Map futex 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;
+
+       /* Next futex in queue. */
+       futex_t next_futex;
+
+       /* Previous futex in queue. */
+       futex_t prev_futex;
+
+       /* Number of futexed threads at the same address. */
+       unsigned int num_futexed_threads;
+
+       /* Array of futexed threads at the same address. */
+       thread_t futexed_threads;
+};
+
+struct futex_hash_table {
+       
+       /* Futex hash table lock. */
+       decl_simple_lock_data( , futex_hash_table_lock);
+
+       /* Size of the hash table. */
+       size_t size;
+
+       /* Task to which the hash table belongs. */
+       task_t task;
+
+       /* Array of futexes. */
+       futex_t futex_elements;
+};
+
+typedef struct futex_hash_table *futex_hash_table_t;
+
+extern int futex(int *address, int value, int operation);
+int futex_init(int *address, futex_t futex, futex_hash_table_t hash_table, 
unsigned int hash_val);
+extern int futex_wait(int *address, int value);
+extern int futex_wake(int *address, int thread_num);
+futex_hash_table_t futex_hash_table_init(size_t size, task_t task);
+unsigned int futex_hash(futex_hash_table_t hash_table, int *address);
+futex_t futex_hash_table_lookup_address(futex_hash_table_t hash_table, int 
*address, boolean_t *per_process_mutex);
+int futex_hash_table_add_address(futex_hash_table_t hash_table, int *address);
+int futex_hash_table_delete_futex(futex_t futex, futex_hash_table_t 
hash_table, int *address);
+extern int futex_rpc(thread_t thread, int *address, int value, int operation);
+extern int futex_wait_rpc(thread_t thread, int *address, int value);
+extern int futex_wake_rpc(thread_t thread, int *address, int thread_num);
+
+#endif /* _KERN_FUTEX_H_ */
-- 
1.8.1.4




reply via email to

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