From: Noah Watkins Date: Sat, 4 Jan 2014 19:32:51 +0000 (-0800) Subject: onexit: add an on exit callback utility X-Git-Tag: v0.77~41^2~2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=2181d25f7a07803227fc7ad0713ecf19b0507342;p=ceph.git onexit: add an on exit callback utility 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 --- diff --git a/src/include/Makefile.am b/src/include/Makefile.am index 26f98a800f3..4e467e9fa8a 100644 --- a/src/include/Makefile.am +++ b/src/include/Makefile.am @@ -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 index 00000000000..4bb8f2c0687 --- /dev/null +++ b/src/include/on_exit.h @@ -0,0 +1,48 @@ +#ifndef CEPH_ON_EXIT_H +#define CEPH_ON_EXIT_H + +#include +#include +#include + +/* + * 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::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 funcs_; + pthread_mutex_t lock_; +}; + +#endif diff --git a/src/test/Makefile.am b/src/test/Makefile.am index 5faf9ac49fd..5e8ef8cd887 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -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 index 00000000000..45d22b23dc2 --- /dev/null +++ b/src/test/on_exit.cc @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include +#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; +}