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

2013-12-26 Thread Marin Ramesa
Number of threads to wake is now a boolean, which means it's one or all.
Call to assert_wait() has been added before thread_suspend(). The scan
for vm_objects has been removed and recursion in futex_wake() has been
removed and code rewriten.

There are problems however, futex_wait() test on the Hurd fails with
illegal instruction error. When I comment out the entire futex_wait()
I still get the same error, so I don't know how to fix this, since
I can't trace the error to any instruction in the program. The code
to reproduce this is:

extern int futex_wait();

int e = 0;

int main(void)
{
return futex_wait(e, e);
}

On Linux everything works fine, except segfaults in vm_map_lookup() and
assert_wait() and of course thread_suspend() doesn't actually work. But
when commented out everything else works fine. 

There is also a failed assertion in malloc.c:3096 when allocating memory 
for the threads.

If anybody has any info on this errors, I'll be thankful. They may just
be beginner's mistakes. 

---
 Makefrag.am  |2 +
 kern/futex.c |  370 ++
 kern/futex.h |  100 
 3 files changed, 472 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/kern/futex.c b/kern/futex.c
new file mode 100644
index 000..878be81
--- /dev/null
+++ b/kern/futex.c
@@ -0,0 +1,370 @@
+/*
+ * 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/sched_prim.h
+
+static futex_hash_table_t table = NULL;
+static unsigned int prev_hash_val = 0; 
+static unsigned int max_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 all or 
one 
+ * thread waiting for a change of value at a given address.
+ *
+ * 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 - threads have been resumed by futex_wake()
+ * FUTEX_NO_THREAD - futex_wake() did not found a suspended thread at the 
passed address
+ * FUTEX_MEMORY_ERROR - memory error
+ * FUTEX_RESOURCE_SHORTAGE - no virtual memory 
+ * FUTEX_THREAD_NO_SUSPEND - thread suspend failed
+ *
+ */
+
+int futex_init(futex_t futex, int *address)
+{
+   if (table == NULL) {
+   if (futex_hash_table_init() == FUTEX_RESOURCE_SHORTAGE)
+   return FUTEX_RESOURCE_SHORTAGE;
+   }
+
+   if ((table-futex_elements = 
(futex_t)__builtin_malloc((max_hash_val+1)*sizeof(struct futex))) == NULL)
+   return FUTEX_RESOURCE_SHORTAGE;
+
+   simple_lock_init(futex-futex_wait_lock);
+
+   futex-address = address;
+   futex-num_futexed_threads = 0;
+   futex-next_futex = NULL;
+   futex-prev_futex = (table-futex_elements[prev_hash_val]);
+   futex-prev_futex-next_futex = futex;
+   *futex-map = current_thread()-task-map;
+
+   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)) {
+   unsigned int temp_hash_val = futex_hash(address);
+   __builtin_free((table-futex_elements[temp_hash_val]));
+   return FUTEX_MEMORY_ERROR;
+   }
+   
+   return 0;   
+}
+
+int futex_wait(int *address, int value)
+{
+   futex_t futex;
+
+   lookup:
+
+   futex = futex_hash_table_lookup_address(address);
+
+   if (futex != NULL) {
+
+   simple_lock(futex-futex_wait_lock);
+
+   /* If the 

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

2013-12-26 Thread Richard Braun
On Thu, Dec 26, 2013 at 02:30:10PM +0100, Marin Ramesa wrote:
 + futex-address = address;
 + futex-num_futexed_threads = 0;
 + futex-next_futex = NULL;
 + futex-prev_futex = (table-futex_elements[prev_hash_val]);
 + futex-prev_futex-next_futex = futex;
 + *futex-map = current_thread()-task-map;

A futex shouldn't be a task-local object since it's used for inter
process synchronization. There should be per-thread objects (probably
allocated on the stack since a thread can only be doing one wait at
a time), and these per-thread objects should be listed in the global
futex object. Waking up the futex then simply walks the list and wakes
up threads.

 + 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)) {
 + unsigned int temp_hash_val = futex_hash(address);
 + __builtin_free((table-futex_elements[temp_hash_val]));

Why __builtin_malloc and __builtin_free ??

 + if (futex-num_futexed_threads == 128)
 + return FUTEX_RESOURCE_SHORTAGE;

I assume this limit is temporary. If not, remove it, there is no reason
to add a constraint like this one.

 + suspend:
 + assert_wait(current_thread()-state , FALSE);
 + simple_unlock(futex-futex_wait_lock);
 + kern_return_t ret = thread_suspend(current_thread());

I really doubt assert_wait can be used with thread_suspend and
thread_resume... Use thread_block and thread_wakeup instead. Again, be
more careful.

 +struct futex {

As previously said, rework that structure.

 + /* Next futex in queue. */
 + futex_t next_futex;
 +
 + /* Previous futex in queue. */
 + futex_t prev_futex;

Do NOT reimplement generic doubly-linked lists...

 + /* 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;
 +
 + thread_t futexed_threads[128];

Ugh. No.

 +int futex_init(futex_t futex, int *address);
 +extern int futex_wait(int *address, int value);
 +extern int futex_wake(int *address, boolean_t wake_all);
 +void futex_cross_address_space_wake(futex_t futex, boolean_t wake_all);
 +void futex_wake_threads(futex_t futex, boolean_t wake_all);
 +int futex_hash_table_init(void);
 +unsigned int futex_hash(int *address);
 +futex_t futex_hash_table_lookup_address(int *address);
 +int futex_hash_table_add_address(int *address);
 +void futex_hash_table_delete_futex(futex_t futex);

I know this isn't the traditional way to do it in Mach, but please,
extensively document the API in the header, e.g. as it's done for the
slab allocator. I also suggest moving everything not public (such as
the hash table) in the .c file, and if there is anything private that
still needs to be in a header, move that to a _i.h file (i could mean
internal/implementation/inline/whatever), so that the main header only
documents the public API.

-- 
Richard Braun



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

2013-12-26 Thread Richard Braun
On Thu, Dec 26, 2013 at 02:58:01PM +0100, Richard Braun wrote:
 I know this isn't the traditional way to do it in Mach, but please,
 extensively document the API in the header, e.g. as it's done for the
 slab allocator. I also suggest moving everything not public (such as

Actually, kern/rbtree.h is a better example concerning the separation
of private/public stuff.

-- 
Richard Braun



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

2013-12-26 Thread Marin Ramesa

On 12/26/2013 02:58:01 PM, Richard Braun wrote:

Why __builtin_malloc and __builtin_free ??


I get segfaults in kalloc(). I don't know what I'm doing wrong.


 +  if (futex-num_futexed_threads == 128)
 +  return FUTEX_RESOURCE_SHORTAGE;

I assume this limit is temporary. If not, remove it, there is no  
reason

to add a constraint like this one.


This is just for testing until I found a dynamic allocation function
that I can use with thread allocation.

Thanks for the comments!





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

2013-12-26 Thread Richard Braun
On Thu, Dec 26, 2013 at 03:15:24PM +0100, Marin Ramesa wrote:
 I get segfaults in kalloc(). I don't know what I'm doing wrong.

Show us how you use it.

-- 
Richard Braun



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

2013-12-26 Thread Marin Ramesa

On 12/26/2013 03:25:11 PM, Richard Braun wrote:

On Thu, Dec 26, 2013 at 03:15:24PM +0100, Marin Ramesa wrote:
 I get segfaults in kalloc(). I don't know what I'm doing wrong.

Show us how you use it.


futex-futexed_threads =  
(thread_t)kalloc((futex-num_futexed_threads+1)*sizeof(struct thread));


When futexed_threads is a pointer to struct thread.


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

2013-12-26 Thread Richard Braun
On Thu, Dec 26, 2013 at 03:51:30PM +0100, Marin Ramesa wrote:
 futex-futexed_threads =
 (thread_t)kalloc((futex-num_futexed_threads+1)*sizeof(struct
 thread));
 
 When futexed_threads is a pointer to struct thread.

Why are you allocating a thread structure ??

I strongly suspect kalloc does what you want it to do, but something
in your code makes an illegal access. In the worst case, use prints
to find the faulty instruction.

-- 
Richard Braun