about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2013-02-15 02:40:50 +0000
committerEric Wong <normalperson@yhbt.net>2013-02-15 02:40:50 +0000
commit5629899a12649b9b21f41efc29b92adbd82afe6c (patch)
tree8f97f684d65356623af0610e5587bebaa9af6129
parent44f4f76d06899b1a0e4719671a4fde3c0851764a (diff)
downloadcmogstored-5629899a12649b9b21f41efc29b92adbd82afe6c.tar.gz
This will inform the user of why cmogstored may be slow
to start, since we need the mountlist to be populated at
startup.

We also throw a pthread_cancel() in there to load libgcc_s under
glibc, so we can avoid loading libgcc_s once we're under FD pressure.
This makes test/http_idle_expire.rb more reliable.
-rw-r--r--Makefile.am2
-rw-r--r--cmogstored.h3
-rw-r--r--m4/.gitignore3
-rw-r--r--m4/gnulib-cache.m43
-rw-r--r--mnt.c81
-rw-r--r--sig.c21
6 files changed, 103 insertions, 10 deletions
diff --git a/Makefile.am b/Makefile.am
index 1e2b598..6fa520c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,7 @@
 ACLOCAL_AMFLAGS = -I m4
 AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib
 AM_CFLAGS = $(WARN_CFLAGS) $(PTHREAD_CFLAGS)
-AM_LDFLAGS = $(LIBGNU_LIBDEPS)
+AM_LDFLAGS = $(LIBGNU_LIBDEPS) $(LIB_CLOCK_GETTIME)
 SUBDIRS = lib
 
 # slow.mk is auto-generated by the maintainer (see GNUmakefile)
diff --git a/cmogstored.h b/cmogstored.h
index 2a31857..393cecb 100644
--- a/cmogstored.h
+++ b/cmogstored.h
@@ -20,6 +20,7 @@
 #include <sys/uio.h>
 #include <sys/mman.h>
 #include <sys/types.h>
+#include <sys/select.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
@@ -62,6 +63,7 @@
 #include "gc.h"
 #include "nproc.h"
 #include "findprog.h"
+#include "timespec.h"
 
 #include "gcc.h"
 #include "util.h"
@@ -412,6 +414,7 @@ enum mog_next mog_queue_step(struct mog_fd *mfd) MOG_CHECK;
 /* sig.c */
 void mog_intr_disable(void);
 void mog_intr_enable(void);
+void mog_sleep(long seconds);
 
 /* file.c */
 struct mog_fd * mog_file_open_read(struct mog_svc *, char *path);
diff --git a/m4/.gitignore b/m4/.gitignore
index 391f27d..a0934c7 100644
--- a/m4/.gitignore
+++ b/m4/.gitignore
@@ -175,3 +175,6 @@
 /eaccess.m4
 /findprog.m4
 /stpcpy.m4
+/clock_time.m4
+/gettime.m4
+/timespec.m4
diff --git a/m4/gnulib-cache.m4 b/m4/gnulib-cache.m4
index 1eeafec..03015fb 100644
--- a/m4/gnulib-cache.m4
+++ b/m4/gnulib-cache.m4
@@ -27,7 +27,7 @@
 
 
 # Specification in the form of a command-line invocation:
-#   gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --with-tests --avoid=accept --avoid=accept4 --avoid=alloca --avoid=fstatat --avoid=getcwd --avoid=ioctl --avoid=openat --avoid=read --avoid=sleep --avoid=write --no-conditional-dependencies --no-libtool --macro-prefix=gl argp base64 canonicalize crypto/gc-md5 crypto/gc-sha1 dprintf error findprog git-version-gen hash mempcpy minmax mountlist nonblocking nproc pipe2 progname random_r verify warnings xvasprintf
+#   gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --with-tests --avoid=accept --avoid=accept4 --avoid=alloca --avoid=fstatat --avoid=getcwd --avoid=ioctl --avoid=openat --avoid=read --avoid=sleep --avoid=write --no-conditional-dependencies --no-libtool --macro-prefix=gl argp base64 canonicalize crypto/gc-md5 crypto/gc-sha1 dprintf error findprog gettime git-version-gen hash mempcpy minmax mountlist nonblocking nproc pipe2 progname random_r verify warnings xvasprintf
 
 # Specification in the form of a few gnulib-tool.m4 macro invocations:
 gl_LOCAL_DIR([])
@@ -40,6 +40,7 @@ gl_MODULES([
   dprintf
   error
   findprog
+  gettime
   git-version-gen
   hash
   mempcpy
diff --git a/mnt.c b/mnt.c
index 8b34255..57f5e07 100644
--- a/mnt.c
+++ b/mnt.c
@@ -8,6 +8,11 @@
  */
 #include "cmogstored.h"
 
+struct init_args {
+        pthread_mutex_t cond_lock;
+        pthread_cond_t cond;
+};
+
 static pthread_mutex_t by_dev_lock = PTHREAD_MUTEX_INITIALIZER;
 
 /*
@@ -113,6 +118,73 @@ skip:
         }
 }
 
+/* runs inside a thread, this is called at startup before daemonization */
+static void * init_once(void *ptr)
+{
+        struct init_args *ia = ptr;
+
+        CHECK(int, 0, pthread_mutex_lock(&by_dev_lock) );
+        assert(by_dev == NULL &&
+               "by_dev exists during initialization");
+        by_dev = mnt_new(7);
+        mnt_populate(by_dev);
+        CHECK(int, 0, pthread_mutex_unlock(&by_dev_lock) );
+
+        /* wake up parent thread, this tells parent to cancel us */
+        CHECK(int, 0, pthread_mutex_lock(&ia->cond_lock));
+        CHECK(int, 0, pthread_cond_signal(&ia->cond));
+        CHECK(int, 0, pthread_mutex_unlock(&ia->cond_lock));
+
+        mog_sleep(-1); /* wait for cancellation */
+        assert(0 && "init_once did not get cancelled");
+        return NULL;
+}
+
+/* once-only initialization */
+static void timed_init_once(void)
+{
+        pthread_t thr;
+        unsigned long tries;
+        struct init_args ia = {
+                .cond_lock = PTHREAD_MUTEX_INITIALIZER,
+                .cond = PTHREAD_COND_INITIALIZER
+        };
+
+        CHECK(int, 0, pthread_mutex_lock(&ia.cond_lock));
+        CHECK(int, 0, pthread_create(&thr, NULL, init_once, &ia));
+
+        for (tries = 1; ; tries++) {
+                struct timespec ts;
+                int rc;
+
+                gettime(&ts);
+                ts.tv_sec += 5;
+                rc = pthread_cond_timedwait(&ia.cond, &ia.cond_lock, &ts);
+
+                if (rc == 0)
+                        break;
+                if (rc == ETIMEDOUT)
+                        warn("still populating mountlist (tries: %lu)", tries);
+                else if (rc == EINTR)
+                        continue;
+                else
+                        assert(0 && "unhandled pthread_cond_timedwait failure");
+        }
+        CHECK(int, 0, pthread_mutex_unlock(&ia.cond_lock));
+
+        /*
+         * this will load libgcc_s under glibc, we want to do this early
+         * in process lifetime to prevent load failures if we are under
+         * FD pressure later on.
+         */
+        CHECK(int, 0, pthread_cancel(thr));
+
+        CHECK(int, 0, pthread_join(thr, NULL));
+        CHECK(int, 0, pthread_cond_destroy(&ia.cond));
+        CHECK(int, 0, pthread_mutex_destroy(&ia.cond_lock));
+        atexit(mnt_atexit);
+}
+
 void mog_mnt_refresh(void)
 {
         Hash_table *new, *old;
@@ -146,14 +218,7 @@ void mog_mnt_refresh(void)
                 mog_iou_cleanup_finish();
                 hash_free(old);
         } else {
-                /* once-only initialization */
-                CHECK(int, 0, pthread_mutex_lock(&by_dev_lock) );
-                assert(by_dev == NULL &&
-                       "by_dev exists during initialization");
-                by_dev = mnt_new(7);
-                mnt_populate(by_dev);
-                CHECK(int, 0, pthread_mutex_unlock(&by_dev_lock) );
-                atexit(mnt_atexit);
+                timed_init_once();
         }
 
         CHECK(int, 0, pthread_mutex_unlock(&refresh_lock) );
diff --git a/sig.c b/sig.c
index 63a40fe..7fa2708 100644
--- a/sig.c
+++ b/sig.c
@@ -23,3 +23,24 @@ void mog_intr_enable(void)
         CHECK(int, 0, sigemptyset(&set));
         CHECK(int, 0, pthread_sigmask(SIG_SETMASK, &set, NULL));
 }
+
+/* thread-safe, interruptible sleep, negative seconds -> sleep forever */
+void mog_sleep(long seconds)
+{
+        sigset_t set;
+        struct timespec ts;
+        struct timespec *tsp;
+
+        if (seconds < 0) {
+                tsp = NULL;
+        } else {
+                ts.tv_sec = seconds;
+                ts.tv_nsec = 0;
+                tsp = &ts;
+        }
+
+        CHECK(int, 0, sigemptyset(&set));
+        if (pselect(0, NULL, NULL, NULL, tsp, &set) < 0)
+                assert((errno == EINTR || errno == ENOMEM) &&
+                       "BUG in pselect usage");
+}