]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
onexit: add an on exit callback utility
authorNoah Watkins <noahwatkins@gmail.com>
Sat, 4 Jan 2014 19:32:51 +0000 (11:32 -0800)
committerNoah Watkins <noahwatkins@gmail.com>
Sat, 4 Jan 2014 20:54:03 +0000 (12:54 -0800)
Adds a class that executes registered callbacks in its destructor. Since
static duration objects have their destructors called when returning
from main or calling exit then this can be used as a replacement for
on_exit.

Signed-off-by: Noah Watkins <noahwatkins@gmail.com>
src/include/Makefile.am
src/include/on_exit.h [new file with mode: 0644]
src/test/Makefile.am
src/test/on_exit.cc [new file with mode: 0644]

index 26f98a800f3bb15d565c47786ee3a332b97da148..4e467e9fa8aaf17eabb4b9147c16240b7fefa69d 100644 (file)
@@ -80,4 +80,5 @@ noinst_HEADERS += \
        include/rbd/librbd.h \
        include/rbd/librbd.hpp\
        include/util.h\
-       include/stat.h
+       include/stat.h \
+       include/on_exit.h
diff --git a/src/include/on_exit.h b/src/include/on_exit.h
new file mode 100644 (file)
index 0000000..4bb8f2c
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef CEPH_ON_EXIT_H
+#define CEPH_ON_EXIT_H
+
+#include <pthread.h>
+#include <assert.h>
+#include <vector>
+
+/*
+ * Create a static instance at the file level to get callbacks called when the
+ * process exits via main() or exit().
+ */
+
+class OnExitManager {
+  public:
+    typedef void (*callback_t)(void *arg);
+
+    OnExitManager() {
+      assert(pthread_mutex_init(&lock_, NULL) == 0);
+    }
+
+    ~OnExitManager() {
+      pthread_mutex_lock(&lock_);
+      std::vector<struct cb>::iterator it;
+      for (it = funcs_.begin(); it != funcs_.end(); it++) {
+        it->func(it->arg);
+      }
+      funcs_.clear();
+      pthread_mutex_unlock(&lock_);
+    }
+
+    void add_callback(callback_t func, void *arg) {
+      pthread_mutex_lock(&lock_);
+      struct cb callback = { func, arg };
+      funcs_.push_back(callback);
+      pthread_mutex_unlock(&lock_);
+    }
+
+  private:
+    struct cb {
+      callback_t func;
+      void *arg;
+    };
+
+    std::vector<struct cb> funcs_;
+    pthread_mutex_t lock_;
+};
+
+#endif
index 5faf9ac49fd1e83c2d738a928091634dbc3ed755..5e8ef8cd8872ffd604b774ce77e177ec222205a9 100644 (file)
@@ -602,6 +602,10 @@ unittest_texttable_LDADD = $(LIBCOMMON) $(UNITTEST_LDADD)
 unittest_texttable_CXXFLAGS = $(UNITTEST_CXXFLAGS)
 check_PROGRAMS += unittest_texttable
 
+unittest_on_exit_SOURCES = test/on_exit.cc
+unittest_on_exit_LDADD = $(PTHREAD_LIBS)
+check_PROGRAMS += unittest_on_exit
+
 check_SCRIPTS += test/pybind/test_ceph_argparse.py
 
 if WITH_RADOSGW
diff --git a/src/test/on_exit.cc b/src/test/on_exit.cc
new file mode 100644 (file)
index 0000000..45d22b2
--- /dev/null
@@ -0,0 +1,111 @@
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "include/on_exit.h"
+
+#ifndef MAP_ANONYMOUS
+# ifdef MAP_ANON
+#  define MAP_ANONYMOUS MAP_ANON
+# else
+#  error "Don't know how to create anonymous mmap"
+# endif
+#endif
+
+static int func_scope_val;
+
+static void add(void *incp)
+{
+  func_scope_val += *((int*)incp);
+}
+
+static void func_scope(void)
+{
+  OnExitManager mgr;
+
+  int *inc_1 = (int*)malloc(sizeof(*inc_1));
+  *inc_1 = 5;
+  mgr.add_callback(add, inc_1);
+
+  int *inc_2 = (int*)malloc(sizeof(*inc_2));
+  *inc_2 = 3;
+  mgr.add_callback(add, inc_2);
+}
+
+// shared between processes
+static int *shared_val;
+
+#define MAIN_SCOPE_VAL 0x1111111
+static OnExitManager main_scope_mgr;
+static void main_scope_cb(void *val)
+{
+  *shared_val = *((int*)val);
+}
+
+#define EXIT_FUNC_VAL 0x22222222
+static OnExitManager exit_func_mgr;
+static void exit_func_cb(void *val)
+{
+  *shared_val = *((int*)val);
+}
+
+static void call_exit()
+{
+  exit(3);
+}
+
+int main(int argc, char **argv)
+{
+  // test basic function scope behavior
+  assert(func_scope_val == 0);
+  func_scope();
+  assert(func_scope_val == 8);
+
+  // shared mem for exit tests
+  shared_val = (int*)mmap(NULL, sizeof(int),
+      PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, 0, 0);
+  assert(shared_val != MAP_FAILED);
+
+  // test normal exit returning from main
+  *shared_val = 0;
+  int pid = fork();
+  assert(pid >= 0);
+  if (pid) {
+    int status;
+    assert(pid == waitpid(pid, &status, 0));
+    assert(status == 0);
+    assert(*shared_val == MAIN_SCOPE_VAL);
+  } else {
+    // child adds a callback to the static scope callback manager and then
+    // exits by returning from main. The parent checks the value after the
+    // child exits via the memory map.
+    assert(*shared_val == 0);
+    int *new_val = (int*)malloc(sizeof(*new_val));
+    *new_val = MAIN_SCOPE_VAL;
+    main_scope_mgr.add_callback(main_scope_cb, new_val);
+    return 0;
+  }
+
+  // test exit via exit()
+  *shared_val = 0;
+  pid = fork();
+  assert(pid >= 0);
+  if (pid) {
+    int status;
+    assert(pid == waitpid(pid, &status, 0));
+    assert(WEXITSTATUS(status) == 3);
+    assert(*shared_val == EXIT_FUNC_VAL);
+  } else {
+    // child adds a callback to the static scope callback manager and then
+    // exits via exit().
+    assert(*shared_val == 0);
+    int *new_val = (int*)malloc(sizeof(*new_val));
+    *new_val = EXIT_FUNC_VAL;
+    exit_func_mgr.add_callback(exit_func_cb, new_val);
+    call_exit();
+    assert(0);
+  }
+
+  return 0;
+}