Convert run_once() to parlib_run_once() (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 4 Apr 2017 17:59:25 +0000 (13:59 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 3 May 2017 16:13:02 +0000 (12:13 -0400)
The kernel still uses run_once(); that's now in the kernel-only header.
Userspace will now use parlib_run_once().  They can also use
pthread_once(), which differs in that you can't pass an argument to the
init function.  That's a POSIX limitation, but I'll need the void *arg for
another use case.

Rebuild glibc.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
13 files changed:
kern/include/common.h
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/socket.c
tools/dev-util/perf/akaros.c
user/benchutil/pvcalarm.c
user/iplib/epoll.c
user/iplib/select.c
user/parlib/debug.c
user/parlib/include/parlib/parlib.h
user/parlib/include/parlib/slab.h
user/parlib/slab.c
user/pthread/futex.c
user/pthread/pthread.c
user/pthread/pthread.h

index 61d2ae6..859fd35 100644 (file)
 #define IS_PWR2(x) ((x) && !((x) & (x - 1)))
 
 #define ARRAY_SIZE(x) COUNT_OF(x)
+
+/* Makes sure func is run exactly once.  Can handle concurrent callers, and
+ * other callers spin til the func is complete. */
+#define run_once(func)                                                         \
+do {                                                                           \
+       static bool ran_once = FALSE;                                              \
+       static bool is_running = FALSE;                                            \
+       if (!ran_once) {                                                           \
+               /* fetch and set TRUE, without a header or test_and_set weirdness */   \
+               if (!__sync_fetch_and_or(&is_running, TRUE)) {                         \
+                       /* we won the race and get to run the func */                      \
+                       func;                                                              \
+                       wmb();  /* don't let the ran_once write pass previous writes */    \
+                       ran_once = TRUE;                                                   \
+               } else {                                                               \
+                       /* someone else won, wait til they are done to break out */        \
+                       while (!ran_once)                                                  \
+                               cpu_relax();                                                   \
+               }                                                                      \
+       }                                                                          \
+} while (0)
+
+/* Unprotected, single-threaded version, makes sure func is run exactly once */
+#define run_once_racy(func)                                                    \
+do {                                                                           \
+       static bool ran_once = FALSE;                                              \
+       if (!ran_once) {                                                           \
+               func;                                                                  \
+               ran_once = TRUE;                                                       \
+       }                                                                          \
+} while (0)
index d8caa81..9a29049 100644 (file)
@@ -17,6 +17,7 @@
 #include <sys/stat.h>
 #include <sys/close_cb.h>
 #include <ros/common.h>
+#include <parlib/parlib.h>
 
 /* bsd extensions */
 #include <sys/uio.h>
 
 #include <sys/plan9_helpers.h>
 
+static void socket_init(void *arg)
+{
+       static struct close_cb _sock_close_cb = {.func = _sock_fd_closed};
+
+       register_close_cb(&_sock_close_cb);
+}
+
 /* Create a new socket of type TYPE in domain DOMAIN, using
    protocol PROTOCOL.  If PROTOCOL is zero, one is chosen automatically.
    Returns a file descriptor for the new socket, or -1 for errors.  */
@@ -35,10 +43,10 @@ int __socket(int domain, int type, int protocol)
        int pfd[2];
        const char *net;
        char msg[128];
-       static struct close_cb _sock_close_cb = {.func = _sock_fd_closed};
        int open_flags;
+       static parlib_once_t once = PARLIB_ONCE_INIT;
 
-       run_once(register_close_cb(&_sock_close_cb));
+       parlib_run_once(&once, socket_init, NULL);
 
        switch (domain) {
                case PF_INET:
index 3ae2a22..08d8e2a 100644 (file)
@@ -52,17 +52,18 @@ static int get_vars_nr_cores(void)
 
 static int nr_cores;
 
-static void set_nr_cores(void)
+static void set_nr_cores(void *arg)
 {
        nr_cores = get_vars_nr_cores();
        if (nr_cores == -1)
                nr_cores = guess_nr_cores();
 }
 
-
 size_t ros_total_cores(void)
 {
-       run_once(set_nr_cores());
+       static parlib_once_t once = PARLIB_ONCE_INIT;
+
+       parlib_run_once(&once, set_nr_cores, NULL);
        return nr_cores;
 }
 
index b43c1b9..087ab9b 100644 (file)
@@ -61,7 +61,7 @@ static void handle_alarm_prof(struct event_msg *ev_msg, unsigned int ev_type,
                               void *data);
 
 /* Initialize the pvcalarm service. Only call this function once */
-static void init_global_pvcalarm(void)
+static void init_global_pvcalarm(void *arg)
 {
        global_pvcalarm.interval = 0;
        global_pvcalarm.callback = NULL;
@@ -108,6 +108,8 @@ static void stop_pvcalarm(struct pvcalarm_data *pvcalarm_data)
  * active vcore according to that policy. */
 int enable_pvcalarms(int method, uint64_t interval, void (*callback) (void))
 {
+       static parlib_once_t once = PARLIB_ONCE_INIT;
+
        assert(!in_vcore_context());
        if (method != PVCALARM_REAL && method != PVCALARM_PROF)
                return EINVAL;
@@ -118,7 +120,7 @@ int enable_pvcalarms(int method, uint64_t interval, void (*callback) (void))
        if (!atomic_cas(&global_pvcalarm.state, S_DISABLED, S_ENABLING))
                return EBUSY;
 
-       run_once_racy(init_global_pvcalarm());
+       parlib_run_once(&once, init_global_pvcalarm, NULL);
 
        global_pvcalarm.interval = usec2tsc(interval);
        global_pvcalarm.callback = callback;
index 85fa52f..9a1c2fd 100644 (file)
@@ -272,7 +272,7 @@ static void ep_alarm_dtor(void *obj, size_t unused)
        ep_put_alarm_evq(ep_a->alarm_evq);
 }
 
-static void epoll_init(void)
+static void epoll_init(void *arg)
 {
        static struct close_cb epoll_close_cb = {.func = epoll_fd_closed};
 
@@ -289,8 +289,9 @@ int epoll_create(int size)
 {
        int fd;
        struct epoll_ctlr *ep;
+       static parlib_once_t once = PARLIB_ONCE_INIT;
 
-       run_once(epoll_init());
+       parlib_run_once(&once, epoll_init, NULL);
        /* good thing the arg is a signed int... */
        if (size < 0) {
                errno = EINVAL;
index c8a822b..c50349b 100644 (file)
@@ -53,6 +53,7 @@
 #include <malloc.h>
 #include <parlib/arch/arch.h>
 #include <parlib/uthread.h>
+#include <parlib/parlib.h>
 #include <ros/common.h>
 #include <ros/fs.h>
 #include <signal.h>
@@ -108,7 +109,7 @@ static void select_forked(void)
        uth_mutex_unlock(fdset_mtx);
 }
 
-static void select_init(void)
+static void select_init(void *arg)
 {
        static struct close_cb select_close_cb = {.func = select_fd_closed};
        static struct fork_cb select_fork_cb = {.func = select_forked};
@@ -162,8 +163,9 @@ int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
        uintptr_t my_call_id;
        int ret;
        int ep_timeout = select_tv_to_ep_timeout(timeout);
+       static parlib_once_t once = PARLIB_ONCE_INIT;
 
-       run_once(select_init());
+       parlib_run_once(&once, select_init, NULL);
        /* good thing nfds is a signed int... */
        if (nfds < 0) {
                errno = EINVAL;
index 42d72b0..e81acb5 100644 (file)
@@ -79,19 +79,23 @@ void __print_func_exit(const char *func, const char *file)
        spinlock_unlock(&lock);
 }
 
+static int kptrace;
+
+static void trace_init(void *arg)
+{
+       kptrace = open("#kprof/kptrace", O_WRITE);
+       if (kptrace < 0)
+               perror("Unable to open kptrace!\n");
+}
+
 void trace_printf(const char *fmt, ...)
 {
-       static int kptrace;
        va_list args;
        char buf[128];
        int amt;
+       static parlib_once_t once = PARLIB_ONCE_INIT;
 
-       run_once(
-               kptrace = open("#kprof/kptrace", O_WRITE);
-               if (kptrace < 0)
-                       perror("Unable to open kptrace!\n");
-       );
-
+       parlib_run_once(&once, trace_init, NULL);
        if (kptrace < 0)
                return;
        amt = snprintf(buf, sizeof(buf), "PID %d: ", getpid());
index 8bcd54d..d968ffc 100644 (file)
@@ -73,6 +73,49 @@ pid_t create_child(const char *exe, int argc, char *const argv[],
 pid_t create_child_with_stdfds(const char *exe, int argc, char *const argv[],
                                char *const envp[]);
 
+/* Once */
+typedef struct {
+       bool ran_once;
+       bool is_running;
+} parlib_once_t;
+
+#define PARLIB_ONCE_INIT {FALSE, FALSE}
+
+/* Makes sure func is run exactly once.  Can handle concurrent callers, and
+ * other callers spin til the func is complete. */
+static inline void parlib_run_once(parlib_once_t *once_ctl,
+                                   void (*init_fn)(void *), void *arg)
+{
+       if (!once_ctl->ran_once) {
+               /* fetch and set TRUE, without a header or test_and_set weirdness */
+               if (!__sync_fetch_and_or(&once_ctl->is_running, TRUE)) {
+                       /* we won the race and get to run the func */
+                       init_fn(arg);
+                       wmb();  /* don't let the ran_once write pass previous writes */
+                       once_ctl->ran_once = TRUE;
+               } else {
+                       /* someone else won, wait til they are done to break out */
+                       while (!once_ctl->ran_once)
+                               cpu_relax();
+               }
+       }
+}
+
+/* Unprotected, single-threaded version, makes sure func is run exactly once */
+static inline void parlib_run_once_racy(parlib_once_t *once_ctl,
+                                        void (*init_fn)(void *), void *arg)
+{
+       if (!once_ctl->ran_once) {
+               init_fn(arg);
+               once_ctl->ran_once = TRUE;
+       }
+}
+
+static inline void parlib_set_ran_once(parlib_once_t *once_ctl)
+{
+       once_ctl->ran_once = TRUE;
+}
+
 /* Aborts with 'retcmd' if this function has already been called.  Compared to
  * run_once, this is put at the top of a function that can be called from
  * multiple sources but should only execute once. */
index 7195819..8b89fc0 100644 (file)
@@ -98,7 +98,6 @@ void kmem_cache_destroy(struct kmem_cache *cp);
 void *kmem_cache_alloc(struct kmem_cache *cp, int flags);
 void kmem_cache_free(struct kmem_cache *cp, void *buf);
 /* Back end: internal functions */
-void kmem_cache_init(void);
 void kmem_cache_reap(struct kmem_cache *cp);
 
 /* Debug */
index f094c11..b6bf38f 100644 (file)
@@ -15,6 +15,7 @@
 #include <parlib/slab.h>
 #include <stdio.h>
 #include <parlib/assert.h>
+#include <parlib/parlib.h>
 #include <sys/mman.h>
 #include <sys/param.h>
 
@@ -65,7 +66,7 @@ static void __kmem_cache_create(struct kmem_cache *kc, const char *name,
        spin_pdr_unlock(&kmem_caches_lock);
 }
 
-void kmem_cache_init(void)
+static void kmem_cache_init(void *arg)
 {
        spin_pdr_init(&kmem_caches_lock);
        SLIST_INIT(&kmem_caches);
@@ -90,8 +91,11 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t obj_size,
                                      void (*ctor)(void *, size_t),
                                      void (*dtor)(void *, size_t))
 {
-       run_once(kmem_cache_init());
-       struct kmem_cache *kc = kmem_cache_alloc(&kmem_cache_cache, 0);
+       struct kmem_cache *kc;
+       static parlib_once_t once = PARLIB_ONCE_INIT;
+
+       parlib_run_once(&once, kmem_cache_init, NULL);
+       kc = kmem_cache_alloc(&kmem_cache_cache, 0);
        __kmem_cache_create(kc, name, obj_size, align, flags, ctor, dtor);
        return kc;
 }
index 803a751..d82dfc3 100644 (file)
@@ -30,7 +30,7 @@ struct futex_data {
 };
 static struct futex_data __futex;
 
-static inline void futex_init()
+static inline void futex_init(void *arg)
 {
   mcs_pdr_init(&__futex.lock);
   TAILQ_INIT(&__futex.queue);
@@ -188,6 +188,9 @@ int futex(int *uaddr, int op, int val,
           const struct timespec *timeout,
           int *uaddr2, int val3)
 {
+  static parlib_once_t once = PARLIB_ONCE_INIT;
+
+  parlib_run_once(&once, futex_init, NULL);
   // Round to the nearest micro-second
   uint64_t us_timeout = (uint64_t)-1;
   assert(uaddr2 == NULL);
@@ -196,8 +199,6 @@ int futex(int *uaddr, int op, int val,
     us_timeout = timeout->tv_sec*1000000L + timeout->tv_nsec/1000L;
     assert(us_timeout > 0);
   }
-
-  run_once(futex_init());
   switch(op) {
     case FUTEX_WAIT:
       return futex_wait(uaddr, val, us_timeout);
index b2cb8d4..932ecc0 100644 (file)
@@ -1014,11 +1014,15 @@ int pthread_equal(pthread_t t1, pthread_t t2)
   return t1 == t2;
 }
 
-int pthread_once(pthread_once_t* once_control, void (*init_routine)(void))
-{
-  if (atomic_swap_u32(once_control, 1) == 0)
-    init_routine();
-  return 0;
+int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
+{
+       /* pthread_once's init routine doesn't take an argument, like parlibs.  This
+        * means the func will be run with an argument passed to it, but it'll be
+        * ignored. */
+       parlib_run_once(once_control, (void (*)(void *))init_routine, NULL);
+       /* The return for pthread_once isn't an error from the function, it's just
+        * an overall error.  Note pthread's init_routine() has no return value. */
+       return 0;
 }
 
 int pthread_barrier_init(pthread_barrier_t *b,
index ca5a3cc..5c245cf 100644 (file)
@@ -67,7 +67,7 @@ struct sysc_mgmt {
        struct event_queue                      *ev_q;
 };
 
-#define PTHREAD_ONCE_INIT 0
+#define PTHREAD_ONCE_INIT PARLIB_ONCE_INIT
 #define PTHREAD_BARRIER_SERIAL_THREAD 12345
 #define PTHREAD_MUTEX_INITIALIZER {0,0}
 #define PTHREAD_RWLOCK_INITIALIZER PTHREAD_MUTEX_INITIALIZER
@@ -149,7 +149,7 @@ typedef struct
        int sched_inherit;
 } pthread_attr_t;
 typedef int pthread_barrierattr_t;
-typedef int pthread_once_t;
+typedef parlib_once_t pthread_once_t;
 typedef dtls_key_t pthread_key_t;
 
 /* Akaros pthread extensions / hacks */