Browse Source

Futex and clone fixes. New futex unit test. May help issue #40.

Don Porter 7 years ago
parent
commit
11029ffff8

+ 13 - 4
LibOS/shim/src/sys/shim_clone.c

@@ -82,6 +82,12 @@
  *   Provided stack and execute the user Provided function.
  */
 
+/* glibc needs space offset by fs.  In the absence of a good way to predict
+ * how big the struct pthread will be (defined in nptl/descr.h),
+ * let's just define a value that over-shoots it.
+ */
+#define PTHREAD_PADDING 2048
+
 int clone_implementation_wrapper(struct clone_args * arg)
 {
     //The child thread created by PAL is now running on the
@@ -89,7 +95,8 @@ int clone_implementation_wrapper(struct clone_args * arg)
     //the user provided stack.
 
     struct clone_args *pcargs = arg;
-
+    int stack_allocated = 0;
+    
     DkObjectsWaitAny(1, &pcargs->create_event, NO_TIMEOUT);
     DkObjectClose(pcargs->create_event);
 
@@ -97,12 +104,14 @@ int clone_implementation_wrapper(struct clone_args * arg)
     assert(my_thread);
     get_thread(my_thread);
 
-    if (!my_thread->tcb)
-        my_thread->tcb = __alloca(sizeof(__libc_tcb_t));
+    if (!my_thread->tcb) {
+        stack_allocated = 1;
+        my_thread->tcb = __alloca(sizeof(__libc_tcb_t) + PTHREAD_PADDING);
+    }
     allocate_tls(my_thread->tcb, my_thread->user_tcb, my_thread);
     shim_tcb_t * tcb = &((__libc_tcb_t *) my_thread->tcb)->shim_tcb;
     debug_setbuf(tcb, true);
-    debug("set tcb to %p\n", my_thread->tcb);
+    debug("set tcb to %p (stack allocated? %d)\n", my_thread->tcb, stack_allocated);
 
     struct shim_regs * regs = __alloca(sizeof(struct shim_regs));
     *regs = *((__libc_tcb_t *) arg->parent->tcb)->shim_tcb.context.regs;

+ 6 - 10
LibOS/shim/src/sys/shim_futex.c

@@ -174,20 +174,15 @@ int shim_do_futex (unsigned int * uaddr, int op, int val, void * utime,
         case FUTEX_WAKE_BITSET: {
             uint32_t bitset = (futex_op == FUTEX_WAKE_BITSET) ? val3 :
                               0xffffffff;
+            struct list_head *cursor;
             debug("FUTEX_WAKE: %p (val = %d) count = %d mask = %08x\n",
                   uaddr, *uaddr, val, bitset);
             int cnt, nwaken = 0;
-            for (cnt = 0 ; cnt < val ; cnt++) {
-                if (list_empty(&futex->waiters))
-                    break;
 
-                // BUG: if the first entry in the list isn't eligible, do we
-                // ever wake anything up? doesn't this check the first entry
-                // over and over?
-                struct futex_waiter * waiter = list_entry(futex->waiters.next,
+            list_for_each(cursor, &futex->waiters) {
+                struct futex_waiter * waiter = list_entry(cursor,
                                                           struct futex_waiter,
                                                           list);
-
                 if (!(bitset & waiter->bitset))
                     continue;
 
@@ -196,10 +191,11 @@ int shim_do_futex (unsigned int * uaddr, int op, int val, void * utime,
                 list_del(&waiter->list);
                 thread_wakeup(waiter->thread);
                 nwaken++;
+                if (nwaken >= val) break;
             }
-
+            
             ret = nwaken;
-            debug("FUTEX_WAKE done: %p (val = %d)\n", uaddr, *uaddr);
+            debug("FUTEX_WAKE done: %p (val = %d) woke %d threads\n", uaddr, *uaddr, ret);
             break;
         }
 

+ 14 - 0
LibOS/shim/test/regression/30_futex.py

@@ -0,0 +1,14 @@
+#!/usr/bin/python
+
+import os, sys, mmap
+from regression import Regression
+
+loader = sys.argv[1]
+
+# Running futex
+regression = Regression(loader, "futex")
+
+regression.add_check(name="Futex Wake Test",
+    check=lambda res: "Woke all kiddos" in res[0].out)
+
+regression.run_checks()

+ 2 - 0
LibOS/shim/test/regression/Makefile

@@ -47,6 +47,8 @@ PYTHONENV="PYTHONPATH=../../../../Scripts"
 regression: $(target)
 	@echo "\n\nBasic Bootstrapping:"
 	@for f in $(wildcard 00_*.py); do env $(PYTHONENV) python $$f $(RUNTIME)/pal-$(PAL_HOST); done
+	@echo "\n\nFutex Support:"
+	@for f in $(wildcard 30_*.py); do env $(PYTHONENV) python $$f $(RUNTIME)/pal-$(PAL_HOST); done
 	@echo "\n\nLarge File Support:"
 	@for f in $(wildcard 90_*.py); do env $(PYTHONENV) python $$f $(RUNTIME)/pal-$(PAL_HOST); done
 	@echo "\n\nLTP tests for system calls:"

+ 114 - 0
LibOS/shim/test/regression/futex.c

@@ -0,0 +1,114 @@
+/* -*- mode:c; c-file-style:"k&r"; c-basic-offset: 4; tab-width:4; indent-tabs-mode:nil; mode:auto-fill; fill-column:78; -*- */
+/* vim: set ts=4 sw=4 et tw=78 fo=cqt wm=0: */
+
+#define _GNU_SOURCE
+#include <malloc.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <sched.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+#include <asm/prctl.h>
+#include <linux/futex.h>
+#include <assert.h>
+
+// 64kB stack
+#define FIBER_STACK 1024 * 64
+#define THREADS 2
+static int myfutex = 0;
+struct atomic_int {
+    volatile int counter;
+};
+static struct atomic_int my_counter;
+    
+static inline void atomic_inc (struct atomic_int * v)
+{
+    asm volatile( "lock; incl %0"
+                 : "+m" (v->counter));
+}
+
+static int
+futex(int *uaddr, int futex_op, int val,
+      const struct timespec *timeout, int *uaddr2, int val3)
+{
+    return syscall(SYS_futex, uaddr, futex_op, val,
+                   timeout, uaddr, val3);
+}
+
+int thread_function (void * argument)
+{
+    int *ptr = (int *) argument;
+    int rv;
+    atomic_inc(&my_counter);
+
+    // Sleep on the futex
+    rv = futex(&myfutex, FUTEX_WAIT_BITSET, 0, NULL, NULL, *ptr);
+    assert(rv == 0);
+    //printf("child thread %d awakened\n", getpid());
+    return 0;
+}
+
+int main (int argc, const char ** argv)
+{
+    void * stacks[THREADS];
+    pid_t pids[THREADS];
+    int varx[THREADS];
+    my_counter.counter = 0;
+
+    for (int i = 0; i < THREADS; i++) {
+
+        varx[i] = (1 << i);
+        
+        // Allocate the stack
+        stacks[i] = malloc(FIBER_STACK);
+        if (stacks[i] == 0) {
+            perror("malloc: could not allocate stack");
+            _exit(1);
+        }
+        
+        // Call the clone system call to create the child thread
+        pids[i] = clone(&thread_function, (void *) stacks[i] + FIBER_STACK,
+                        CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM,
+                        &varx[i]);
+        
+        //printf("clone() creates new thread %d\n", pids[i]);
+        
+        if (pids[i] == -1) {
+            perror("clone");
+            _exit(2);
+        }
+    }
+    
+    // Make sure the threads are sleeping
+    do {
+        sleep(1); 
+    } while(my_counter.counter != THREADS);
+    
+    printf("Waking up kiddos\n");
+    /* Wake in reverse order */
+    for (int i = THREADS-1; i >= 0; i--) {
+        pid_t pid;
+        int rv;
+        int var = (1 << i);
+
+        // Wake up the thread
+        rv = futex(&myfutex, FUTEX_WAKE_BITSET, 1, NULL, NULL, var);
+        assert(rv == 1);
+
+        // Wait for the child thread to exit
+        pid = waitpid(pids[i], NULL, __WALL);
+        if (pid == -1) {
+            perror("waitpid");
+            _exit(3);
+        }
+
+        // Free the stack
+        free(stacks[i]);
+    }
+
+    printf("Woke all kiddos\n");
+
+    return 0;
+}

+ 21 - 0
LibOS/shim/test/regression/futex.manifest.template

@@ -0,0 +1,21 @@
+loader.preload = file:../../src/libsysdb.so
+loader.env.LD_LIBRARY_PATH = /lib
+loader.debug_type = none
+loader.syscall_symbol = syscalldb
+
+fs.mount.lib.type = chroot
+fs.mount.lib.path = /lib
+fs.mount.lib.uri = file:../../../../Runtime
+
+fs.mount.bin.type = chroot
+fs.mount.bin.path = /bin
+fs.mount.bin.uri = file:/bin
+
+# allow to bind on port 8000
+net.rules.1 = 127.0.0.1:8000:0.0.0.0:0-65535
+# allow to connect to port 8000
+net.rules.2 = 0.0.0.0:0-65535:127.0.0.1:8000
+
+sgx.trusted_files.ld = file:../../../../Runtime/ld-linux-x86-64.so.2
+sgx.trusted_files.libc = file:../../../../Runtime/libc.so.6
+sgx.thread_num = 4