From 44e92dc8b900efda68f3ce5c8ecb1a7fbe80a6ee Mon Sep 17 00:00:00 2001 From: William Brown Date: Oct 25 2019 02:34:26 +0000 Subject: Ticket 50669 - remove nunc-stans Bug Description: We have been attempting to modernise our connection code for a long time - one attempt was nunc-stans. However after a series of attempts to integrate it, and multiple failures we discussed this in the listed email thread and decided to remove this. https://lists.fedoraproject.org/archives/list/389-devel@lists.fedoraproject.org/thread/3JRQQRIPOVDLLRD2QMF2PWNHJGZFUDCC/ Fix Description: rm -r src/nunc-stans https://pagure.io/389-ds-base/issue/50669 Author: William Brown Review by: mreynolds (Thanks!) --- diff --git a/Makefile.am b/Makefile.am index 6593be3..d847604 100644 --- a/Makefile.am +++ b/Makefile.am @@ -68,9 +68,6 @@ else SDS_INCLUDES = -I$(srcdir)/src/libsds/include/ -I$(srcdir)/src/libsds/external/ endif -NUNCSTANS_INCLUDES = -I$(srcdir)/src/nunc-stans/include/ -NUNC_STANS_ON = 1 - SVRCORE_INCLUDES = -I$(srcdir)/src/svrcore/src/ # the -U undefines these symbols - should use the corresponding DS_ ones instead - see configure.ac @@ -142,8 +139,6 @@ DSPLUGIN_CPPFLAGS = $(DS_DEFINES) $(DS_INCLUDES) $(PATH_DEFINES) $(SYSTEMD_DEFIN DSINTERNAL_CPPFLAGS = -I$(srcdir)/include/ldaputil # Flags for Datastructure Library SDS_CPPFLAGS = $(SDS_INCLUDES) $(NSPR_INCLUDES) -# Flags for nunc-stans -NUNCSTANS_CPPFLAGS = $(NSPR_INCLUDES) $(NUNCSTANS_INCLUDES) $(SDS_INCLUDES) $(EVENT_CFLAGS) #------------------------ # Linker Flags @@ -345,7 +340,7 @@ bin_PROGRAMS = dbscan \ # based on defines # ---------------------------------------------------------------------------------------- -server_LTLIBRARIES = libsds.la libnunc-stans.la libslapd.la libldaputil.la libns-dshttpd.la +server_LTLIBRARIES = libsds.la libslapd.la libldaputil.la libns-dshttpd.la lib_LTLIBRARIES = libsvrcore.la @@ -583,9 +578,7 @@ dist_noinst_HEADERS = \ src/libsds/sds/bpt/bpt.h \ src/libsds/sds/bpt_cow/bpt_cow.h \ src/libsds/sds/queue/queue.h \ - src/libsds/sds/ht/ht.h \ - src/nunc-stans/ns/ns_event_fw.h \ - src/nunc-stans/ns/ns_private.h + src/libsds/sds/ht/ht.h if ATOMIC_QUEUE_OPERATIONS dist_noinst_HEADERS += \ @@ -606,8 +599,7 @@ dist_noinst_HEADERS += \ test/test_slapd.h \ src/libsds/test/test_sds.h \ src/libsds/test/benchmark.h \ - src/libsds/test/benchmark_par.h \ - src/nunc-stans/test/test_nuncstans_stress.h + src/libsds/test/benchmark_par.h endif dist_noinst_DATA = \ @@ -908,7 +900,6 @@ mib_DATA = ldap/servers/snmp/redhat-directory.mib pkgconfig_DATA = src/pkgconfig/dirsrv.pc \ src/pkgconfig/libsds.pc \ - src/pkgconfig/nunc-stans.pc \ src/pkgconfig/svrcore.pc #------------------------ @@ -918,7 +909,6 @@ serverinc_HEADERS = ldap/servers/plugins/replication/repl-session-plugin.h \ ldap/servers/slapd/slapi_pal.h \ ldap/servers/slapd/slapi-plugin.h \ ldap/servers/plugins/replication/winsync-plugin.h \ - src/nunc-stans/include/nunc-stans.h \ src/libsds/include/sds.h include_HEADERS = src/svrcore/src/svrcore.h @@ -1221,20 +1211,6 @@ libsds_la_SOURCES += \ endif #------------------------ -# libnunc-stans -#------------------------ - -libnunc_stans_la_SOURCES = src/nunc-stans/ns/ns_thrpool.c \ - src/nunc-stans/ns/ns_event_fw_event.c - -# EDIT THESE AT THE TOP OF THE MAKE FILE!!! -libnunc_stans_la_CPPFLAGS = $(AM_CPPFLAGS) $(NUNCSTANS_CPPFLAGS) -libnunc_stans_la_LDFLAGS = $(AM_LDFLAGS) $(NUNCSTANS_LDFLAGS) -libnunc_stans_la_LIBADD = libsds.la -libnunc_stans_la_DEPENDENCIES = libsds.la - - -#------------------------ # libns-dshttpd #------------------------ libns_dshttpd_la_SOURCES = lib/libaccess/access_plhash.cpp \ @@ -2094,9 +2070,9 @@ ns_slapd_SOURCES = ldap/servers/slapd/abandon.c \ $(GETSOCKETPEER) ns_slapd_CPPFLAGS = $(AM_CPPFLAGS) $(DSPLUGIN_CPPFLAGS) $(SASL_CFLAGS) $(SVRCORE_INCLUDES) -ns_slapd_LDADD = libnunc-stans.la libslapd.la libldaputil.la libsvrcore.la $(LDAPSDK_LINK) $(NSS_LINK) $(LIBADD_DL) \ +ns_slapd_LDADD = libslapd.la libldaputil.la libsvrcore.la $(LDAPSDK_LINK) $(NSS_LINK) $(LIBADD_DL) \ $(NSPR_LINK) $(SASL_LINK) $(LIBNSL) $(LIBSOCKET) $(THREADLIB) $(SYSTEMD_LIBS) $(EVENT_LINK) -ns_slapd_DEPENDENCIES = libslapd.la libnunc-stans.la libldaputil.la +ns_slapd_DEPENDENCIES = libslapd.la libldaputil.la # We need to link ns-slapd with the C++ compiler on HP-UX since we load # some C++ shared libraries (such as icu). if HPUX @@ -2133,15 +2109,10 @@ if ENABLE_CMOCKA check_PROGRAMS = test_slapd \ test_libsds \ benchmark_sds \ - benchmark_par_sds \ - test_nuncstans \ - test_nuncstans_stress_small \ - test_nuncstans_stress_large + benchmark_par_sds # Mark all check programs for testing TESTS = test_slapd \ - test_libsds \ - test_nuncstans \ - test_nuncstans_stress_small + test_libsds test_slapd_SOURCES = test/main.c \ test/libslapd/test.c \ @@ -2193,22 +2164,6 @@ benchmark_par_sds_LDFLAGS = $(ASAN_CFLAGS) $(MSAN_CFLAGS) $(TSAN_CFLAGS) $(UBSAN benchmark_par_sds_LDADD = libsds.la $(NSPR_LINK) benchmark_par_sds_CPPFLAGS = $(AM_CPPFLAGS) $(CMOCKA_INCLUDES) $(SDS_CPPFLAGS) $(DS_INCLUDES) -test_nuncstans_SOURCES = src/nunc-stans/test/test_nuncstans.c -test_nuncstans_CPPFLAGS = $(AM_CPPFLAGS) $(CMOCKA_INCLUDES) $(NUNCSTANS_CPPFLAGS) -test_nuncstans_LDADD = libnunc-stans.la libsds.la $(NSPR_LINK) -test_nuncstans_LDFLAGS = $(ASAN_CFLAGS) $(MSAN_CFLAGS) $(TSAN_CFLAGS) $(UBSAN_CFLAGS) $(PROFILING_LINKS) $(CMOCKA_LINKS) $(EVENT_LINK) - -test_nuncstans_stress_large_SOURCES = src/nunc-stans/test/test_nuncstans_stress_large.c src/nunc-stans/test/test_nuncstans_stress_core.c -test_nuncstans_stress_large_CPPFLAGS = $(AM_CPPFLAGS) $(CMOCKA_INCLUDES) $(NUNCSTANS_CPPFLAGS) -test_nuncstans_stress_large_LDADD = libnunc-stans.la libsds.la $(NSPR_LINK) -test_nuncstans_stress_large_LDFLAGS = $(ASAN_CFLAGS) $(MSAN_CFLAGS) $(TSAN_CFLAGS) $(UBSAN_CFLAGS) $(PROFILING_LINKS) $(CMOCKA_LINKS) $(EVENT_LINK) - -test_nuncstans_stress_small_SOURCES = src/nunc-stans/test/test_nuncstans_stress_small.c src/nunc-stans/test/test_nuncstans_stress_core.c -test_nuncstans_stress_small_CPPFLAGS = $(AM_CPPFLAGS) $(CMOCKA_INCLUDES) $(NUNCSTANS_CPPFLAGS) -test_nuncstans_stress_small_LDADD = libnunc-stans.la libsds.la $(NSPR_LINK) -test_nuncstans_stress_small_LDFLAGS = $(ASAN_CFLAGS) $(MSAN_CFLAGS) $(TSAN_CFLAGS) $(UBSAN_CFLAGS) $(PROFILING_LINKS) $(CMOCKA_LINKS) $(EVENT_LINK) - - endif #------------------------ # end cmocka tests diff --git a/configure.ac b/configure.ac index e2c4814..6cf3c3a 100644 --- a/configure.ac +++ b/configure.ac @@ -862,9 +862,6 @@ AC_SUBST(ldaplib_defs) AC_SUBST(ldaptool_bindir) AC_SUBST(ldaptool_opts) AC_SUBST(plainldif_opts) -AC_SUBST(nunc_stans_inc) -AC_SUBST(nunc_stans_lib) -AC_SUBST(nunc_stans_libdir) AC_SUBST(localrundir) AC_SUBST(brand) diff --git a/ldap/servers/slapd/connection.c b/ldap/servers/slapd/connection.c index 300e0b7..fb22866 100644 --- a/ldap/servers/slapd/connection.c +++ b/ldap/servers/slapd/connection.c @@ -711,12 +711,6 @@ connection_release_nolock_ext(Connection *conn, int release_only) } else { conn->c_refcnt--; - if (!release_only && (conn->c_refcnt == 1) && (conn->c_flags & CONN_FLAG_CLOSING)) { - /* if refcnt == 1 usually means only the active connection list has a ref */ - /* refcnt == 0 means conntable just dropped the last ref */ - ns_connection_post_io_or_closing(conn); - } - return 0; } } @@ -1351,10 +1345,6 @@ connection_make_readable_nolock(Connection *conn) conn->c_gettingber = 0; slapi_log_err(SLAPI_LOG_CONNS, "connection_make_readable_nolock", "making readable conn %" PRIu64 " fd=%d\n", conn->c_connid, conn->c_sd); - if (!(conn->c_flags & CONN_FLAG_CLOSING)) { - /* if the connection is closing, try the close in connection_release_nolock */ - ns_connection_post_io_or_closing(conn); - } } /* @@ -1496,10 +1486,8 @@ connection_threadmain() int replication_connection = 0; /* If this connection is from a replication supplier, we want to ensure that operation processing is serialized */ int doshutdown = 0; int maxthreads = 0; - int enable_nunc_stans = 0; long bypasspollcnt = 0; - enable_nunc_stans = config_get_enable_nunc_stans(); #if defined(hpux) /* Arrange to ignore SIGPIPE signals. */ SIGNAL(SIGPIPE, SIG_IGN); @@ -1730,7 +1718,7 @@ connection_threadmain() * when using nunc-stans - it is supposed to be an optimization but turns out * to not be the opposite with nunc-stans */ - } else if (!enable_nunc_stans) { /* more data in conn - just put back on work_q - bypass poll */ + } else { /* more data in conn - just put back on work_q - bypass poll */ bypasspollcnt++; pthread_mutex_lock(&(conn->c_mutex)); /* don't do this if it would put us over the max threads per conn */ @@ -2327,9 +2315,6 @@ disconnect_server_nomutex_ext(Connection *conn, PRUint64 opconnid, int opid, PRE } } } - if (schedule_closure_job) { - ns_connection_post_io_or_closing(conn); /* make sure event loop wakes up and closes this conn */ - } } else { slapi_log_err(SLAPI_LOG_CONNS, "disconnect_server_nomutex_ext", "Not setting conn %d to be disconnected: %s\n", diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c index 345e292..7c6d35f 100644 --- a/ldap/servers/slapd/daemon.c +++ b/ldap/servers/slapd/daemon.c @@ -99,8 +99,6 @@ typedef struct listener_info static size_t listeners = 0; /* number of listener sockets */ static listener_info *listener_idxs = NULL; /* array of indexes of listener sockets in the ct->fd array */ -static int enable_nunc_stans = 0; /* if nunc-stans is set to enabled, set to 1 in slapd_daemon */ - #define SLAPD_POLL_LISTEN_READY(xxflagsxx) (xxflagsxx & PR_POLL_READ) static int get_configured_connection_table_size(void); @@ -150,22 +148,12 @@ accept_and_configure(int s __attribute__((unused)), PRFileDesc *pr_acceptfd, PRN * This is the shiny new re-born daemon function, without all the hair */ static int handle_new_connection(Connection_Table *ct, int tcps, PRFileDesc *pr_acceptfd, int secure, int local, Connection **newconn); -static void ns_handle_new_connection(struct ns_job_t *job); -static void ns_handle_closure(struct ns_job_t *job); static void handle_pr_read_ready(Connection_Table *ct, PRIntn num_poll); static int clear_signal(struct POLL_STRUCT *fds); static void unfurl_banners(Connection_Table *ct, daemon_ports_t *ports, PRFileDesc **n_tcps, PRFileDesc **s_tcps, PRFileDesc **i_unix); static int write_pid_file(void); static int init_shutdown_detect(void); -#define NS_HANDLER_NEW_CONNECTION 0 -#define NS_HANDLER_READ_CONNECTION 1 -#define NS_HANDLER_CLOSE_CONNECTION 2 -static ns_job_func_t ns_handlers[] = { - ns_handle_new_connection, - ns_handle_pr_read_ready, - ns_handle_closure -}; /* Globals which are used to store the sockets between * calls to daemon_pre_setuid_init() and the daemon thread * creation. */ @@ -804,7 +792,7 @@ convert_pbe_des_to_aes(void) } void -slapd_daemon(daemon_ports_t *ports, ns_thrpool_t *tp) +slapd_daemon(daemon_ports_t *ports) { /* We are passed some ports---one for regular connections, one * for SSL connections, one for ldapi connections. @@ -825,7 +813,13 @@ slapd_daemon(daemon_ports_t *ports, ns_thrpool_t *tp) int connection_table_size = get_configured_connection_table_size(); the_connection_table = connection_table_new(connection_table_size); - enable_nunc_stans = config_get_enable_nunc_stans(); + /* + * Log a warning if we detect nunc-stans + */ + if (config_get_enable_nunc_stans()) { + slapi_log_err(SLAPI_LOG_WARNING, "slapd_daemon", "cn=config: nsslapd-enable-nunc-stans is on. nunc-stans has been deprecated and this flag is now ignored.\n"); + slapi_log_err(SLAPI_LOG_WARNING, "slapd_daemon", "cn=config: nsslapd-enable-nunc-stans should be set to off or deleted from cn=config.\n"); + } #ifdef RESOLVER_NEEDS_LOW_FILE_DESCRIPTORS /* @@ -848,11 +842,9 @@ slapd_daemon(daemon_ports_t *ports, ns_thrpool_t *tp) i_unix = ports->i_socket; #endif /* ENABLE_LDAPI */ - if (!enable_nunc_stans) { - createsignalpipe(); - /* Setup our signal interception. */ - init_shutdown_detect(); - } + createsignalpipe(); + /* Setup our signal interception. */ + init_shutdown_detect(); if ( (n_tcps == NULL) && @@ -961,17 +953,6 @@ slapd_daemon(daemon_ports_t *ports, ns_thrpool_t *tp) */ convert_pbe_des_to_aes(); - if (enable_nunc_stans && !g_get_shutdown()) { - setup_pr_read_pds(the_connection_table, n_tcps, s_tcps, i_unix, &num_poll); - for (size_t ii = 0; ii < listeners; ++ii) { - listener_idxs[ii].ct = the_connection_table; /* to pass to handle_new_connection */ - ns_result_t result = ns_add_io_job(tp, listener_idxs[ii].listenfd, NS_JOB_ACCEPT | NS_JOB_PERSIST | NS_JOB_PRESERVE_FD, - ns_handlers[NS_HANDLER_NEW_CONNECTION], &listener_idxs[ii], &(listener_idxs[ii].ns_job)); - if (result != NS_SUCCESS) { - slapi_log_err(SLAPI_LOG_CRIT, "slapd_daemon", "ns_add_io_job failed to create add acceptor %d\n", result); - } - } - } /* Now we write the pid file, indicating that the server is finally and listening for connections */ write_pid_file(); @@ -985,58 +966,32 @@ slapd_daemon(daemon_ports_t *ports, ns_thrpool_t *tp) (unsigned long)getpid()); #endif - if (enable_nunc_stans) { - if (ns_thrpool_wait(tp)) { - slapi_log_err(SLAPI_LOG_ERR, - "slapd-daemon", "ns_thrpool_wait failed errno %d (%s)\n", errno, - slapd_system_strerror(errno)); - } - - /* we have exited from ns_thrpool_wait. This means we are shutting down! */ - /* Please see https://firstyear.fedorapeople.org/nunc-stans/md_docs_job-safety.html */ - /* tldr is shutdown needs to run first to allow job_done on an ARMED job */ - for (uint64_t i = 0; i < listeners; i++) { - PRStatus shutdown_status; + /* The meat of the operation is in a loop on a call to select */ + while (!g_get_shutdown()) { + int select_return = 0; + PRErrorCode prerr; - if (listener_idxs[i].ns_job) { - shutdown_status = ns_job_done(listener_idxs[i].ns_job); - if (shutdown_status != PR_SUCCESS) { - slapi_log_err(SLAPI_LOG_CRIT, "ns_set_shutdown", "Failed to shutdown listener idx %" PRIu64 " !\n", i); - } - PR_ASSERT(shutdown_status == PR_SUCCESS); - } else { - slapi_log_err(SLAPI_LOG_CRIT, "slapd_daemon", "Listeners uninitialized. Possibly the server was shutdown while starting\n"); - } - listener_idxs[i].ns_job = NULL; - } - } else { - /* The meat of the operation is in a loop on a call to select */ - while (!g_get_shutdown()) { - int select_return = 0; - PRErrorCode prerr; - - setup_pr_read_pds(the_connection_table, n_tcps, s_tcps, i_unix, &num_poll); - select_return = POLL_FN(the_connection_table->fd, num_poll, pr_timeout); - switch (select_return) { - case 0: /* Timeout */ - break; - case -1: /* Error */ - prerr = PR_GetError(); - slapi_log_err(SLAPI_LOG_TRACE, "slapd_daemon", "PR_Poll() failed, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", - prerr, slapd_system_strerror(prerr)); - break; - default: /* either a new connection or some new data ready */ - /* handle new connections from the listeners */ - handle_listeners(the_connection_table); - /* handle new data ready */ - handle_pr_read_ready(the_connection_table, connection_table_size); - clear_signal(the_connection_table->fd); - break; - } + setup_pr_read_pds(the_connection_table, n_tcps, s_tcps, i_unix, &num_poll); + select_return = POLL_FN(the_connection_table->fd, num_poll, pr_timeout); + switch (select_return) { + case 0: /* Timeout */ + break; + case -1: /* Error */ + prerr = PR_GetError(); + slapi_log_err(SLAPI_LOG_TRACE, "slapd_daemon", "PR_Poll() failed, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + prerr, slapd_system_strerror(prerr)); + break; + default: /* either a new connection or some new data ready */ + /* handle new connections from the listeners */ + handle_listeners(the_connection_table); + /* handle new data ready */ + handle_pr_read_ready(the_connection_table, connection_table_size); + clear_signal(the_connection_table->fd); + break; } - /* We get here when the server is shutting down */ - /* Do what we have to do before death */ } + /* We get here when the server is shutting down */ + /* Do what we have to do before death */ #ifdef WITH_SYSTEMD sd_notify(0, "STOPPING=1"); @@ -1103,19 +1058,6 @@ slapd_daemon(daemon_ports_t *ports, ns_thrpool_t *tp) */ connection_table_disconnect_all(the_connection_table); - /* - * WARNING: Normally we should close the tp in main - * but because of issues in the current connection design - * we need to close it here to guarantee events won't fire! - * - * All the connection close jobs "should" complete before - * shutdown at least. - */ - if (enable_nunc_stans) { - ns_thrpool_shutdown(tp); - ns_thrpool_wait(tp); - } - if (!in_referral_mode) { /* signal tasks to start shutting down */ task_cancel_all(); @@ -1130,33 +1072,31 @@ slapd_daemon(daemon_ports_t *ports, ns_thrpool_t *tp) threads = g_get_active_threadcnt(); while (threads > 0) { - if (!enable_nunc_stans) { - PRPollDesc xpd; - char x; - int spe = 0; - - /* try to read from the signal pipe, in case threads are - * blocked on it. */ - xpd.fd = signalpipe[0]; - xpd.in_flags = PR_POLL_READ; - xpd.out_flags = 0; - spe = PR_Poll(&xpd, 1, PR_INTERVAL_NO_WAIT); - if (spe > 0) { - spe = PR_Read(signalpipe[0], &x, 1); - if (spe < 0) { - PRErrorCode prerr = PR_GetError(); - slapi_log_err(SLAPI_LOG_ERR, "slapd_daemon", "listener could not clear signal pipe, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", - prerr, slapd_system_strerror(prerr)); - break; - } - } else if (spe == -1) { + PRPollDesc xpd; + char x; + int spe = 0; + + /* try to read from the signal pipe, in case threads are + * blocked on it. */ + xpd.fd = signalpipe[0]; + xpd.in_flags = PR_POLL_READ; + xpd.out_flags = 0; + spe = PR_Poll(&xpd, 1, PR_INTERVAL_NO_WAIT); + if (spe > 0) { + spe = PR_Read(signalpipe[0], &x, 1); + if (spe < 0) { PRErrorCode prerr = PR_GetError(); - slapi_log_err(SLAPI_LOG_ERR, "slapd_daemon", "PR_Poll() failed, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + slapi_log_err(SLAPI_LOG_ERR, "slapd_daemon", "listener could not clear signal pipe, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", prerr, slapd_system_strerror(prerr)); break; - } else { - /* no data */ } + } else if (spe == -1) { + PRErrorCode prerr = PR_GetError(); + slapi_log_err(SLAPI_LOG_ERR, "slapd_daemon", "PR_Poll() failed, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n", + prerr, slapd_system_strerror(prerr)); + break; + } else { + /* no data */ } DS_Sleep(PR_INTERVAL_NO_WAIT); if (threads != g_get_active_threadcnt()) { @@ -1226,9 +1166,6 @@ slapd_daemon(daemon_ports_t *ports, ns_thrpool_t *tp) int signal_listner() { - if (enable_nunc_stans) { - return (0); - } /* Replaces previous macro---called to bump the thread out of select */ if (write(writesignalpipe, "", 1) != 1) { /* this now means that the pipe is full @@ -1244,9 +1181,6 @@ signal_listner() static int clear_signal(struct POLL_STRUCT *fds) { - if (enable_nunc_stans) { - return 0; - } if (fds[FDS_SIGNAL_PIPE].out_flags & SLAPD_POLL_FLAGS) { char buf[200]; @@ -1300,14 +1234,12 @@ setup_pr_read_pds(Connection_Table *ct, PRFileDesc **n_tcps, PRFileDesc **s_tcps ct->c[i].c_fdi = SLAPD_INVALID_SOCKET_INDEX; } - if (!enable_nunc_stans) { - /* The fds entry for the signalpipe is always FDS_SIGNAL_PIPE (== 0) */ - count = FDS_SIGNAL_PIPE; - ct->fd[count].fd = signalpipe[0]; - ct->fd[count].in_flags = SLAPD_POLL_FLAGS; - ct->fd[count].out_flags = 0; - count++; - } + /* The fds entry for the signalpipe is always FDS_SIGNAL_PIPE (== 0) */ + count = FDS_SIGNAL_PIPE; + ct->fd[count].fd = signalpipe[0]; + ct->fd[count].in_flags = SLAPD_POLL_FLAGS; + ct->fd[count].out_flags = 0; + count++; /* The fds entry for n_tcps starts with n_tcps and less than n_tcpe */ ct->n_tcps = count; if (n_tcps != NULL && accept_new_connections) { @@ -1543,293 +1475,6 @@ handle_pr_read_ready(Connection_Table *ct, PRIntn num_poll __attribute__((unused } } -#define CONN_NEEDS_CLOSING(c) (c->c_flags & CONN_FLAG_CLOSING) || (c->c_sd == SLAPD_INVALID_SOCKET) -/* Used internally by ns_handle_closure and ns_handle_pr_read_ready. - * Returns 0 if the connection was successfully closed, or 1 otherwise. - * Must be called with the c->c_mutex locked. - */ -static int -ns_handle_closure_nomutex(Connection *c) -{ - int rc = 0; - PR_ASSERT(c->c_refcnt > 0); /* one for the conn active list, plus possible other threads */ - PR_ASSERT(CONN_NEEDS_CLOSING(c)); - if (connection_table_move_connection_out_of_active_list(c->c_ct, c)) { - /* not closed - another thread still has a ref */ - rc = 1; - /* reschedule closure job */ - ns_connection_post_io_or_closing(c); - } - return rc; -} -/* This function is called when the connection has been marked - * as closing and needs to be cleaned up. It will keep trying - * and re-arming itself until there are no references. - */ -static void -ns_handle_closure(struct ns_job_t *job) -{ - Connection *c = (Connection *)ns_job_get_data(job); - int do_yield = 0; - - pthread_mutex_lock(&(c->c_mutex)); - /* Assert we really have the right job state. */ - PR_ASSERT(job == c->c_job); - - connection_release_nolock_ext(c, 1); /* release ref acquired for event framework */ - PR_ASSERT(c->c_ns_close_jobs == 1); /* should be exactly 1 active close job - this one */ - c->c_ns_close_jobs--; /* this job is processing closure */ - /* Because handle closure will add a new job, we need to detach our current one. */ - c->c_job = NULL; - do_yield = ns_handle_closure_nomutex(c); - pthread_mutex_unlock(&(c->c_mutex)); - /* Remove this task now. */ - ns_job_done(job); - if (do_yield) { - /* closure not done - another reference still outstanding */ - /* yield thread after unlocking conn mutex */ - PR_Sleep(PR_INTERVAL_NO_WAIT); /* yield to allow other thread to release conn */ - } - return; -} - -/** - * Schedule more I/O for this connection, or make sure that it - * is closed in the event loop. - * - * caller must hold c_mutex - */ -void -ns_connection_post_io_or_closing(Connection *conn) -{ - struct timeval tv; - - if (!enable_nunc_stans) { - return; - } - - /* - * A job was already scheduled. - * Check if it is the appropriate one - */ - if (conn->c_job != NULL) { - if (connection_is_free(conn, 0)) { - PRStatus shutdown_status; - - /* The connection being freed, - * It means that ns_handle_closure already completed and the connection - * is no longer on the active list. - * The scheduled job is useless and scheduling a new one as well - */ - shutdown_status = ns_job_done(conn->c_job); - if (shutdown_status != PR_SUCCESS) { - slapi_log_err(SLAPI_LOG_CRIT, "ns_connection_post_io_or_closing", "Failed cancel a job on a freed connection %d !\n", conn->c_sd); - } - conn->c_job = NULL; - return; - } - if (CONN_NEEDS_CLOSING(conn)) { - if (ns_job_is_func(conn->c_job, ns_handlers[NS_HANDLER_CLOSE_CONNECTION])) { - /* Due to the closing state we would schedule a ns_handle_closure - * but one is already registered. - * Just return; - */ - slapi_log_err(SLAPI_LOG_CONNS, "ns_connection_post_io_or_closing", "Already ns_handle_closure " - "job in progress on conn %" PRIu64 " for fd=%d\n", - conn->c_connid, conn->c_sd); - return; - } else { - /* Due to the closing state we would schedule a ns_handle_closure - * but a different handler is registered. Stop it and schedule (below) ns_handle_closure - */ - ns_job_done(conn->c_job); - conn->c_job = NULL; - } - } else { - /* Here the connection is still active => ignore the call and return */ - if (ns_job_is_func(conn->c_job, ns_handlers[NS_HANDLER_READ_CONNECTION])) { - /* Connection is still active and a read_ready is already scheduled - * Likely a consequence of async operations - * Just let the current read_ready do its job - */ - slapi_log_err(SLAPI_LOG_CONNS, "ns_connection_post_io_or_closing", "Already ns_handle_pr_read_ready " - "job in progress on conn %" PRIu64 " for fd=%d\n", - conn->c_connid, conn->c_sd); - } else { - /* Weird situation where the connection is not flagged closing but ns_handle_closure - * is scheduled. - * We should not try to read it anymore - */ - PR_ASSERT(ns_job_is_func(conn->c_job, ns_handlers[NS_HANDLER_CLOSE_CONNECTION])); - } - return; - } - } - - /* At this point conn->c_job is NULL - * Either it was null when the function was called - * Or we cleared it (+ns_job_done) if the wrong (according - * to the connection state) handler was scheduled - * - * Now we need to determine which handler to schedule - */ - - if (CONN_NEEDS_CLOSING(conn)) { - /* there should only ever be 0 or 1 active closure jobs */ - PR_ASSERT((conn->c_ns_close_jobs == 0) || (conn->c_ns_close_jobs == 1)); - if (conn->c_ns_close_jobs) { - slapi_log_err(SLAPI_LOG_CONNS, "ns_connection_post_io_or_closing", "Already a close " - "job in progress on conn %" PRIu64 " for fd=%d\n", - conn->c_connid, conn->c_sd); - return; - } else { - conn->c_ns_close_jobs++; /* now 1 active closure job */ - connection_acquire_nolock_ext(conn, 1 /* allow acquire even when closing */); /* event framework now has a reference */ - /* Close the job asynchronously. Why? */ - ns_result_t job_result = ns_add_job(conn->c_tp, NS_JOB_TIMER, ns_handlers[NS_HANDLER_CLOSE_CONNECTION], conn, &(conn->c_job)); - if (job_result != NS_SUCCESS) { - if (job_result == NS_SHUTDOWN) { - slapi_log_err(SLAPI_LOG_INFO, "ns_connection_post_io_or_closing", "post closure job " - "for conn %" PRIu64 " for fd=%d failed to be added to event queue as server is shutting down\n", - conn->c_connid, conn->c_sd); - } else { - slapi_log_err(SLAPI_LOG_ERR, "ns_connection_post_io_or_closing", "post closure job " - "for conn %" PRIu64 " for fd=%d failed to be added to event queue %d\n", - conn->c_connid, conn->c_sd, job_result); - } - } else { - slapi_log_err(SLAPI_LOG_CONNS, "ns_connection_post_io_or_closing", "post closure job " - "for conn %" PRIu64 " for fd=%d\n", - conn->c_connid, conn->c_sd); - } - } - } else { - /* process event normally - wait for I/O until idletimeout */ - /* With nunc-stans there is a quirk. When we have idleTimeout of -1 - * which is set on some IPA bind dns for infinite, this causes libevent - * to *instantly* timeout. So if we detect < 0, we set 0 to this timeout, to - * catch all possible times that an admin could set. - */ - if (conn->c_idletimeout < 0) { - tv.tv_sec = 0; - } else { - tv.tv_sec = conn->c_idletimeout; - } - tv.tv_usec = 0; -#ifdef DEBUG - PR_ASSERT(0 == connection_acquire_nolock(conn)); -#else - if (connection_acquire_nolock(conn) != 0) { /* event framework now has a reference */ - /* - * This has already been logged as an error in ./ldap/servers/slapd/connection.c - * The error occurs when we get a connection in a closing state. - * For now we return, but there is probably a better way to handle the error case. - */ - return; - } -#endif - ns_result_t job_result = ns_add_io_timeout_job(conn->c_tp, conn->c_prfd, &tv, - NS_JOB_READ | NS_JOB_PRESERVE_FD, - ns_handlers[NS_HANDLER_READ_CONNECTION], conn, &(conn->c_job)); - if (job_result != NS_SUCCESS) { - if (job_result == NS_SHUTDOWN) { - slapi_log_err(SLAPI_LOG_INFO, "ns_connection_post_io_or_closing", "post I/O job for " - "conn %" PRIu64 " for fd=%d failed to be added to event queue as server is shutting down\n", - conn->c_connid, conn->c_sd); - } else { - slapi_log_err(SLAPI_LOG_ERR, "ns_connection_post_io_or_closing", "post I/O job for " - "conn %" PRIu64 " for fd=%d failed to be added to event queue %d\n", - conn->c_connid, conn->c_sd, job_result); - } - } else { - slapi_log_err(SLAPI_LOG_CONNS, "ns_connection_post_io_or_closing", "post I/O job for " - "conn %" PRIu64 " for fd=%d added to event queue\n", - conn->c_connid, conn->c_sd); - } - } - return; -} - -/* This function must be called without the thread flag, in the - * event loop. This function may free the connection. This can - * only be done in the event loop thread. - */ -void -ns_handle_pr_read_ready(struct ns_job_t *job) -{ - Connection *c = (Connection *)ns_job_get_data(job); - - pthread_mutex_lock(&(c->c_mutex)); - /* Assert we really have the right job state. */ - PR_ASSERT(job == c->c_job); - - /* On all code paths we remove the job, so set it null now */ - c->c_job = NULL; - - slapi_log_err(SLAPI_LOG_CONNS, "ns_handle_pr_read_ready", "activity on conn %" PRIu64 " for fd=%d\n", - c->c_connid, c->c_sd); - /* if we were called due to some i/o event, see what the state of the socket is */ - if (slapi_is_loglevel_set(SLAPI_LOG_CONNS) && !NS_JOB_IS_TIMER(ns_job_get_output_type(job)) && c && c->c_sd) { - /* check socket state */ - char buf[1]; - ssize_t rc = recv(c->c_sd, buf, sizeof(buf), MSG_PEEK); - if (!rc) { - slapi_log_err(SLAPI_LOG_CONNS, "ns_handle_pr_read_ready", "socket is closed conn" - " %" PRIu64 " for fd=%d\n", - c->c_connid, c->c_sd); - } else if (rc > 0) { - slapi_log_err(SLAPI_LOG_CONNS, "ns_handle_pr_read_ready", "socket read data available" - " for conn %" PRIu64 " for fd=%d\n", - c->c_connid, c->c_sd); - } else if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { - slapi_log_err(SLAPI_LOG_CONNS, "ns_handle_pr_read_ready", "socket has no data available" - " conn %" PRIu64 " for fd=%d\n", - c->c_connid, c->c_sd); - } else { - slapi_log_err(SLAPI_LOG_CONNS, "ns_handle_pr_read_ready", "socket has error [%d] " - "conn %" PRIu64 " for fd=%d\n", - errno, c->c_connid, c->c_sd); - } - } - connection_release_nolock_ext(c, 1); /* release ref acquired when job was added */ - if (CONN_NEEDS_CLOSING(c)) { - ns_handle_closure_nomutex(c); - /* We shouldn't need the c_idletimeout check here because of how libevent works. - * consider testing this and removing it oneday. - */ - } else if (NS_JOB_IS_TIMER(ns_job_get_output_type(job))) { - if (c->c_idletimeout > 0) { - /* idle timeout */ - disconnect_server_nomutex_ext(c, c->c_connid, -1, - SLAPD_DISCONNECT_IDLE_TIMEOUT, EAGAIN, - 0 /* do not schedule closure, do it next */); - } else { - slapi_log_err(SLAPI_LOG_WARNING, "ns_handle_pr_read_ready", "Received idletime out with c->c_idletimeout as 0. Ignoring.\n"); - } - ns_handle_closure_nomutex(c); - } else if ((connection_activity(c, c->c_max_threads_per_conn)) == -1) { - /* This might happen as a result of - * trying to acquire a closing connection - */ - slapi_log_err(SLAPI_LOG_ERR, "ns_handle_pr_read_ready", "connection_activity: abandoning" - " conn %" PRIu64 " as fd=%d is already closing\n", - c->c_connid, c->c_sd); - /* The call disconnect_server should do nothing, - * as the connection c should be already set to CLOSING */ - disconnect_server_nomutex_ext(c, c->c_connid, -1, - SLAPD_DISCONNECT_POLL, EPIPE, - 0 /* do not schedule closure, do it next */); - ns_handle_closure_nomutex(c); - } else { - slapi_log_err(SLAPI_LOG_CONNS, "ns_handle_pr_read_ready", "queued conn %" PRIu64 " for fd=%d\n", - c->c_connid, c->c_sd); - } - /* Since we call done on the job, we need to remove it here. */ - pthread_mutex_unlock(&(c->c_mutex)); - ns_job_done(job); - return; -} - /* * wrapper functions required so we can implement ioblock_timeout and * avoid blocking forever. @@ -2403,53 +2048,6 @@ handle_new_connection(Connection_Table *ct, int tcps, PRFileDesc *pr_acceptfd, i return 0; } -static void -ns_handle_new_connection(struct ns_job_t *job) -{ - int rc; - Connection *c = NULL; - listener_info *li = (listener_info *)ns_job_get_data(job); - - /* only accept new connections if we have enough fds, more than - * the number of reserved descriptors - */ - if ((li->ct->size - g_get_current_conn_count()) <= config_get_reservedescriptors()) { - /* too many open fds - Just return, and hope next time is better. */ - slapi_log_error(SLAPI_LOG_FATAL, "ns_handle_new_connection", "Insufficient Reserve FD: File Descriptor exhaustion has occured! Connections will be silently dropped!\n"); - return; - } - - rc = handle_new_connection(li->ct, SLAPD_INVALID_SOCKET, li->listenfd, li->secure, li->local, &c); - if (rc) { - PRErrorCode prerr = PR_GetError(); - if (PR_PROC_DESC_TABLE_FULL_ERROR == prerr) { - /* too many open fds - shut off this listener - when an fd is - * closed, try to resume this listener. - * - * WARNING: This generates a lot of false negatives. Why? - */ - slapi_log_error(SLAPI_LOG_FATAL, "ns_handle_new_connection", "PR_PROC_DESC_TABLE_FULL_ERROR: File Descriptor exhaustion has occured! Connections will be silently dropped!\n"); - } else { - slapi_log_err(SLAPI_LOG_FATAL, "ns_handle_new_connection", "Error accepting new connection listenfd=%d [%d:%s]\n", - PR_FileDesc2NativeHandle(li->listenfd), prerr, - slapd_pr_strerror(prerr)); - } - return; - } - c->c_tp = ns_job_get_tp(job); - /* This originally just called ns_handle_pr_read_ready directly - however, there - * are certain cases where accept() will return a file descriptor that is not - * immediately available for reading - this would cause the poll() in - * connection_read_operation() to be hit - it seemed to perform better when - * that poll() was avoided, even at the expense of putting this new fd back - * in nunc-stans to poll for read ready. - */ - pthread_mutex_lock(&(c->c_mutex)); - ns_connection_post_io_or_closing(c); - pthread_mutex_unlock(&(c->c_mutex)); - return; -} - static int init_shutdown_detect(void) { @@ -2497,10 +2095,8 @@ init_shutdown_detect(void) (void)SIGNAL(SIGUSR1, slapd_do_nothing); (void)SIGNAL(SIGUSR2, set_shutdown); #endif - if (!enable_nunc_stans) { - (void)SIGNAL(SIGTERM, set_shutdown); - (void)SIGNAL(SIGHUP, set_shutdown); - } + (void)SIGNAL(SIGTERM, set_shutdown); + (void)SIGNAL(SIGHUP, set_shutdown); #endif /* HPUX */ return 0; } @@ -2928,9 +2524,6 @@ netaddr2string(const PRNetAddr *addr, char *addrbuf, size_t addrbuflen) static int createsignalpipe(void) { - if (enable_nunc_stans) { - return (0); - } if (PR_CreatePipe(&signalpipe[0], &signalpipe[1]) != 0) { PRErrorCode prerr = PR_GetError(); slapi_log_err(SLAPI_LOG_ERR, "createsignalpipe", diff --git a/ldap/servers/slapd/fe.h b/ldap/servers/slapd/fe.h index f47bb61..e96ebb4 100644 --- a/ldap/servers/slapd/fe.h +++ b/ldap/servers/slapd/fe.h @@ -116,7 +116,7 @@ int connection_table_iterate_active_connections(Connection_Table *ct, void *arg, */ int signal_listner(void); int daemon_pre_setuid_init(daemon_ports_t *ports); -void slapd_daemon(daemon_ports_t *ports, ns_thrpool_t *tp); +void slapd_daemon(daemon_ports_t *ports); void daemon_register_connection(void); int slapd_listenhost2addr(const char *listenhost, PRNetAddr ***addr); int daemon_register_reslimits(void); diff --git a/ldap/servers/slapd/main.c b/ldap/servers/slapd/main.c index 183750b..b39d95a 100644 --- a/ldap/servers/slapd/main.c +++ b/ldap/servers/slapd/main.c @@ -115,149 +115,6 @@ static int slapd_exemode_suffix2instance(struct main_config *mcfg); static int slapd_debug_level_string2level(const char *s); static void slapd_debug_level_log(int level); static void slapd_debug_level_usage(void); -/* - * global variables - */ - -struct ns_job_t *ns_signal_job[6]; - -/* - * Nunc stans logging function. - */ -static void -nunc_stans_logging(int severity, const char *format, va_list varg) -{ - va_list varg_copy; - int loglevel = SLAPI_LOG_ERR; - - if (severity == LOG_DEBUG) { - loglevel = SLAPI_LOG_NUNCSTANS; - } else if (severity == LOG_INFO) { - loglevel = SLAPI_LOG_CONNS; - } - - va_copy(varg_copy, varg); - slapi_log_error_ext(loglevel, "nunc-stans", (char *)format, varg, varg_copy); - va_end(varg_copy); -} - -static void * -nunc_stans_malloc(size_t size) -{ - return (void *)slapi_ch_malloc((unsigned long)size); -} - -static void * -nunc_stans_memalign(size_t size, size_t alignment) -{ - return (void *)slapi_ch_memalign(size, alignment); -} - -static void * -nunc_stans_calloc(size_t count, size_t size) -{ - return (void *)slapi_ch_calloc((unsigned long)count, (unsigned long)size); -} - -static void * -nunc_stans_realloc(void *block, size_t size) -{ - return (void *)slapi_ch_realloc((char *)block, (unsigned long)size); -} - -static void -nunc_stans_free(void *ptr) -{ - slapi_ch_free((void **)&ptr); -} - -static void -ns_set_user(struct ns_job_t *job __attribute__((unused))) -{ - /* This literally does nothing. We intercept user signals (USR1, USR2) */ - /* Could be good for a status output, or an easter egg. */ - return; -} - -static void -ns_set_shutdown(struct ns_job_t *job) -{ - /* Is there a way to make this a bit more atomic? */ - /* I think NS protects this by only executing one signal job at a time */ - if (g_get_shutdown() == 0) { - g_set_shutdown(SLAPI_SHUTDOWN_SIGNAL); - - /* Signal all the worker threads to stop */ - } - ns_thrpool_shutdown(ns_job_get_tp(job)); -} - - -/* - * Setup our nunc-stans worker pool from our config. - * we must have read dse.ldif before this point. - */ - -static int_fast32_t -main_create_ns(ns_thrpool_t **tp_in) -{ - if (!config_get_enable_nunc_stans()) { - return 1; - } - struct ns_thrpool_config tp_config; - - int32_t maxthreads = (int32_t)config_get_threadnumber(); - /* Set the nunc-stans thread pool config */ - ns_thrpool_config_init(&tp_config); - - tp_config.max_threads = maxthreads; - tp_config.stacksize = SLAPD_DEFAULT_THREAD_STACKSIZE; - /* Highly likely that we need to re-write logging to be controlled by NS here. */ - tp_config.log_fct = nunc_stans_logging; - tp_config.log_start_fct = NULL; - tp_config.log_close_fct = NULL; - tp_config.malloc_fct = nunc_stans_malloc; - tp_config.memalign_fct = nunc_stans_memalign; - tp_config.calloc_fct = nunc_stans_calloc; - tp_config.realloc_fct = nunc_stans_realloc; - tp_config.free_fct = nunc_stans_free; - - *tp_in = ns_thrpool_new(&tp_config); - - /* We mark these as persistent so they keep blocking signals forever. */ - /* These *must* be in the event thread (ie not ns_job_thread) to prevent races */ - ns_add_signal_job(*tp_in, SIGINT, NS_JOB_PERSIST, ns_set_shutdown, NULL, &ns_signal_job[0]); - ns_add_signal_job(*tp_in, SIGTERM, NS_JOB_PERSIST, ns_set_shutdown, NULL, &ns_signal_job[1]); - ns_add_signal_job(*tp_in, SIGTSTP, NS_JOB_PERSIST, ns_set_shutdown, NULL, &ns_signal_job[3]); - ns_add_signal_job(*tp_in, SIGHUP, NS_JOB_PERSIST, ns_set_user, NULL, &ns_signal_job[2]); - ns_add_signal_job(*tp_in, SIGUSR1, NS_JOB_PERSIST, ns_set_user, NULL, &ns_signal_job[4]); - ns_add_signal_job(*tp_in, SIGUSR2, NS_JOB_PERSIST, ns_set_user, NULL, &ns_signal_job[5]); - return 0; -} - -static int_fast32_t -main_stop_ns(ns_thrpool_t *tp) -{ - if (tp == NULL) { - return 0; - } - ns_thrpool_shutdown(tp); - ns_thrpool_wait(tp); - - /* Now we free the signal jobs. We do it late here to keep intercepting - * them for as long as possible .... Later we need to rethink this to - * have plugins and such destroy while the tp is still active. - */ - ns_job_done(ns_signal_job[0]); - ns_job_done(ns_signal_job[1]); - ns_job_done(ns_signal_job[2]); - ns_job_done(ns_signal_job[3]); - ns_job_done(ns_signal_job[4]); - ns_job_done(ns_signal_job[5]); - ns_thrpool_destroy(tp); - - return 0; -} /* Four cases: @@ -666,7 +523,6 @@ main(int argc, char **argv) slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); daemon_ports_t ports_info = {0}; - ns_thrpool_t *tp = NULL; #ifdef LINUX char *m = getenv("SLAPD_MXFAST"); @@ -951,11 +807,6 @@ main(int argc, char **argv) } /* - * Create our thread pool here for tasks to utilise. - */ - main_create_ns(&tp); - - /* * if we were called upon to do special database stuff, do it and be * done. */ @@ -1212,7 +1063,7 @@ main(int argc, char **argv) { starttime = slapi_current_utc_time(); - slapd_daemon(&ports_info, tp); + slapd_daemon(&ports_info); } slapi_log_err(SLAPI_LOG_INFO, "main", "slapd stopped.\n"); reslimit_cleanup(); @@ -1225,7 +1076,6 @@ cleanup: SSL_ClearSessionCache(); ndn_cache_destroy(); NSS_Shutdown(); - main_stop_ns(tp); PR_Cleanup(); /* diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h index 5964719..75259e4 100644 --- a/ldap/servers/slapd/proto-slap.h +++ b/ldap/servers/slapd/proto-slap.h @@ -1497,9 +1497,6 @@ void slapd_wait4child(int); void disk_mon_get_dirs(char ***list); int32_t disk_get_info(char *dir, uint64_t *total_space, uint64_t *avail_space, uint64_t *used_space); -void ns_handle_pr_read_ready(struct ns_job_t *job); -void ns_connection_post_io_or_closing(Connection *conn); - /* * main.c */ diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h index ef5132c..0aa2dcc 100644 --- a/ldap/servers/slapd/slap.h +++ b/ldap/servers/slapd/slap.h @@ -124,8 +124,6 @@ typedef struct symbol_t */ #include -#include - #ifdef WITH_SYSTEMD #ifdef HAVE_JOURNALD #include @@ -1651,9 +1649,6 @@ typedef struct conn PRInt32 c_threadnumber; /* # threads used in this conn */ int c_refcnt; /* # ops refering to this conn */ pthread_mutex_t c_mutex; /* protect each conn structure; need to be re-entrant */ - /* Note that c_mutex is a pthreadmutex to allow sharing - * into nunc-stans. - */ PRLock *c_pdumutex; /* only write one pdu at a time */ time_t c_idlesince; /* last time of activity on conn */ int c_idletimeout; /* local copy of idletimeout */ @@ -1683,8 +1678,6 @@ typedef struct conn Conn_IO_Layer_cb c_pop_io_layer_cb; /* callback to pop an IO layer off of the conn->c_prfd */ void *c_io_layer_cb_data; /* callback data */ struct connection_table *c_ct; /* connection table that this connection belongs to */ - ns_thrpool_t *c_tp; /* thread pool for this connection */ - struct ns_job_t *c_job; /* If it exists, the current ns_job_t */ int c_ns_close_jobs; /* number of current close jobs */ char *c_ipaddr; /* ip address str - used by monitor */ /* per conn static config */ @@ -2541,7 +2534,9 @@ typedef struct _slapdFrontendConfig slapi_onoff_t cn_uses_dn_syntax_in_dns; /* indicates the cn value in dns has dn syntax */ slapi_onoff_t global_backend_lock; slapi_int_t maxsimplepaged_per_conn; /* max simple paged results reqs handled per connection */ - slapi_onoff_t enable_nunc_stans; + slapi_onoff_t enable_nunc_stans; /* Despite the removal of NS, we have to leave the value in + * case someone was setting it. + */ #if defined(LINUX) int malloc_mxfast; /* mallopt M_MXFAST */ int malloc_trim_threshold; /* mallopt M_TRIM_THRESHOLD */ diff --git a/src/nunc-stans/README.md b/src/nunc-stans/README.md deleted file mode 100644 index ba37d46..0000000 --- a/src/nunc-stans/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# nunc-stans - -Nunc Stans is an event framework wrapper that provides a thread pool for event callback execution. It provides thread safety to event frameworks by isolating and protecting the thread safe parts from the non-thread safe parts, and allows multi-threaded applications to use event frameworks that are not thread safe. - -It has been primarily developed using http://libevent.org . - -# Documentation - -Nunc Stans uses doxygen to generate docs from the source. The generated docs are here: https://firstyear.fedorapeople.org/nunc-stans/index.html . - - diff --git a/src/nunc-stans/include/nunc-stans.h b/src/nunc-stans/include/nunc-stans.h deleted file mode 100644 index a0ddbdb..0000000 --- a/src/nunc-stans/include/nunc-stans.h +++ /dev/null @@ -1,965 +0,0 @@ -/* --- BEGIN COPYRIGHT BLOCK --- - * Copyright (C) 2015 Red Hat - * see files 'COPYING' and 'COPYING.openssl' for use and warranty - * information - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Additional permission under GPLv3 section 7: - * - * If you modify this Program, or any covered work, by linking or - * combining it with OpenSSL, or a modified version of OpenSSL licensed - * under the OpenSSL license - * (https://www.openssl.org/source/license.html), the licensors of this - * Program grant you additional permission to convey the resulting - * work. Corresponding Source for a non-source form of such a - * combination shall include the source code for the parts that are - * licensed under the OpenSSL license as well as that of the covered - * work. - * --- END COPYRIGHT BLOCK --- - */ -/*! \file nunc-stans.h - \brief Nunc Stans public API - - This is the public API for Nunc Stans -*/ -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifndef NS_THRPOOL_H -/** \cond */ -#define NS_THRPOOL_H -/** \endcond */ - -#include "nspr.h" - -/** - * ns_result_t encapsulates the set of results that can occur when interacting - * with nunc_stans. This is better than a simple status, because we can indicate - * why a failure occured, and allow the caller to handle it gracefully. - */ -typedef enum _ns_result_t { - /** - * Indicate the operation succeded. - */ - NS_SUCCESS = 0, - /** - * indicate that the event loop is shutting down, so we may reject operations. - */ - NS_SHUTDOWN = 1, - /** - * We failed to allocate resources as needed. - */ - NS_ALLOCATION_FAILURE = 2, - /** - * An invalid request was made to the API, you should probably check your - * call. - */ - NS_INVALID_REQUEST = 3, - /** - * You made a request against a job that would violate the safety of the job - * state machine. - */ - NS_INVALID_STATE = 4, - /** - * This occurs when a lower level OS issue occurs, generally thread related. - */ - NS_THREAD_FAILURE = 5, - /** - * The job is being deleted - */ - NS_DELETING = 6, -} ns_result_t; - -/** - * Forward declaration of the thread pool struct - * - * The actual struct is opaque to applications. The forward declaration is here - * for the typedef. - */ -struct ns_thrpool_t; -/** - * This is the thread pool typedef - * - * The actual thread pool is opaque to applications. - * \sa ns_thrpool_new, ns_thrpool_wait, ns_thrpool_destroy, ns_job_get_tp - */ -typedef struct ns_thrpool_t ns_thrpool_t; - -/** \struct ns_job_t - * The nunc stans event and worker job object. - * - * When a new job is created, a pointer to a struct of this object type - * is returned. This is the object that will be the argument to the job - * callback specified. Since the struct is opaque, the functions ns_job_get_data(), ns_job_get_tp(), - * ns_job_get_type(), ns_job_get_fd(), and ns_job_get_output_type() can be used to get - * information about the job. The function ns_job_done() must be used - * when the job should be freed. - * \sa ns_job_get_data, ns_job_get_fd, ns_job_get_tp, ns_job_get_type, ns_job_get_output_type - */ -struct ns_job_t; - -/** - * The job callback function type - * - * Job callback functions must have a function signature of ns_job_func_t. - * \sa ns_add_io_job, ns_add_io_timeout_job, ns_add_timeout_job, ns_add_signal_job, ns_add_job - */ -typedef void (*ns_job_func_t)(struct ns_job_t *); - -/** - * Flag for jobs that are not associated with an event. - * - * Use this flag when creating a job that you want to be run but - * not associated with an event. Usually used in conjunction with - * ns_add_job() and #NS_JOB_THREAD to execute a function using the - * thread pool. - * \sa ns_add_job, NS_JOB_THREAD - */ -#define NS_JOB_NONE 0x0 -/** - * Flag for accept() jobs - new connection listeners - * - * Use this flag when creating jobs that listen for and accept - * new connections. This is typically used in conjunction with - * the #NS_JOB_PERSIST flag so that the job does not have to be - * rearmed every time it is called. - * - * \code - * struct ns_job_t *listenerjob; - * PRFileDesc *listenfd = PR_OpenTCPSocket(...); - * PR_Bind(listenfd, ...); - * PR_Listen(listenfd, ...); - * listenerctx = new_listenerctx(...); // the application context object - * ns_add_io_job(threadpool, NS_JOB_ACCEPT|NS_JOB_PERSIST, - * accept_new_connection, listenerctx, &listenerjob); - * \endcode - * You will probably want to keep track of listenerjob and use it with - * ns_job_done() at application shutdown time to avoid leaking resources. - * \sa ns_add_io_job, ns_add_io_timeout_job, NS_JOB_IS_ACCEPT, ns_job_get_type, ns_job_get_output_type - */ -#define NS_JOB_ACCEPT 0x1 -/** - * Flag for jobs that will use connect() - * - * When creating an I/O job, set this flag in the job_type to be notified - * when the file descriptor is available for outgoing connections. In the - * job callback, use ns_job_get_output_type() and #NS_JOB_IS_CONNECT to - * see if the callback was called due to connect available if the callback is - * used with more than one of the job flags. - * \sa ns_add_io_job, ns_add_io_timeout_job, ns_job_get_type, ns_job_get_output_type, NS_JOB_IS_CONNECT - */ -#define NS_JOB_CONNECT 0x2 -/** - * Flag for I/O read jobs - * - * When creating an I/O job, set this flag in the job_type to be notified - * when the file descriptor is available for reading. In the - * job callback, use ns_job_get_output_type() and #NS_JOB_IS_READ to - * see if the callback was called due to read available if the callback is - * used with more than one of the job flags. - * \sa ns_add_io_job, ns_add_io_timeout_job, ns_job_get_type, ns_job_get_output_type, NS_JOB_IS_READ - */ -#define NS_JOB_READ 0x4 -/** - * Flag for I/O write jobs - * - * When creating an I/O job, set this flag in the job_type to be notified - * when the file descriptor is available for writing. In the - * job callback, use ns_job_get_output_type() and #NS_JOB_IS_WRITE to - * see if the callback was called due to write available if the callback is - * used with more than one of the job flags. - * \sa ns_add_io_job, ns_add_io_timeout_job, ns_job_get_type, ns_job_get_output_type, NS_JOB_IS_WRITE - */ -#define NS_JOB_WRITE 0x8 -/** - * Flag for timer jobs - * - * When creating a timeout or I/O timeout job, set this flag in the job_type - * to be notified when the time given by the timeval argument has elapsed. - * In the job callback, use ns_job_get_output_type() and #NS_JOB_IS_TIMER to - * see if the callback was called due to elapsed time if the callback is - * used with more than one of the job flags. - * \sa ns_add_timeout_job, ns_add_io_timeout_job, ns_job_get_type, ns_job_get_output_type, NS_JOB_IS_TIMER - */ -#define NS_JOB_TIMER 0x10 -/** - * Flag for signal jobs - * - * When creating a signal job, set this flag in the job_type - * to be notified when the process receives the given signal. - * In the job callback, use ns_job_get_output_type() and #NS_JOB_IS_SIGNAL to - * see if the callback was called due to receiving the signal if the callback is - * used with more than one of the job flags. - * \sa ns_add_signal_job, ns_job_get_type, ns_job_get_output_type, NS_JOB_IS_SIGNAL - */ -#define NS_JOB_SIGNAL 0x20 -/** - * Flag to make jobs persistent - * - * By default, when an event (I/O, timer, signal) is triggered and - * the job callback is called, the event is removed from the event framework, - * and the application will no longer receive callbacks for events. The - * application is then responsible for calling ns_job_rearm to "re-arm" - * the job to respond to the event again. Adding the job with the - * #NS_JOB_PERSIST flag added to the job_type means the job will not have - * to be rearmed. This is usually used in conjunction with #NS_JOB_ACCEPT - * for accept jobs. Use ns_job_get_type() or ns_job_get_output_type() with - * #NS_JOB_IS_PERSIST to test if the job is persistent. - * - * \note Be very careful when using this flag in conjunction with #NS_JOB_THREAD. - * For example, for a #NS_JOB_ACCEPT job, once you call accept(), the socket - * may be immediately available for another accept, and your callback could be - * called again immediately in another thread. Same for the other types of I/O - * jobs. In that case, your job callback function must be thread safe - global - * resources must be protected with a mutex, the function must be reentrant, etc. - * - * \sa NS_JOB_ACCEPT, NS_JOB_THREAD, NS_JOB_IS_PERSIST, ns_job_get_type, ns_job_get_output_type - */ -#define NS_JOB_PERSIST 0x40 -/** - * Flag to make jobs run in a thread pool thread - * - * This flag allows you to specify if you want a job to run threaded or not. - * If the job is threaded, the job callback is executed by a thread in - * the thread pool, and the job callback function must be thread safe and - * reentrant. If the job is not threaded, the job runs in the same thread - * as the event loop thread. - * - * \note When #NS_JOB_THREAD is \e not used, the job callback will be run in the - * event loop thread, and will block all events from being processed. - * Care must be taken to ensure that the job callback does not block. - * - * Use ns_job_get_type() or ns_job_get_output_type() with #NS_JOB_IS_THREAD to - * test if the job is threaded. - * - * \sa NS_JOB_IS_THREAD, ns_job_get_type, ns_job_get_output_type - */ -#define NS_JOB_THREAD 0x80 -/** - * Flag to tell ns_job_done() not to close the job fd - * - * I/O jobs will have a file descriptor (fd). If the job->fd lifecycle - * is managed by the application, this flag tells ns_job_done() not to close - * the fd. - * \sa ns_add_io_job, ns_add_io_timeout_job, ns_job_get_type, ns_job_get_output_type, NS_JOB_IS_PRESERVE_FD - */ -#define NS_JOB_PRESERVE_FD 0x100 -/** - * Internal flag to shutdown a worker thread. - * - * If you assign this to a job it will cause the worker thread that dequeues it to be - * shutdown, ready for pthread_join() to be called on it. - * - * You probably DON'T want to use this ever, as it really will shutdown threads - * and you can't get them back .... you have been warned. - */ -#define NS_JOB_SHUTDOWN_WORKER 0x200 -/** - * Bitflag type for job types - * - * This is the job_type bitfield argument used when adding jobs, and the return - * value of the functions ns_job_get_type() and ns_job_get_output_type(). The - * value is one or more of the NS_JOB_* macros OR'd together. - * \code - * ns_job_type_t job_type = NS_JOB_READ|NS_JOB_SIGNAL; - * \endcode - * When used with ns_job_get_type() or ns_job_get_output_type() to see what type - * of job it is, use the return value with one of the NS_JOB_IS_* macros: - * \code - * if (NS_JOB_IS_TIMER(ns_job_get_output_type(job))) { - * // handle timeout - * } - * \endcode - * \sa ns_add_io_job, ns_add_io_timeout_job, ns_add_job, ns_add_signal_job, ns_add_timeout_job, ns_job_get_type, ns_job_get_output_type - */ -typedef uint_fast16_t ns_job_type_t; - -/** - * Used to test an #ns_job_type_t value for #NS_JOB_ACCEPT - * \sa NS_JOB_ACCEPT, ns_job_get_type, ns_job_get_output_type - */ -#define NS_JOB_IS_ACCEPT(eee) ((eee)&NS_JOB_ACCEPT) -/** - * Used to test an #ns_job_type_t value for #NS_JOB_READ - * \sa NS_JOB_READ, ns_job_get_type, ns_job_get_output_type - */ -#define NS_JOB_IS_READ(eee) ((eee)&NS_JOB_READ) -/** - * Used to test an #ns_job_type_t value for #NS_JOB_CONNECT - * \sa NS_JOB_CONNECT, ns_job_get_type, ns_job_get_output_type - */ -#define NS_JOB_IS_CONNECT(eee) ((eee)&NS_JOB_CONNECT) -/** - * Used to test an #ns_job_type_t value for #NS_JOB_WRITE - * \sa NS_JOB_WRITE, ns_job_get_type, ns_job_get_output_type - */ -#define NS_JOB_IS_WRITE(eee) ((eee)&NS_JOB_WRITE) -/** - * Used to test an #ns_job_type_t value for #NS_JOB_TIMER - * \sa NS_JOB_TIMER, ns_job_get_type, ns_job_get_output_type - */ -#define NS_JOB_IS_TIMER(eee) ((eee)&NS_JOB_TIMER) -/** - * Used to test an #ns_job_type_t value for #NS_JOB_SIGNAL - * \sa NS_JOB_SIGNAL, ns_job_get_type, ns_job_get_output_type - */ -#define NS_JOB_IS_SIGNAL(eee) ((eee)&NS_JOB_SIGNAL) -/** - * Used to test an #ns_job_type_t value for #NS_JOB_PERSIST - * \sa NS_JOB_PERSIST, ns_job_get_type, ns_job_get_output_type - */ -#define NS_JOB_IS_PERSIST(eee) ((eee)&NS_JOB_PERSIST) -/** - * Used to test if an #ns_job_type_t is to shutdown the worker thread. - */ -#define NS_JOB_IS_SHUTDOWN_WORKER(eee) ((eee)&NS_JOB_SHUTDOWN_WORKER) -/** - * Used to test an #ns_job_type_t value to see if it is any sort of I/O job - * \sa NS_JOB_IS_ACCEPT, NS_JOB_IS_READ, NS_JOB_IS_CONNECT, NS_JOB_IS_WRITE, ns_job_get_type, ns_job_get_output_type - */ -#define NS_JOB_IS_IO(eee) (NS_JOB_IS_ACCEPT(eee) || NS_JOB_IS_READ(eee) || NS_JOB_IS_CONNECT(eee) || NS_JOB_IS_WRITE(eee)) -/** - * Used to test an #ns_job_type_t value for #NS_JOB_THREAD - * \sa NS_JOB_THREAD, ns_job_get_type, ns_job_get_output_type - */ -#define NS_JOB_IS_THREAD(eee) ((eee)&NS_JOB_THREAD) -/** - * Used to test an #ns_job_type_t value for #NS_JOB_PRESERVE_FD - * \sa NS_JOB_PRESERVE_FD, ns_job_get_type, ns_job_get_output_type - */ -#define NS_JOB_IS_PRESERVE_FD(eee) ((eee)&NS_JOB_PRESERVE_FD) - -/** - * Used to set an #ns_job_type_t value to have #NS_JOB_READ - */ -#define NS_JOB_SET_READ(eee) ((eee) |= NS_JOB_READ) -/** - * Used to set an #ns_job_type_t value to have #NS_JOB_WRITE - */ -#define NS_JOB_SET_WRITE(eee) ((eee) |= NS_JOB_WRITE) -/** - * Used to set an #ns_job_type_t value to have #NS_JOB_THREAD - */ -#define NS_JOB_SET_THREAD(eee) ((eee) |= NS_JOB_THREAD) -/** - * Remove #NS_JOB_READ from an #ns_job_type_t value - */ -#define NS_JOB_UNSET_READ(eee) ((eee) &= ~NS_JOB_READ) -/** - * Remove #NS_JOB_WRITE from an #ns_job_type_t value - */ -#define NS_JOB_UNSET_WRITE(eee) ((eee) &= ~NS_JOB_WRITE) -/** - * Remove #NS_JOB_THREAD from an #ns_job_type_t value - */ -#define NS_JOB_UNSET_THREAD(eee) ((eee) &= ~NS_JOB_THREAD) - -/** - * Used to configure the thread pool - * - * This is the argument to ns_thrpool_new(). This is used to set all of the - * configuration parameters for the thread pool. - * - * This must be initialized using ns_thrpool_config_init(). This will - * initialize the fields to their default values. Use like this: - * \code - * struct ns_thrpool_config nsconfig; - * ns_thrpool_config_init(&nsconfig); - * nsconfig.max_threads = 16; - * nsconfig.malloc_fct = mymalloc; - * ... - * rc = ns_thrpool_new(&nsconfig); - * \endcode - * \sa ns_thrpool_config_init, ns_thrpool_new - */ -struct ns_thrpool_config -{ - /** \cond */ - int32_t init_flag; - /** \endcond */ - size_t max_threads; /**< Do not grow the thread pool greater than this size */ - size_t stacksize; /**< Thread stack size */ - - /* pluggable logging functions */ - void (*log_fct)(int, const char *, va_list); /**< Provide a function that works like vsyslog */ - void (*log_start_fct)(void); /**< Function to call to initialize the logging system */ - void (*log_close_fct)(void); /**< Function to call to shutdown the logging system */ - - /* pluggable memory functions */ - void *(*malloc_fct)(size_t); /**< malloc() replacement */ - void *(*memalign_fct)(size_t size, size_t alignment); /**< posix_memalign() replacement. Note the argument order! */ - void *(*calloc_fct)(size_t, size_t); /**< calloc() replacement */ - void *(*realloc_fct)(void *, size_t); /**< realloc() replacement */ - void (*free_fct)(void *); /**< free() replacement */ -}; - -/** - * Initialize a thrpool config struct - * - * The config struct must be allocated/freed by the caller. A stack - * variable is typically used. - * \code - * struct ns_thrpool_config nsconfig; - * ns_thrpool_config_init(&nsconfig); - * nsconfig.max_threads = 16; - * nsconfig.malloc_fct = mymalloc; - * ... - * rc = ns_thrpool_new(&nsconfig); - * \endcode - * \sa ns_thrpool_config, ns_thrpool_new - * \param tp_config - thread pool config struct - */ -void ns_thrpool_config_init(struct ns_thrpool_config *tp_config); - -/** - * The application is finished with this job - * - * The application uses this function to tell nunc-stans that it is finished - * using this job. Once the application calls this function, it may no - * longer refer to job - it should be considered as an allocated pointer - * that the free() function has been called with. An application will - * typically call ns_job_done() at the end of a job callback function for - * non-persistent jobs (not using #NS_JOB_PERSIST), or at application - * shutdown time for persistent jobs (using #NS_JOB_PERSIST). For an I/O job, - * ns_job_done will close() the file descriptor associated with the job unless - * the #NS_JOB_PRESERVE_FD is specified when the job is added. - * - * Note that a persistant job can only disarm/ns_job_done() itself, unless the - * threadpool is in shutdown, then external threads may request the job to be marked - * as done(). This is to protect from a set of known race conditions that may occur. - * - * All jobs *must* have ns_job_done() called upon them to clean them correctly. Failure - * to do so may cause resource leaks. - * - * \code - * void read_callback(struct ns_job_t *job) - * { - * appctx_t *appctx = (appctx_t *)ns_job_get_data(job); - * ... - * ns_job_done(job); - * // ok to use appctx here, but not job - * // app must free or ensure appctx is not leaked - * return; - * } - * \endcode - * \param job the job to clean up - * \retval NS_SUCCESS Job was successfully queued for removal. - * \retval NS_INVALID_STATE Failed to mark job for removal. Likely the job is ARMED! - * We cannot remove jobs that are armed due to the race conditions it can cause. - * \retval NS_INVALID_REQUEST No job was provided to remove (IE NULL request) - * \sa ns_job_t, ns_job_get_data, NS_JOB_PERSIST, NS_JOB_PRESERVE_FD - */ -ns_result_t ns_job_done(struct ns_job_t *job); - -/** - * Create a new job which is not yet armed. - * - * Specify the type of job using the job_type bitfield. You can specify - * more than one type of job. - * - * The callback will need to rearm the job or add another job if it wants to - * be notified of more events. - * - * This job is not armed at creation unlike other ns_add_*_job. This means that - * after the job is created, you can use ns_job_set_*, and when ready, arm the job. - * - * \code - * struct ns_job_t *job; - * ns_create_job(tp, NS_JOB_READ, my_callback, &job); - * ns_job_set_data(job, data); - * // You may not alter job once it is armed. - * ns_job_rearm(job); - * \endcode - * - * \param tp The thread pool you want to add an I/O job to. - * \param job_type A set of flags that indicates the job type. - * \param func The callback function to call when processing the job. - * \param[out] job The address of a job pointer that will be filled in once the job is allocated. - * \retval NS_SUCCESS Job was successfully added. - * \retval NS_ALLOCATION_FAILURE Failed to allocate job. - * \retval NS_INVALID_REQUEST As create job does not create armed, if you make a request - * with a NULL job parameter, this would create a memory leak. As a result, we fail if - * the request is NULL. - * \warning The thread pool will not allow a job to be added when it has been signaled - * to shutdown. It will return PR_FAILURE in that case. - * \sa ns_job_t, ns_job_get_data, NS_JOB_READ, NS_JOB_WRITE, NS_JOB_ACCEPT, NS_JOB_CONNECT, NS_JOB_IS_IO, ns_job_done - */ -ns_result_t -ns_create_job(struct ns_thrpool_t *tp, ns_job_type_t job_type, ns_job_func_t func, struct ns_job_t **job); - -/** - * Adds an I/O job to the thread pool - * - * Specify the type of I/O job using the job_type bitfield. You can specify - * more than one type of I/O job. Use ns_job_get_output_type(job) to - * determine which event triggered the I/O. - * \code - * ns_add_io_job(tp, fd, NS_JOB_READ|NS_JOB_WRITE, my_io_callback, ...); - * ... - * void my_io_callback(struct ns_job_t *job) - * { - * if (NS_JOB_IS_READ(ns_job_get_output_type(job))) { - * // handle reading from fd - * } else { - * // handle writing to fd - * } - * } - * \endcode - * The callback will need to rearm the job or add another job if it wants to - * be notified of more events, or use #NS_JOB_PERSIST. If you want an I/O job - * that will timeout if I/O is not detected within a certain period of time, - * use ns_add_io_timeout_job(). - * \param tp The thread pool you want to add an I/O job to. - * \param fd The file descriptor to use for I/O. - * \param job_type A set of flags that indicates the job type. - * \param func The callback function to call when processing the job. - * \param data Arbitrary data that will be available to the job callback function. - * \param[out] job The address of a job pointer that will be filled in once the job is allocated. - * \c NULL can be passed if a pointer to the job is not needed. - * \retval NS_SUCCESS Job was successfully added. - * \retval NS_ALLOCATION_FAILURE Failed to allocate job. - * \retval NS_INVALID_REQUEST An invalid job request was made: likely you asked for an - * accept job to be threaded, which is currently invalid. - * \warning The thread pool will not allow a job to be added when it has been signaled - * to shutdown. It will return PR_FAILURE in that case. - * \sa ns_job_t, ns_job_get_data, NS_JOB_READ, NS_JOB_WRITE, NS_JOB_ACCEPT, NS_JOB_CONNECT, NS_JOB_IS_IO, ns_job_done - */ -ns_result_t ns_add_io_job(struct ns_thrpool_t *tp, - PRFileDesc *fd, - ns_job_type_t job_type, - ns_job_func_t func, - void *data, - struct ns_job_t **job); - -/** - * Adds a timeout job to the thread pool - * - * The func function will be called when the timer expires. - * - * \param tp The thread pool you want to add a timeout job to. - * \param tv The timer that needs to expire before triggering the callback function. - * \param job_type A set of flags that indicates the job type - #NS_JOB_TIMER + other flags - * \param func The callback function to call when processing the job. - * \param data Arbitrary data that will be available to the job callback function. - * \param[out] job The address of a job pointer that will be filled in once the job is allocated. - * \c NULL can be passed if a pointer to the job is not needed. - * \retval NS_SUCCESS Job was successfully added. - * \retval NS_ALLOCATION_FAILURE Failed to allocate job. - * \retval NS_INVALID_REQUEST An invalid job request was made: likely you asked for a - * timeout that is not valid (negative integer). - * \warning The thread pool will not allow a job to be added when it has been signaled - * to shutdown. It will return PR_FAILURE in that case. - * \sa ns_job_t, ns_job_get_data, NS_JOB_TIMER, NS_JOB_IS_TIMER, ns_job_done - */ -ns_result_t ns_add_timeout_job(struct ns_thrpool_t *tp, - struct timeval *tv, - ns_job_type_t job_type, - ns_job_func_t func, - void *data, - struct ns_job_t **job); - -/** - * Adds an I/O job to the thread pool's work queue with a timeout. - * - * The callback func function should test the type of event that triggered - * the callback using ns_job_get_output_type(job) to get the - * #ns_job_type_t, then use #NS_JOB_IS_TIMER(output_type) to see if - * this callback was triggered by a timer event. This is useful if - * you want to perform some sort of I/O, but you require that I/O - * must happen in a certain amount of time. - * \code - * ns_add_io_timeout_job(tp, fd, &tv, NS_JOB_READ|NS_JOB_TIMER, my_iot_callback, ...); - * ... - * void my_iot_callback(struct ns_job_t *job) - * { - * if (NS_JOB_IS_TIMER(ns_job_get_output_type(job))) { - * // handle timeout condition - * } else { - * // handle read from fd - * } - * } - * \endcode - * \note This is like adding an I/O job, with an optional timeout. - * This is not like adding a timeout job with an additional I/O - * event component. This depends on the underlying event framework - * having the ability to have a timed I/O job. For example, libevent - * I/O events can have a timeout. - * - * \param tp The thread pool whose work queue you want to add an I/O job to. - * \param fd The file descriptor to use for I/O. - * \param tv The timer that needs to expire before triggering the callback function. - * \param job_type A set of flags that indicates the job type. - * \param func The callback function for a worker thread to call when processing the job. - * \param data Arbitrary data that will be available to the job callback function. - * \param[out] job The address of a job pointer that will be filled in once the job is allocated. - * \c NULL can be passed if a pointer to the job is not needed. - * \retval NS_SUCCESS Job was successfully added. - * \retval NS_ALLOCATION_FAILURE Failed to allocate job. - * \retval NS_INVALID_REQUEST An invalid job request was made: likely you asked for a - * timeout that is not valid (negative integer). Another failure is you requested a - * threaded accept job. - * \warning The thread pool will not allow a job to be added when it has been signaled - * to shutdown. It will return NS_SHUTDOWN in that case. - * \sa ns_job_t, ns_job_get_data, NS_JOB_READ, NS_JOB_WRITE, NS_JOB_ACCEPT, NS_JOB_CONNECT, NS_JOB_IS_IO, ns_job_done, NS_JOB_TIMER, NS_JOB_IS_TIMER - */ -ns_result_t ns_add_io_timeout_job(struct ns_thrpool_t *tp, - PRFileDesc *fd, - struct timeval *tv, - ns_job_type_t job_type, - ns_job_func_t func, - void *data, - struct ns_job_t **job); - -/** - * Adds a signal job to the thread pool - * - * The \a func function will be called when the signal is received by the process. - * - * \param tp The thread pool you want to add a signal job to. - * \param signum The signal number that you want to trigger the callback function. - * \param job_type A set of flags that indicates the job type. - * \param func The callback function to call when processing the job. - * \param data Arbitrary data that will be available to the job callback function. - * \param[out] job The address of a job pointer that will be filled in once the job is allocated. - * \c NULL can be passed if a pointer to the job is not needed. - * \retval NS_SUCCESS Job was successfully added. - * \retval NS_ALLOCATION_FAILURE Failed to allocate job. - * \warning The thread pool will not allow a job to be added when it has been signaled - * to shutdown. It will return PR_FAILURE in that case. - * \sa ns_job_t, ns_job_get_data, NS_JOB_SIGNAL, NS_JOB_IS_SIGNAL - */ -ns_result_t ns_add_signal_job(ns_thrpool_t *tp, - int32_t signum, - ns_job_type_t job_type, - ns_job_func_t func, - void *data, - struct ns_job_t **job); - -/** - * Add a non-event related job to the thread pool - * - * A non-event related job is a job that is executed immediately that is not contingent - * on an event or signal. This is typically used when the application wants to do some - * processing in parallel using a thread from the thread pool. - * \code - * ns_add_job(tp, NS_JOB_NONE|NS_JOB_THREAD, my_callback, ...); - * ... - * void my_callback(struct ns_job_t *job) - * { - * // now in a separate thread - * } - * \endcode - * - * \param tp The thread pool you want to add the job to. - * \param job_type A set of flags that indicates the job type (usually just NS_JOB_NONE|NS_JOB_THREAD) - * \param func The callback function to call when processing the job. - * \param data Arbitrary data that will be available to the job callback function. - * \param[out] job The address of a job pointer that will be filled in once the job is allocated. - * \c NULL can be passed if a pointer to the job is not needed. - * \retval NS_SUCCESS Job was successfully added. - * \retval NS_ALLOCATION_FAILURE Failed to allocate job. - * \warning The thread pool will not allow a job to be added when it has been signaled - * to shutdown. It will return PR_FAILURE in that case. - * \sa ns_job_t, ns_job_get_data, NS_JOB_NONE, NS_JOB_THREAD - */ -ns_result_t ns_add_job(ns_thrpool_t *tp, ns_job_type_t job_type, ns_job_func_t func, void *data, struct ns_job_t **job); - -/** - * Allows the callback to access the file descriptor for an I/O job - * - * \code - * void my_io_job_callback(struct ns_job_t *job) - * { - * PRFileDesc *fd = ns_job_get_fd(job); - * rc = PR_Read(fd, ...); - * ... - * } - * \endcode - * If the job is not an I/O job, the function will return NULL. - * - * \param job The job to get the fd for. - * \retval fd The file descriptor associated with the I/O job. - * \retval NULL The job is not an I/O job - * \sa ns_job_t, ns_add_io_job, ns_add_io_timeout_job - */ -PRFileDesc *ns_job_get_fd(struct ns_job_t *job); - -/** - * Allows the callback to access the private data field in the job. - * - * This is the \c data field passed in when the job is added. This - * data is private to the application - nunc-stans does not touch it - * in any way. The application is responsible for managing the lifecycle - * of this data. - * \code - * void my_job_callback(struct ns_job_t *job) - * { - * myappctx_t *myappctx = (myappctx_t *)ns_job_get_data(job); - * ... - * } - * \endcode - * - * \param job The job to get the data for. - * \return The private data associated with the job. - * \sa ns_job_t - */ -void *ns_job_get_data(struct ns_job_t *job); - -/** - * Allows the caller to set private data into the job - * Care should be taken to make sure that the previous contents are - * freed, or that the data is freed after use. Leaks will be annoying to track - * down with this! - * - * This sets the \c data field. This data is private to the application - nunc-stans - * will not touch it in any way. - * \code - * void my_job_callback(struct ns_job_t *job) - * { - * myappctx_t *myappctx = malloc(sizeof(struct appctx)); - * ... - * // You must check and *free* the data if required. - * // Else you may introduce a memory leak! - * void *data = ns_job_get_data(job); - * if (data != NULL) { - * myapp_use_data(data); - * ... - * } - * free(data); - * if (ns_job_set_data(job, (void *)myappctx) == PR_SUCCESS) { - * //handle the error, you probably have a bug .... - * } - * } - * \endcode - * - * \param job The job to set the data for - * \param data The void * pointer to the data to set - * \retval NS_SUCCESS Job was modified correctly. - * \retval NS_INVALID_STATE Failed to modify the job as this may be unsafe. - * \sa ns_job_t - */ -ns_result_t ns_job_set_data(struct ns_job_t *job, void *data); - -/** - * Allows the callback to access the job type flags. - * - * Usually used in conjunction with one of the NS_JOB_IS_* macros. - * - * \code - * void my_job_callback(struct ns_job_t *job) - * { - * if (NS_JOB_IS_READ(ns_job_get_type(job)) { - * ... - * } - * } - * \endcode - * \param job The job to get the type for. - * \return The #ns_job_type_t flags for the job - * \sa ns_job_t, NS_JOB_IS_READ, NS_JOB_IS_WRITE, NS_JOB_IS_IO, NS_JOB_IS_TIMER - */ -ns_job_type_t ns_job_get_type(struct ns_job_t *job); - -/** - * Allows the callback to access the thread pool that the job is associated with. - * - * Useful for adding jobs from within job callbacks. - * - * \code - * void my_job_callback(struct ns_job_t *job) - * { - * // do some work - * // need to listen for some events - * ns_add_io_job(ns_job_get_tp(job), fd, ...); - * // finished with job - * ns_job_done(job); - * return; - * } - * \endcode - * \param job The job to get the thread pool for. - * \return The thread pool associated with the job. - * \sa ns_job_t, ns_add_io_job - */ -ns_thrpool_t *ns_job_get_tp(struct ns_job_t *job); - -/** - * Allows the callback to know which event triggered the callback. Can only be called from within the callback itself. - * - * The callback func may need to know which event triggered the - * callback. This function will allow access to the type of event - * that triggered the callback. For example, when using - * ns_add_io_timeout_job(), the callback can be called either because of - * an I/O event or a timer event. Use #NS_JOB_IS_TIMER to tell if - * the event is a timer event, like this: - * \code - * if (NS_JOB_IS_TIMER(ns_job_get_output_type(job))) { - * ... handle timeout ... - * } else { - * ... handle I/O ... - * } - * \endcode - * \param job The job to get the output type for. - * \return The #ns_job_type_t corresponding to the event that triggered the callback - * \sa ns_job_t, NS_JOB_IS_TIMER, NS_JOB_IS_READ, NS_JOB_IS_WRITE, NS_JOB_IS_IO, ns_add_io_timeout_job - */ -ns_job_type_t ns_job_get_output_type(struct ns_job_t *job); - -/** - * Allows setting the job done callback. - * - * The job done callback will be triggered when ns_job_done is called on the - * job. This allows jobs to have private data fields cleaned and freed correctly - * \code - * ns_create_job(tp, NS_JOB_READ, my_callback, &job); - * if (ns_job_set_done_cb(job, my_done_callback) != PR_SUCCESS) { - * // you must handle this error!!!! the cb did not set! - * } - * ns_job_rearm(job); - * ... - * void my_done_callback(struct ns_job_t job) { - * free(ns_job_get_data(job)); - * } - * \endcode - * \param job The job to set the callback for. - * \param func The callback function, to be called when ns_job_done is triggered. - * \retval NS_SUCCESS Job was modified correctly. - * \retval NS_INVALID_STATE Failed to modify the job as this may be unsafe. - * \sa ns_job_t, ns_job_done - */ -ns_result_t ns_job_set_done_cb(struct ns_job_t *job, ns_job_func_t func); - -/** - * Block until a job is completed. This returns the next state of the job as as a return. - * - * \param job The job to set the callback for. - * \retval ns_job_state_t The next state the job will move to. IE, WAITING, DELETED, ARMED. - */ -ns_result_t ns_job_wait(struct ns_job_t *job); - -/** - * Creates a new thread pool - * - * Must be called with a struct ns_thrpool_config that has been - * initialized by ns_thrpool_config_init(). Typically, once the - * thread pool has been created, one or more listener jobs or - * other long lived jobs will be added, and the application will - * just call ns_thrpool_wait(). The application should add at least - * one job that will listen for shutdown events, signals, etc. which - * will call ns_thrpool_shutdown(). After ns_thrpool_wait() returns, - * the application should use ns_job_done() to finish any long-lived - * jobs, then call ns_thrpool_destroy(). - * \param config A pointer to a struct ns_thrpool_config - * \return A pointer to the newly created thread pool. - * \sa ns_thrpool_config, ns_thrpool_config_init, ns_thrpool_wait, ns_thrpool_shutdown, ns_thrpool_destroy - */ -struct ns_thrpool_t *ns_thrpool_new(struct ns_thrpool_config *config); - -/** - * Frees a thread pool from memory - * - * This will free a thread pool and it's internal resources from memory. You should - * be sure that the thread pool has been shutdown before destroying it by calling - * ns_thrpool_wait(), then call ns_job_done() to finish any long-lived jobs. After - * calling ns_thrpool_destroy(), do not use tp. - * - * \param tp The thread pool to destroy. - * \sa ns_thrpool_config, ns_thrpool_config_init, ns_thrpool_new, ns_thrpool_wait, ns_thrpool_shutdown - */ -void ns_thrpool_destroy(struct ns_thrpool_t *tp); - -/** - * Tells a thread pool to shutdown its threads - * - * The application will usually call ns_thrpool_shutdown() from an event - * callback that is listening for shutdown events e.g. a signal job that - * is listening for SIGINT or SIGTERM events. - * \code - * tp = ns_thrpool_new(...); - * ns_add_signal_job(tp, SIGTERM, handle_shutdown_signal, NS_JOB_SIGNAL|NS_JOB_THREAD, ...); - * - * void handle_shutdown_signal(job) - * { - * ns_thrpool_shutdown(ns_job_get_tp(job)); - * // set application shutdown flag - * return; - * } - * \endcode - * Use NS_JOB_SIGNAL|NS_JOB_THREAD so that the job will run in a worker thread, not - * in the event loop thread. - * \note The application must call ns_thrpool_shutdown() or ns_thrpool_wait() will - * never return. - * - * \param tp The thread pool to shutdown. - * \warning This must only be called from a job that runs in a worker thread. Calling - * this function from the event thread can cause a deadlock. - * \sa ns_thrpool_config, ns_thrpool_config_init, ns_thrpool_new, ns_thrpool_wait, ns_thrpool_destroy, ns_add_signal_job, NS_JOB_THREAD - */ -void ns_thrpool_shutdown(struct ns_thrpool_t *tp); - -/** - * Checks if a thread pool is shutting down - * - * This can be called by worker threads so they know when the thread pool has - * been requested to shut down. - * - * \retval 0 if the thread pool is not shutting down. - * \retval 1 if the thread pool is shutting down. - * \sa ns_thrpool_shutdown - */ -int32_t ns_thrpool_is_shutdown(struct ns_thrpool_t *tp); - -/** - * Waits for all threads in the pool to exit - * - * This call will block the caller until all threads in the thread pool have exited. A - * program will typically create the thread pool by calling ns_thrpool_new(), then it - * will call ns_thrpool_wait() to wait until the thread pool is shutdown (which is likely - * initiated by a signal handler). Once this function successfully returns, the thread - * pool can be safely destroyed by calling ns_thrpool_destroy(). - * \note The application must call ns_thrpool_shutdown() or ns_thrpool_wait() will - * never return. - * - * \param tp The thread pool to wait for. - * \retval NS_SUCCESS The thread pool threads completed successfully - * \retval NS_THREAD_FAILURE Failure waiting for a thread to rejoin. - * \sa ns_thrpool_config, ns_thrpool_config_init, ns_thrpool_new, ns_thrpool_destroy, ns_thrpool_shutdown - */ -ns_result_t ns_thrpool_wait(struct ns_thrpool_t *tp); - - -/** - * Convenience function to re-arm the same job - * - * This is used for non-persistent (not using #NS_JOB_PERSIST) jobs. - * For example, if you have an I/O reading job, and the job needs to read more data, - * the job callback can just call ns_job_rearm(), and the job callback will be - * called again when read is ready on the job fd. Once this function is called, - * the job callback may be called immediately if the job uses #NS_JOB_THREAD. Do not - * refer to job after calling ns_job_rearm(). - * \note Do not call ns_job_done() with a job if using ns_job_rearm() with the job - * \param job The job to re-arm - * \retval NS_SUCCESS The job was queued correctly. - * \retval NS_SHUTDOWN The job was not able to be queued as the server is in the procees - * of shutting down. - * \retval NS_INVALID_STATE The job was not able to be queued as it is in an invalid state - * \retval NS_INVALID_REQUEST The job to be queued is invalid. - * \sa ns_job_t, ns_job_done, NS_JOB_PERSIST, NS_JOB_THREAD - */ -ns_result_t ns_job_rearm(struct ns_job_t *job); - -int -ns_job_is_func(struct ns_job_t *job, ns_job_func_t func); - -#endif /* NS_THRPOOL_H */ diff --git a/src/nunc-stans/ns/ns_event_fw.h b/src/nunc-stans/ns/ns_event_fw.h deleted file mode 100644 index 88997b2..0000000 --- a/src/nunc-stans/ns/ns_event_fw.h +++ /dev/null @@ -1,180 +0,0 @@ -/* --- BEGIN COPYRIGHT BLOCK --- - * Copyright (C) 2015 Red Hat - * see files 'COPYING' and 'COPYING.openssl' for use and warranty - * information - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Additional permission under GPLv3 section 7: - * - * If you modify this Program, or any covered work, by linking or - * combining it with OpenSSL, or a modified version of OpenSSL licensed - * under the OpenSSL license - * (https://www.openssl.org/source/license.html), the licensors of this - * Program grant you additional permission to convey the resulting - * work. Corresponding Source for a non-source form of such a - * combination shall include the source code for the parts that are - * licensed under the OpenSSL license as well as that of the covered - * work. - * --- END COPYRIGHT BLOCK --- - */ -#ifndef NS_EVENT_FW_H -#define NS_EVENT_FW_H - -#include "ns_private.h" - -/* For locks and cond var */ -#include - -struct ns_event_fw_ctx_t; -struct ns_event_fw_fd_t; -struct ns_event_fw_time_t; -struct ns_event_fw_sig_t; - -#ifndef NO_EVENT_TYPEDEFS -typedef struct ns_event_fw_ctx_t ns_event_fw_ctx_t; -typedef struct ns_event_fw_fd_t ns_event_fw_fd_t; -typedef struct ns_event_fw_time_t ns_event_fw_time_t; -typedef struct ns_event_fw_sig_t ns_event_fw_sig_t; -#endif - -/* - * The states we can have for a job. - * We start in WAITING. Valid transitions are: - * // With waiting, *any* thread can change this. Prevents races. - * WAITING -> NEEDS_DELETE - * WAITING -> NEEDS_ARM - * NEEDS_ARM -> ARMED - * NEEDS_ARM -> NEEDS_DELETE - * ARMED -> RUNNING - * When a job is ARMED, and tp->shutdown 1, it CAN move to NEEDS_DELETE - * ARMED -> NEEDS_DELETE // Only by shutdown of event loop - * // The trick with RUNNING is that only the current thread working thread can change this! - * RUNNING -> NEEDS_DELETE - * RUNNING -> NEEDS_ARM - * RUNNING -> WAITING - * NEEDS_DELETE -> DELETED - */ - -typedef enum _ns_job_state { - NS_JOB_NEEDS_DELETE = 2, - NS_JOB_DELETED = 3, - NS_JOB_NEEDS_ARM = 4, - NS_JOB_ARMED = 5, - NS_JOB_RUNNING = 6, - NS_JOB_WAITING = 7, -} ns_job_state_t; - -/* this is our "kitchen sink" pblock/glue object that is the main - interface between the app/thread pool/event framework */ -typedef struct ns_job_t -{ - pthread_mutex_t monitor; - pthread_cond_t notify; - struct ns_thrpool_t *tp; - ns_job_func_t func; - struct ns_job_data_t *data; - ns_job_type_t job_type; /* NS_JOB_ACCEPT etc. */ - PRFileDesc *fd; /* for I/O events */ - struct timeval tv; /* used for timed events */ - int signal; /* if the event was triggered by a signal, this is the signal number */ - ns_event_fw_fd_t *ns_event_fw_fd; /* event framework fd event object */ - ns_event_fw_time_t *ns_event_fw_time; /* event framework timer event object */ - ns_event_fw_sig_t *ns_event_fw_sig; /* event framework signal event object */ - ns_job_type_t output_job_type; /* info about event that triggered the callback */ - ns_job_state_t state; /* What state the job is currently in. */ - ns_event_fw_ctx_t *ns_event_fw_ctx; - void *(*alloc_event_context)(size_t size, struct ns_job_t *job); - void (*free_event_context)(void *ev_ctx, struct ns_job_t *job); - /* this is called by the event framework when an event is triggered */ - void (*event_cb)(struct ns_job_t *tpec); - /* Called during dispose of the job, to allow user defined cleanup */ - ns_job_func_t done_cb; -} ns_job_t; - -typedef void (*ns_event_fw_accept_cb_t)(PRFileDesc *fd, ns_job_t *job); -typedef void (*ns_event_fw_read_cb_t)(PRFileDesc *fd, ns_job_t *job); -typedef void (*ns_event_fw_write_cb_t)(PRFileDesc *fd, ns_job_t *job); -typedef void (*ns_event_fw_connect_cb_t)(PRFileDesc *fd, ns_job_t *job); - -typedef struct ns_event_fw_t -{ - ns_event_fw_ctx_t *(*ns_event_fw_init)(void); - /* -struct event_base *event_base_new(void); -struct tevent_context *tevent_context_init(TALLOC_CTX *mem_ctx); - */ - void (*ns_event_fw_destroy)(ns_event_fw_ctx_t *ns_event_fw_ctx); - /* -void event_base_free(struct event_base *); -int talloc_free(void *ptr); - */ - int (*ns_event_fw_loop)(ns_event_fw_ctx_t *ns_event_fw_ctx); /* run the event fw main loop */ - /* - the return value - 1 = no events to process; 0 = normal termination; -1 = error -int event_base_loop(struct event_base *, int); -int tevent_loop_wait(struct tevent_context *ev) - */ - void (*ns_event_fw_add_io)( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job); - /* -void event_set(struct event *, int, short, void (*)(int, short, void *), void *); -int event_add(struct event *ev, const struct timeval *timeout); - -#define tevent_fd * tevent_add_fd(ev, mem_ctx, fd, flags, handler, private_data) \ - */ - void (*ns_event_fw_mod_io)( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job); - /* write, accept, etc. */ - void (*ns_event_fw_add_timer)( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job); - /* -tevent_timer *tevent_add_timer(ev, mem_ctx, next_event, handler, private_data) - -evtimer_set(ev, cb, arg) -evtimer_add(ev, tv) - */ - void (*ns_event_fw_mod_timer)( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job); - void (*ns_event_fw_add_signal)( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job); - /* -struct tevent_signal *tevent_add_signal(ev, mem_ctx, signum, sa_flags, handler, private_data) - -signal_set(ev, x, cb, arg) -signal_add(ev, tv) - */ - void (*ns_event_fw_mod_signal)( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job); - void (*ns_event_fw_io_event_done)( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job); - void (*ns_event_fw_timer_event_done)( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job); - void (*ns_event_fw_signal_event_done)( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job); -} ns_event_fw_t; - -ns_event_fw_t *get_event_framework_event(void); -ns_event_fw_t *get_event_framework_tevent(void); - -#endif /* THRPOOL_NS_EVENT_FW_H */ diff --git a/src/nunc-stans/ns/ns_event_fw_event.c b/src/nunc-stans/ns/ns_event_fw_event.c deleted file mode 100644 index 9b96c99..0000000 --- a/src/nunc-stans/ns/ns_event_fw_event.c +++ /dev/null @@ -1,336 +0,0 @@ -/* --- BEGIN COPYRIGHT BLOCK --- - * Copyright (C) 2015 Red Hat - * see files 'COPYING' and 'COPYING.openssl' for use and warranty - * information - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Additional permission under GPLv3 section 7: - * - * If you modify this Program, or any covered work, by linking or - * combining it with OpenSSL, or a modified version of OpenSSL licensed - * under the OpenSSL license - * (https://www.openssl.org/source/license.html), the licensors of this - * Program grant you additional permission to convey the resulting - * work. Corresponding Source for a non-source form of such a - * combination shall include the source code for the parts that are - * licensed under the OpenSSL license as well as that of the covered - * work. - * --- END COPYRIGHT BLOCK --- - */ -#ifdef HAVE_CONFIG_H -#include -#endif - -/* - * This implements the thrpool event framework interface - * to the libevent event framework - */ -#include -#include -#include -typedef struct event_base ns_event_fw_ctx_t; -typedef struct event ns_event_fw_fd_t; -typedef struct event ns_event_fw_time_t; -typedef struct event ns_event_fw_sig_t; -#define NO_EVENT_TYPEDEFS -#include "ns_event_fw.h" -#include - -static void -event_logger_cb(int severity, const char *msg) -{ - int priority = 0; - char *text = NULL; - - switch (severity) { - case _EVENT_LOG_DEBUG: - priority = LOG_DEBUG; - break; - case _EVENT_LOG_MSG: - priority = LOG_DEBUG; - break; - case _EVENT_LOG_WARN: - priority = LOG_DEBUG; - break; - case _EVENT_LOG_ERR: - priority = LOG_ERR; - break; - default: - priority = LOG_ERR; - break; /* never reached */ - } - /* need to append newline */ - text = PR_smprintf("%s\n", msg); - ns_log(priority, text); - ns_free(text); -} - -static ns_job_type_t -event_flags_to_type(short events) -{ - /* The volatile here prevents gcc rearranging this code within the thread. */ - volatile ns_job_type_t job_type = 0; - - /* Either we timeout *or* we are a real event */ - if (!(events & EV_TIMEOUT)) { - if (events & EV_READ) { - job_type |= NS_JOB_READ; - } - if (events & EV_WRITE) { - job_type |= NS_JOB_WRITE; - } - if (events & EV_SIGNAL) { - job_type |= NS_JOB_SIGNAL; - } - } else { - job_type = NS_JOB_TIMER; - } - return job_type; -} - -/* this is called by the event main loop when an event - is triggered - this "maps" the event library interface - to our nspr/thrpool interface */ -static void -event_cb(int fd, short event, void *arg) -{ - ns_job_t *job = (ns_job_t *)arg; - - PR_ASSERT(arg); - if (job->fd && fd > 0) { - PR_ASSERT(fd == PR_FileDesc2NativeHandle(job->fd)); - } - - job->output_job_type = event_flags_to_type(event); - - job->event_cb(job); -} - -/* convert from job job_type to event fw type */ -static short -job_type_to_flags(ns_job_type_t job_type) -{ - short flags = 0; - if (NS_JOB_IS_ACCEPT(job_type) || NS_JOB_IS_READ(job_type)) { - flags |= EV_READ; - } - if (NS_JOB_IS_WRITE(job_type) || NS_JOB_IS_CONNECT(job_type)) { - flags |= EV_WRITE; - } - if (NS_JOB_IS_PERSIST(job_type)) { - flags |= EV_PERSIST; - } - if (NS_JOB_IS_TIMER(job_type)) { - flags |= EV_TIMEOUT; - } - if (NS_JOB_IS_SIGNAL(job_type)) { - flags |= EV_SIGNAL; - } - return flags; -} - -static ns_event_fw_ctx_t * -ns_event_fw_init(void) -{ - event_set_log_callback(event_logger_cb); -#ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED - event_set_mem_functions(ns_malloc, ns_realloc, ns_free); -#endif - return event_base_new(); -} - -static void -ns_event_fw_destroy(ns_event_fw_ctx_t *event_ctx_t) -{ - event_base_free(event_ctx_t); -} - -/* - * remove just removes the event from active consideration - * by the event framework - the event object may be - * reactivated by calling mod - event done will actually - * remove and free the object - */ -static void -ns_event_fw_io_event_remove( - ns_event_fw_ctx_t *ns_event_fw_ctx __attribute__((unused)), - ns_job_t *job) -{ - event_del(job->ns_event_fw_fd); -} - -static void -ns_event_fw_io_event_done( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job) -{ - ns_event_fw_io_event_remove(ns_event_fw_ctx, job); - job->free_event_context(job->ns_event_fw_fd, job); - job->ns_event_fw_fd = NULL; -} - -static void -ns_event_fw_timer_event_remove( - ns_event_fw_ctx_t *ns_event_fw_ctx __attribute__((unused)), - ns_job_t *job) -{ - evtimer_del(job->ns_event_fw_time); -} - -static void -ns_event_fw_timer_event_done( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job) -{ - ns_event_fw_timer_event_remove(ns_event_fw_ctx, job); - job->free_event_context(job->ns_event_fw_time, job); - job->ns_event_fw_time = NULL; -} - -static void -ns_event_fw_signal_event_remove( - ns_event_fw_ctx_t *ns_event_fw_ctx __attribute__((unused)), - ns_job_t *job) -{ - evsignal_del(job->ns_event_fw_sig); -} - -static void -ns_event_fw_signal_event_done( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job) -{ - ns_event_fw_signal_event_remove(ns_event_fw_ctx, job); - job->free_event_context(job->ns_event_fw_sig, job); - job->ns_event_fw_sig = NULL; -} - -static void -ns_event_fw_add_io( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job) -{ - struct timeval *tv = NULL; - /* allocate a new event structure - use the job for the memory context */ - struct event *ev = job->alloc_event_context(sizeof(struct event), job); - /* set the fields in the event */ - short flags = job_type_to_flags(job->job_type); - - event_assign(ev, ns_event_fw_ctx, PR_FileDesc2NativeHandle(job->fd), flags, event_cb, job); - if (job->tv.tv_sec || job->tv.tv_usec) { - tv = &job->tv; - } - event_add(ev, tv); - job->ns_event_fw_fd = ev; -} - -static void -ns_event_fw_mod_io( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job) -{ - struct timeval *tv = NULL; - short events = job_type_to_flags(job->job_type); - - if (job->tv.tv_sec || job->tv.tv_usec) { - tv = &job->tv; - } - if (events) { - job->ns_event_fw_fd->ev_events = events; - event_add(job->ns_event_fw_fd, tv); - } else { - /* setting the job_type to remove IO events will remove it from the event system */ - ns_event_fw_io_event_remove(ns_event_fw_ctx, job); - } -} - -static void -ns_event_fw_add_timer( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job) -{ - /* allocate a new event structure - use the job for the memory context */ - struct event *ev = job->alloc_event_context(sizeof(struct event), job); - evtimer_assign(ev, ns_event_fw_ctx, event_cb, job); - evtimer_add(ev, &job->tv); - job->ns_event_fw_time = ev; -} - -static void -ns_event_fw_mod_timer( - ns_event_fw_ctx_t *ns_event_fw_ctx __attribute__((unused)), - ns_job_t *job) -{ - evtimer_add(job->ns_event_fw_time, &job->tv); -} - -static void -ns_event_fw_add_signal( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job) -{ - /* allocate a new event structure - use the job for the memory context */ - struct event *ev = job->alloc_event_context(sizeof(struct event), job); - evsignal_assign(ev, ns_event_fw_ctx, job->signal, event_cb, job); - evsignal_add(ev, NULL); - job->ns_event_fw_sig = ev; -} - -static void -ns_event_fw_mod_signal( - ns_event_fw_ctx_t *ns_event_fw_ctx __attribute__((unused)), - ns_job_t *job) -{ - /* If the event in the ev argument already has a scheduled timeout, calling event_add() replaces the old timeout with the new one, or clears the old timeout if the timeout argument is NULL. */ - evsignal_add(job->ns_event_fw_sig, NULL); -} - -/* returns - 1 - no events to process - 0 - normal termination - -1 - error -*/ -static int -ns_event_fw_loop(ns_event_fw_ctx_t *ns_event_fw_ctx) -{ - int rc = event_base_loop(ns_event_fw_ctx, EVLOOP_ONCE); - if (rc == 0) { - rc = 1; - } else { - rc = -1; - } - - return rc; -} - -static ns_event_fw_t ns_event_fw_event = { - ns_event_fw_init, - ns_event_fw_destroy, - ns_event_fw_loop, - ns_event_fw_add_io, - ns_event_fw_mod_io, - ns_event_fw_add_timer, - ns_event_fw_mod_timer, - ns_event_fw_add_signal, - ns_event_fw_mod_signal, - ns_event_fw_io_event_done, - ns_event_fw_timer_event_done, - ns_event_fw_signal_event_done}; - -ns_event_fw_t * -get_event_framework_event() -{ - return &ns_event_fw_event; -} diff --git a/src/nunc-stans/ns/ns_event_fw_tevent.c b/src/nunc-stans/ns/ns_event_fw_tevent.c deleted file mode 100644 index 943989f..0000000 --- a/src/nunc-stans/ns/ns_event_fw_tevent.c +++ /dev/null @@ -1,328 +0,0 @@ -/* --- BEGIN COPYRIGHT BLOCK --- - * Copyright (C) 2015 Red Hat - * see files 'COPYING' and 'COPYING.openssl' for use and warranty - * information - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Additional permission under GPLv3 section 7: - * - * If you modify this Program, or any covered work, by linking or - * combining it with OpenSSL, or a modified version of OpenSSL licensed - * under the OpenSSL license - * (https://www.openssl.org/source/license.html), the licensors of this - * Program grant you additional permission to convey the resulting - * work. Corresponding Source for a non-source form of such a - * combination shall include the source code for the parts that are - * licensed under the OpenSSL license as well as that of the covered - * work. - * --- END COPYRIGHT BLOCK --- - */ -#ifdef HAVE_CONFIG_H -#include -#endif - -/* - * This implements the thrpool event framework interface - * to the libtevent event framework - */ -#include -#include -#include -typedef struct tevent_context ns_event_fw_ctx_t; -typedef struct tevent_fd ns_event_fw_fd_t; -typedef struct tevent_timer ns_event_fw_time_t; -typedef struct tevent_signal ns_event_fw_sig_t; -#define NO_EVENT_TYPEDEFS -#include "ns_event_fw.h" -#include - -static void ns_event_fw_add_io(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t *job); -static void ns_event_fw_mod_io(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t *job); -static void ns_event_fw_add_timer(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t *job); -static void ns_event_fw_mod_timer(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t *job); -static void ns_event_fw_add_signal(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t *job); -static void ns_event_fw_mod_signal(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t *job); -static void ns_event_fw_io_event_remove(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t *job); -static void ns_event_fw_io_event_done(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t *job); -static void ns_event_fw_timer_event_done(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t *job); -static void ns_event_fw_signal_event_done(ns_event_fw_ctx_t *ns_event_fw_ctx, ns_job_t *job); - -static void -tevent_logger_cb(void *context __attribute__((unused)), enum tevent_debug_level level, const char *fmt, va_list ap) -{ - int priority = 0; - char *msg = NULL; - - switch (level) { - case TEVENT_DEBUG_FATAL: - priority = LOG_ERR; - break; - case TEVENT_DEBUG_ERROR: - priority = LOG_ERR; - break; - case TEVENT_DEBUG_WARNING: - priority = LOG_DEBUG; - break; - case TEVENT_DEBUG_TRACE: - priority = LOG_DEBUG; - break; - default: - priority = LOG_DEBUG; - break; /* never reached */ - } - - msg = PR_smprintf("%s\n", fmt); - ns_log_valist(priority, msg, ap); - ns_free(msg); -} - -static ns_job_type_t -event_flags_to_type(uint16_t flags) -{ - ns_job_type_t job_type = 0; - if (flags & TEVENT_FD_READ) { - job_type |= NS_JOB_READ; - } - if (flags & TEVENT_FD_WRITE) { - job_type |= NS_JOB_WRITE; - } - return job_type; -} - -/* this is called by the event main loop when an fd event - is triggered - this "maps" the event library interface - to our nspr/thrpool interface */ -static void -fd_event_cb(struct tevent_context *ev __attribute__((unused)), struct tevent_fd *fde __attribute__((unused)), uint16_t flags, void *arg) -{ - ns_job_t *job = (ns_job_t *)arg; - - PR_ASSERT(arg); - - /* I/O jobs can be timed - if we get an fd event, cancel the timer */ - if (job->ns_event_fw_time) { - ns_event_fw_timer_event_done(job->ns_event_fw_ctx, job); - } - job->output_job_type = event_flags_to_type(flags); - job->event_cb(job); -} - -/* this is called by the event main loop when a timer event - is triggered - this "maps" the event library interface - to our nspr/thrpool interface */ -static void -timer_event_cb(struct tevent_context *ev __attribute__((unused)), struct tevent_timer *te __attribute__((unused)), struct timeval current_time __attribute__((unused)), void *arg) -{ - ns_job_t *job = (ns_job_t *)arg; - - PR_ASSERT(arg); - - /* I/O jobs can be timed - if we get an timer event, cancel the fd */ - if (job->ns_event_fw_fd) { - ns_event_fw_io_event_done(job->ns_event_fw_ctx, job); - } - job->output_job_type = NS_JOB_TIMER; - job->event_cb(job); -} - -/* this is called by the event main loop when a signal event - is triggered - this "maps" the event library interface - to our nspr/thrpool interface */ -static void -signal_event_cb(struct tevent_context *ev __attribute__((unused)), struct tevent_signal *se __attribute__((unused)), int signum, int count __attribute__((unused)), void *siginfo __attribute__((unused)), void *arg) -{ - ns_job_t *job = (ns_job_t *)arg; - - PR_ASSERT(arg); - - job->output_job_type = NS_JOB_SIGNAL; - job->signal = signum; - job->event_cb(job); -} - -/* convert from job job_type to event fw type */ -static uint16_t -job_type_to_flags(ns_job_type_t job_type) -{ - uint16_t flags = 0; - if (NS_JOB_IS_ACCEPT(job_type) || NS_JOB_IS_READ(job_type)) { - flags |= TEVENT_FD_READ; - } - if (NS_JOB_IS_WRITE(job_type) || NS_JOB_IS_CONNECT(job_type)) { - flags |= TEVENT_FD_WRITE; - } - return flags; -} - -static ns_event_fw_ctx_t * -ns_event_fw_init(void) -{ - ns_event_fw_ctx_t *ev = tevent_context_init(NULL); - tevent_set_debug(ev, tevent_logger_cb, NULL); - return ev; -} - -static void -ns_event_fw_destroy(ns_event_fw_ctx_t *ns_event_fw_ctx_t) -{ - talloc_free(ns_event_fw_ctx_t); -} - -/* - * remove just removes the event from active consideration - * by the event framework - the event object may be - * reactivated by calling mod - event done will actually - * remove and free the object - */ -static void -ns_event_fw_io_event_remove( - ns_event_fw_ctx_t *ns_event_fw_ctx __attribute__((unused)), - ns_job_t *job) -{ - tevent_fd_set_flags(job->ns_event_fw_fd, 0); -} - -static void -ns_event_fw_io_event_done( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job) -{ - ns_event_fw_io_event_remove(ns_event_fw_ctx, job); - talloc_free(job->ns_event_fw_fd); - job->ns_event_fw_fd = NULL; -} - -static void -ns_event_fw_timer_event_done( - ns_event_fw_ctx_t *ns_event_fw_ctx __attribute__((unused)), - ns_job_t *job) -{ - talloc_free(job->ns_event_fw_time); - job->ns_event_fw_time = NULL; -} - -static void -ns_event_fw_signal_event_done( - ns_event_fw_ctx_t *ns_event_fw_ctx __attribute__((unused)), - ns_job_t *job) -{ - talloc_free(job->ns_event_fw_sig); - job->ns_event_fw_sig = NULL; -} - -static void -ns_event_fw_add_io( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job) -{ - /* set the fields in the event */ - uint16_t flags = job_type_to_flags(job->job_type); - - job->ns_event_fw_fd = tevent_add_fd(ns_event_fw_ctx, ns_event_fw_ctx, - PR_FileDesc2NativeHandle(job->fd), - flags, fd_event_cb, job); - /* add/schedule the timer event */ - if (job->tv.tv_sec || job->tv.tv_usec) { - job->ns_event_fw_time = tevent_add_timer(ns_event_fw_ctx, ns_event_fw_ctx, - job->tv, timer_event_cb, job); - } -} - -static void -ns_event_fw_mod_io( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job) -{ - uint16_t events = job_type_to_flags(job->job_type); - - if (job->tv.tv_sec || job->tv.tv_usec) { - ns_event_fw_mod_timer(ns_event_fw_ctx, job); - } - tevent_fd_set_flags(job->ns_event_fw_fd, events); -} - -static void -ns_event_fw_add_timer( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job) -{ - job->ns_event_fw_time = tevent_add_timer(ns_event_fw_ctx, ns_event_fw_ctx, - job->tv, timer_event_cb, job); -} - -static void -ns_event_fw_mod_timer( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job) -{ - ns_event_fw_timer_event_done(ns_event_fw_ctx, job); - ns_event_fw_add_timer(ns_event_fw_ctx, job); -} - -static void -ns_event_fw_add_signal( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job) -{ - job->ns_event_fw_sig = tevent_add_signal(ns_event_fw_ctx, ns_event_fw_ctx, - job->signal, 0, signal_event_cb, job); -} - -static void -ns_event_fw_mod_signal( - ns_event_fw_ctx_t *ns_event_fw_ctx, - ns_job_t *job) -{ - ns_event_fw_signal_event_done(ns_event_fw_ctx, job); - ns_event_fw_add_signal(ns_event_fw_ctx, job); -} - -/* returns - 1 - no events to process - 0 - normal termination - -1 - error -*/ -static int -ns_event_fw_loop(ns_event_fw_ctx_t *ns_event_fw_ctx) -{ - int rc = tevent_loop_once(ns_event_fw_ctx); - if (rc == 0) { - rc = 1; - } else { - rc = -1; - } - /* tevent apparently has no normal termination return value */ - return rc; -} - -static ns_event_fw_t ns_event_fw_tevent = { - ns_event_fw_init, - ns_event_fw_destroy, - ns_event_fw_loop, - ns_event_fw_add_io, - ns_event_fw_mod_io, - ns_event_fw_add_timer, - ns_event_fw_mod_timer, - ns_event_fw_add_signal, - ns_event_fw_mod_signal, - ns_event_fw_io_event_done, - ns_event_fw_timer_event_done, - ns_event_fw_signal_event_done}; - -ns_event_fw_t * -get_event_framework_tevent() -{ - return &ns_event_fw_tevent; -} diff --git a/src/nunc-stans/ns/ns_private.h b/src/nunc-stans/ns/ns_private.h deleted file mode 100644 index a3f6b77..0000000 --- a/src/nunc-stans/ns/ns_private.h +++ /dev/null @@ -1,106 +0,0 @@ -/* --- BEGIN COPYRIGHT BLOCK --- - * Copyright (C) 2015 Red Hat - * see files 'COPYING' and 'COPYING.openssl' for use and warranty - * information - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Additional permission under GPLv3 section 7: - * - * If you modify this Program, or any covered work, by linking or - * combining it with OpenSSL, or a modified version of OpenSSL licensed - * under the OpenSSL license - * (https://www.openssl.org/source/license.html), the licensors of this - * Program grant you additional permission to convey the resulting - * work. Corresponding Source for a non-source form of such a - * combination shall include the source code for the parts that are - * licensed under the OpenSSL license as well as that of the covered - * work. - * --- END COPYRIGHT BLOCK --- - */ -/*! \file ns_private.h - \brief Nunc Stans private API - - This is the private API for Nunc Stans -*/ -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "nspr.h" -#include "prmon.h" -#include - -#include - -/** - * Forward declaration of the thread struct - internal - * - * The actual struct is opaque to applications. The forward declaration is here - * for the typedef. - */ -struct ns_thread_t; -/** - * This is the thread type - internal - * - * The actual thread type is opaque to applications. - */ -typedef struct ns_thread_t ns_thread_t; - -/** - * Log an error to logging system. - internal - * \param priority The log level - * \param fmt The log message string format - */ -void ns_log(int priority, const char *fmt, ...); - -/** - * Log an error to logging system. - internal - * \param priority The log level - * \param fmt The log message string format - * \param varg The va_list arg - */ -void ns_log_valist(int priority, const char *fmt, va_list varg); - -/** - * \param size - Memory allocation size - internal - * \return - allocated memory - */ -void *ns_malloc(size_t size); - -/** - * \param size - Memory allocation size - internal - * \param alignment - The allignment of the memory. Must be a power of two. - * \return - allocated memory - */ -void *ns_memalign(size_t size, size_t alignment); - -/** - * \param count - number of items - internal - * \param size - Memory allocation size - * \return - allocated memory - */ -void *ns_calloc(size_t count, size_t size); - -/** - * \param ptr - pointer to the memory block - internal - * \param size - size of allocation - * \return - allocated memory - */ -void *ns_realloc(void *ptr, size_t size); - -/** - * \param ptr - pointer to memory to be freed - internal - */ -void ns_free(void *ptr); diff --git a/src/nunc-stans/ns/ns_thrpool.c b/src/nunc-stans/ns/ns_thrpool.c deleted file mode 100644 index 774607c..0000000 --- a/src/nunc-stans/ns/ns_thrpool.c +++ /dev/null @@ -1,1688 +0,0 @@ -/* --- BEGIN COPYRIGHT BLOCK --- - * Copyright (C) 2015 Red Hat - * see files 'COPYING' and 'COPYING.openssl' for use and warranty - * information - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Additional permission under GPLv3 section 7: - * - * If you modify this Program, or any covered work, by linking or - * combining it with OpenSSL, or a modified version of OpenSSL licensed - * under the OpenSSL license - * (https://www.openssl.org/source/license.html), the licensors of this - * Program grant you additional permission to convey the resulting - * work. Corresponding Source for a non-source form of such a - * combination shall include the source code for the parts that are - * licensed under the OpenSSL license as well as that of the covered - * work. - * --- END COPYRIGHT BLOCK --- - */ -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include -#endif -#ifdef HAVE_SYSLOG_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_INTTYPES_H -#include -#endif - -#include -#include -#include "ns_event_fw.h" - -/* SDS contains the lock free queue wrapper */ -#include - -#include - - -/* - * Threadpool - */ -struct ns_thrpool_t -{ - sds_lqueue *work_q; - sds_lqueue *event_q; - int32_t shutdown; - int32_t shutdown_event_loop; - pthread_cond_t work_q_cv; - pthread_mutex_t work_q_lock; - sds_queue *thread_stack; - uint32_t thread_count; - pthread_t event_thread; - PRFileDesc *event_q_wakeup_pipe_read; - PRFileDesc *event_q_wakeup_pipe_write; - ns_job_t *event_q_wakeup_job; - ns_event_fw_t *ns_event_fw; - ns_event_fw_ctx_t *ns_event_fw_ctx; - size_t stacksize; -}; - -struct ns_thread_t -{ - pthread_t thr; /* the thread */ - struct ns_thrpool_t *tp; /* pointer back to thread pool */ -}; - -#define ERRNO_WOULD_BLOCK(iii) (iii == EWOULDBLOCK) || (iii == EAGAIN) - -/* Forward declarations. */ -static void internal_ns_job_done(ns_job_t *job); -static void internal_ns_job_rearm(ns_job_t *job); -static void work_job_execute(ns_job_t *job); -static void event_q_notify(ns_job_t *job); -static void work_q_notify(ns_job_t *job); - -/* logging function pointers */ -static void (*logger)(int, const char *, va_list) = NULL; -static void (*log_start)(void) = NULL; -static void (*log_close)(void) = NULL; - -/* memory function pointers */ -static void *(*malloc_fct)(size_t) = NULL; -static void *(*memalign_fct)(size_t size, size_t alignment) = NULL; -static void *(*calloc_fct)(size_t, size_t) = NULL; -static void *(*realloc_fct)(void *, size_t) = NULL; -static void (*free_fct)(void *) = NULL; - -/* syslog functions */ -static void -ns_syslog(int priority, const char *fmt, va_list varg) -{ - vsyslog(priority, fmt, varg); -} - -static void -ns_syslog_start(void) -{ - openlog("nunc-stans", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); -} - -static void -ns_syslog_close(void) -{ - closelog(); -} - -/* default memory functions */ -static void * -os_malloc(size_t size) -{ - return malloc(size); -} - -static void * -os_memalign_malloc(size_t size, size_t alignment) -{ - void *ptr; - if (posix_memalign(&ptr, alignment, size) != 0) { - return NULL; - } - return ptr; -} - -static void * -os_calloc(size_t count, size_t size) -{ - return calloc(count, size); -} - -static void * -os_realloc(void *ptr, size_t size) -{ - return realloc(ptr, size); -} - -static void -os_free(void *ptr) -{ - free(ptr); -} - -int32_t -ns_thrpool_is_shutdown(struct ns_thrpool_t *tp) -{ - int32_t result = 0; -#ifdef ATOMIC_64BIT_OPERATIONS - __atomic_load(&(tp->shutdown), &result, __ATOMIC_ACQUIRE); -#else - result = PR_AtomicAdd(&(tp->shutdown), 0); -#endif - return result; -} - -int32_t -ns_thrpool_is_event_shutdown(struct ns_thrpool_t *tp) -{ - int32_t result = 0; -#ifdef ATOMIC_64BIT_OPERATIONS - __atomic_load(&(tp->shutdown_event_loop), &result, __ATOMIC_ACQUIRE); -#else - result = PR_AtomicAdd(&(tp->shutdown_event_loop), 0); -#endif - return result; -} - -static int32_t -validate_event_timeout(struct timeval *tv) -{ - if (tv->tv_sec < 0 || tv->tv_usec < 0) { - /* If we get here, you have done something WRONG */ - return 1; - } - return 0; -} - -static void -job_queue_cleanup(void *arg) -{ - struct ns_job_t *job = (ns_job_t *)arg; - if (job != NULL) { -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_queue_job_cleanup: destroying job %x\n", job); -#endif - internal_ns_job_done(job); - } -} - -static void -internal_ns_job_done(ns_job_t *job) -{ - pthread_mutex_lock(&(job->monitor)); -#ifdef DEBUG - ns_log(LOG_DEBUG, "internal_ns_job_done %x state %d moving to NS_JOB_DELETED\n", job, job->state); -#endif - /* In theory, due to the monitor placement, this should never be able to be seen by any other thread ... */ - job->state = NS_JOB_DELETED; - - if (job->ns_event_fw_fd) { - job->tp->ns_event_fw->ns_event_fw_io_event_done(job->tp->ns_event_fw_ctx, job); - } - if (job->ns_event_fw_time) { - job->tp->ns_event_fw->ns_event_fw_timer_event_done(job->tp->ns_event_fw_ctx, job); - } - if (job->ns_event_fw_sig) { - job->tp->ns_event_fw->ns_event_fw_signal_event_done(job->tp->ns_event_fw_ctx, job); - } - - if (job->fd && !NS_JOB_IS_PRESERVE_FD(job->job_type)) { - PR_Close(job->fd); - } /* else application is responsible for fd */ - - if (job->done_cb != NULL) { - job->done_cb(job); - } - - pthread_mutex_unlock(&(job->monitor)); - pthread_mutex_destroy(&(job->monitor)); - pthread_cond_destroy(&(job->notify)); - - ns_free(job); -} - -/* Is there a way to get a status back from here? */ -static void -internal_ns_job_rearm(ns_job_t *job) -{ - pthread_mutex_lock(&(job->monitor)); - PR_ASSERT(job->state == NS_JOB_NEEDS_ARM); -/* Don't think I need to check persistence here, it could be the first arm ... */ -#ifdef DEBUG - ns_log(LOG_DEBUG, "internal_ns_rearm_job %x state %d moving to NS_JOB_ARMED\n", job, job->state); -#endif - job->state = NS_JOB_ARMED; - - /* I think we need to check about is_shutdown here? */ - - if (NS_JOB_IS_IO(job->job_type) || NS_JOB_IS_TIMER(job->job_type) || NS_JOB_IS_SIGNAL(job->job_type)) { - event_q_notify(job); - } else { - /* if this is a non event task, just queue it on the work q */ - /* Prevents an un-necessary queue / dequeue to the event_q */ - work_q_notify(job); - } - pthread_mutex_unlock(&(job->monitor)); -} - -static void -work_job_execute(ns_job_t *job) -{ - /* - * when we pull out a job to work on it, we do it here rather than calling - * job function directly. - * - * DANGER: After this is called, you must NOT ACCESS job again. It MAY be - * DELETED! Crashes abound, you have been warned ... - */ - PR_ASSERT(job); - pthread_mutex_lock(&(job->monitor)); -#ifdef DEBUG - ns_log(LOG_DEBUG, "work_job_execute %x state %d moving to NS_JOB_RUNNING\n", job, job->state); -#endif - job->state = NS_JOB_RUNNING; - /* Do the work. */ - PR_ASSERT(job->func); - job->func(job); - - /* Only if !threaded job, and persistent, we automatically tell us to rearm */ - if (!NS_JOB_IS_THREAD(job->job_type) && NS_JOB_IS_PERSIST(job->job_type) && job->state == NS_JOB_RUNNING) { -#ifdef DEBUG - ns_log(LOG_DEBUG, "work_job_execute PERSIST and RUNNING, remarking %x as NS_JOB_NEEDS_ARM\n", job); -#endif - job->state = NS_JOB_NEEDS_ARM; - } - - if (job->state == NS_JOB_NEEDS_DELETE) { -/* Send it to the job mincer! */ -#ifdef DEBUG - ns_log(LOG_DEBUG, "work_job_execute %x state %d job func complete, sending to job_done...\n", job, job->state); -#endif - /* - * Let waiters know we are done, they'll pick up once - * we unlock. - */ - pthread_cond_signal(&(job->notify)); - pthread_mutex_unlock(&(job->monitor)); - internal_ns_job_done(job); - /* MUST NOT ACCESS JOB AGAIN.*/ - } else if (job->state == NS_JOB_NEEDS_ARM) { -#ifdef DEBUG - ns_log(LOG_DEBUG, "work_job_execute %x state %d job func complete, sending to rearm...\n", job, job->state); -#endif - /* Rearm the job! */ - /* We *don't* notify here because we ARE NOT done! */ - pthread_mutex_unlock(&(job->monitor)); - internal_ns_job_rearm(job); - } else { -#ifdef DEBUG - ns_log(LOG_DEBUG, "work_job_execute %x state %d job func complete move to NS_JOB_WAITING\n", job, job->state); -#endif - /* This should never occur? What's going on ... */ - PR_ASSERT(!NS_JOB_IS_PERSIST(job->job_type)); - /* We are now idle, set waiting. */ - job->state = NS_JOB_WAITING; - /* - * Let waiters know we are done, they'll pick up once - * we unlock. - */ - pthread_cond_signal(&(job->notify)); - pthread_mutex_unlock(&(job->monitor)); - } - /* MUST NOT ACCESS JOB AGAIN */ -} - -static void -work_q_wait(ns_thrpool_t *tp) -{ - pthread_mutex_lock(&(tp->work_q_lock)); - pthread_cond_wait(&(tp->work_q_cv), &(tp->work_q_lock)); - pthread_mutex_unlock(&(tp->work_q_lock)); -} - -static void -work_q_notify(ns_job_t *job) -{ - PR_ASSERT(job); - pthread_mutex_lock(&(job->monitor)); -#ifdef DEBUG - ns_log(LOG_DEBUG, "work_q_notify %x state %d\n", job, job->state); -#endif - PR_ASSERT(job->state == NS_JOB_ARMED); - if (job->state != NS_JOB_ARMED) { - /* Maybe we should return some error here? */ - ns_log(LOG_ERR, "work_q_notify %x state %d is not ARMED, cannot queue!\n", job, job->state); - pthread_mutex_unlock(&(job->monitor)); - return; - } - /* MUST NOT ACCESS job after enqueue. So we stash tp.*/ - ns_thrpool_t *ltp = job->tp; - pthread_mutex_unlock(&(job->monitor)); - sds_lqueue_enqueue(ltp->work_q, (void *)job); - pthread_mutex_lock(&(ltp->work_q_lock)); - pthread_cond_signal(&(ltp->work_q_cv)); - pthread_mutex_unlock(&(ltp->work_q_lock)); - PR_Sleep(PR_INTERVAL_NO_WAIT); /* yield to allow worker thread to pick up job */ -} - -/* - * worker thread function - */ -static void * -worker_thread_func(void *arg) -{ - ns_thread_t *thr = (ns_thread_t *)arg; - ns_thrpool_t *tp = thr->tp; - sds_result result = SDS_SUCCESS; - int_fast32_t is_shutdown = 0; - - /* Get ready to use lock free ds */ - sds_lqueue_tprep(tp->work_q); - - /* - * Execute jobs until shutdown is set and the queues are empty. - */ - while (!is_shutdown) { - ns_job_t *job = NULL; - result = sds_lqueue_dequeue(tp->work_q, (void **)&job); - /* Don't need monitor here, job_dequeue barriers the memory for us. Job will be valid */ - /* Is it possible for a worker thread to get stuck here during shutdown? */ - if (result == SDS_LIST_EXHAUSTED) { - work_q_wait(tp); - } else if (result == SDS_SUCCESS && job != NULL) { - /* Even if we are shutdown here, we can process a job. */ - /* Should we just keep dequeing until we exhaust the list? */ - if (NS_JOB_IS_SHUTDOWN_WORKER(job->job_type)) { - ns_log(LOG_INFO, "worker_thread_func notified to shutdown!\n"); - internal_ns_job_done(job); - is_shutdown = 1; - } else { - work_job_execute(job); - } - /* MUST NOT ACCESS JOB FROM THIS POINT */ - } else { - ns_log(LOG_ERR, "worker_thread_func encountered a recoverable issue during processing of the queue\n"); - } - } - - ns_log(LOG_INFO, "worker_thread_func shutdown complete!\n"); - /* With sds, it cleans the thread on join automatically. */ - return NULL; -} - -/* - * Add a new event, or modify or delete an existing event - */ -static void -update_event(ns_job_t *job) -{ - PR_ASSERT(job); - pthread_mutex_lock(&(job->monitor)); -#ifdef DEBUG - ns_log(LOG_DEBUG, "update_event %x state %d\n", job, job->state); -#endif - PR_ASSERT(job->state == NS_JOB_NEEDS_DELETE || job->state == NS_JOB_ARMED); - if (job->state == NS_JOB_NEEDS_DELETE) { - pthread_mutex_unlock(&(job->monitor)); - internal_ns_job_done(job); - return; - } else if (NS_JOB_IS_IO(job->job_type) || job->ns_event_fw_fd) { - if (!job->ns_event_fw_fd) { - job->tp->ns_event_fw->ns_event_fw_add_io(job->tp->ns_event_fw_ctx, job); - } else { - job->tp->ns_event_fw->ns_event_fw_mod_io(job->tp->ns_event_fw_ctx, job); - } - pthread_mutex_unlock(&(job->monitor)); - /* We need these returns to prevent a race on the next else if condition when we release job->monitor */ - return; - } else if (NS_JOB_IS_TIMER(job->job_type) || job->ns_event_fw_time) { - if (!job->ns_event_fw_time) { - job->tp->ns_event_fw->ns_event_fw_add_timer(job->tp->ns_event_fw_ctx, job); - } else { - job->tp->ns_event_fw->ns_event_fw_mod_timer(job->tp->ns_event_fw_ctx, job); - } - pthread_mutex_unlock(&(job->monitor)); - return; - } else if (NS_JOB_IS_SIGNAL(job->job_type) || job->ns_event_fw_sig) { - if (!job->ns_event_fw_sig) { - job->tp->ns_event_fw->ns_event_fw_add_signal(job->tp->ns_event_fw_ctx, job); - } else { - job->tp->ns_event_fw->ns_event_fw_mod_signal(job->tp->ns_event_fw_ctx, job); - } - pthread_mutex_unlock(&(job->monitor)); - return; - } else { - /* It's a "run now" job. */ - if (NS_JOB_IS_THREAD(job->job_type)) { - pthread_mutex_unlock(&(job->monitor)); - work_q_notify(job); - } else { - pthread_mutex_unlock(&(job->monitor)); - event_q_notify(job); - } - } - - return; -} - -static void -event_q_wait(ns_thrpool_t *tp __attribute__((unused))) -{ - /* unused for now */ - /* the main event loop will do our "waiting" - waiting for events - to happen, or we can trigger an event to "wakeup" the event - loop (see event_q_notify) */ -} - -static void -event_q_wake(ns_thrpool_t *tp) -{ - int32_t len; - -/* Rather than trying to make anew event, tell the event loop to exit with no - * events. - */ -#ifdef DEBUG - ns_log(LOG_DEBUG, "event_q_wake attempting to wake event queue.\n"); -#endif - - /* NSPR I/O doesn't allow non-blocking signal pipes, so use write instead of PR_Write */ - len = write(PR_FileDesc2NativeHandle(tp->event_q_wakeup_pipe_write), - "a", 1); - if (1 != len) { - if ((errno == 0) || ERRNO_WOULD_BLOCK(errno)) { - ns_log(LOG_DEBUG, "Write blocked for wakeup pipe - ignore %d\n", - errno); - } else { - ns_log(LOG_ERR, "Error: could not write wakeup pipe: %d:%s\n", - errno, PR_ErrorToString(errno, PR_LANGUAGE_I_DEFAULT)); - } - } - PR_Sleep(PR_INTERVAL_NO_WAIT); /* yield to allow event thread to pick up event */ -#ifdef DEBUG - ns_log(LOG_DEBUG, "event_q_wake result. 0\n"); -#endif -} - -static void -event_q_notify(ns_job_t *job) -{ - ns_thrpool_t *tp = job->tp; - /* if we are being called from a thread other than the - event loop thread, we have to notify that thread to - perform the event work */ - if (pthread_equal(tp->event_thread, pthread_self()) != 0) { - /* If we are being run from the same thread as the event - loop thread, we can just update the event here */ - update_event(job); - } else { -/* The event loop may be waiting for events, and may wait a long - time by default if there are no events to process - since we - want to add an event, we have to "wake up" the event loop by - posting an event - this will cause the wakeup_cb to be called - which will empty the event_q and add all of the events - */ -/* NOTE: once job is queued, it may be deleted immediately in - * another thread, if the event loop picks up the deletion - * job before we can notify it below - so make sure not to - * refer to job after the enqueue. - */ -#ifdef DEBUG - ns_log(LOG_DEBUG, "event_q_notify enqueuing %x with state %d\n", job, job->state); -#endif - - sds_lqueue_enqueue(tp->event_q, (void *)job); - event_q_wake(tp); - } -} - -/* This is run inside the event loop thread, and only in the - event loop thread - This function pulls the io/timer/signal event requests off - the request queue, formats the events in the format required - by the event framework, and adds them -*/ -static void -get_new_event_requests(ns_thrpool_t *tp) -{ - ns_job_t *job = NULL; - while (sds_lqueue_dequeue(tp->event_q, (void **)&job) == SDS_SUCCESS) { - if (job != NULL) { -#ifdef DEBUG - ns_log(LOG_DEBUG, "get_new_event_requests Dequeuing %x with state %d\n", job, job->state); -#endif - update_event(job); -#ifdef DEBUG - } else { - ns_log(LOG_DEBUG, "get_new_event_requests Dequeuing NULL job\n"); -#endif - } - } -} - -static void * -event_loop_thread_func(void *arg) -{ - struct ns_thrpool_t *tp = (struct ns_thrpool_t *)arg; - int rc; - - sds_lqueue_tprep(tp->event_q); - - while (!ns_thrpool_is_event_shutdown(tp)) { - /* get new event requests */ - get_new_event_requests(tp); - /* process events */ - /* return 1 - no events ; 0 - normal exit ; -1 - error */ - rc = tp->ns_event_fw->ns_event_fw_loop(tp->ns_event_fw_ctx); -#ifdef DEBUG - ns_log(LOG_DEBUG, "event_loop_thread_func woke event queue. rc=%d\n", rc); -#endif - if (rc == -1) { /* error */ - } else if (rc == 0) { /* exiting */ - } else { /* no events to process */ - event_q_wait(tp); - } - } - return NULL; -} - -/* - * The event framework calls this function when it receives an - * event - the event framework does all of the work to map - * the framework flags, etc. into the job - * This function is called only in the event loop thread. - * If the THREAD flag is set, this means the job is to be - * handed off to the work q - otherwise, execute it now - * in this thread (the event loop thread) - * the function must be careful not to block the event - * loop thread or starvation will occur - */ -static void -event_cb(ns_job_t *job) -{ - PR_ASSERT(job); - /* - * Sometimes if we queue then request to delete a job REALLY fast, this - * will trigger - * Fix is that if ARMED we don't allow the job to move to NS_JOB_NEEDS_DELETE - */ - - /* There is no guarantee this won't be called once we start to enter the shutdown, especially with timers .... */ - pthread_mutex_lock(&(job->monitor)); - - PR_ASSERT(job->state == NS_JOB_ARMED || job->state == NS_JOB_NEEDS_DELETE); - if (job->state == NS_JOB_ARMED && NS_JOB_IS_THREAD(job->job_type)) { -#ifdef DEBUG - ns_log(LOG_DEBUG, "event_cb %x state %d threaded, send to work_q\n", job, job->state); -#endif - pthread_mutex_unlock(&(job->monitor)); - work_q_notify(job); - } else if (job->state == NS_JOB_NEEDS_DELETE) { -#ifdef DEBUG - ns_log(LOG_DEBUG, "event_cb %x state %d ignoring as NS_JOB_NEEDS_DELETE set\n", job, job->state); -#endif - /* - * If the job is in need of delete we IGNORE IT - * It's here because it's been QUEUED for deletion and *may* be coming - * from the thrpool destroy thread! - */ - pthread_mutex_unlock(&(job->monitor)); - - } else { -#ifdef DEBUG - ns_log(LOG_DEBUG, "event_cb %x state %d non-threaded, execute right meow\n", job, job->state); -#endif - /* Not threaded, execute now! */ - pthread_mutex_unlock(&(job->monitor)); - work_job_execute(job); - /* MUST NOT ACCESS JOB FROM THIS POINT */ - } -} - -static void -wakeup_cb(ns_job_t *job) -{ - int32_t len; - char buf[1]; - -#ifdef DEBUG - ns_log(LOG_DEBUG, "wakeup_cb %x state %d wakeup_cb\n", job, job->state); -#endif - - /* NSPR I/O doesn't allow non-blocking signal pipes, so use read instead of PR_Read */ - len = read(PR_FileDesc2NativeHandle(job->tp->event_q_wakeup_pipe_read), - buf, 1); - if (1 != len) { - if ((errno == 0) || ERRNO_WOULD_BLOCK(errno)) { - ns_log(LOG_DEBUG, "Read blocked for wakeup pipe - ignore %d\n", - errno); - } else { - ns_log(LOG_ERR, "Error: could not read wakeup pipe: %d:%s\n", - errno, PR_ErrorToString(errno, PR_LANGUAGE_I_DEFAULT)); - } - } - /* wakeup_cb is usually called because a worker thread has posted a new - event we need to add - get all new event requests */ - get_new_event_requests(job->tp); -} -/* convenience function for the event fw to use to allocate - space for its event fw event object - it is assumed that eventually job will have a memory region/arena - to use - these functions will be called in the event loop thread from - the event framework function that adds a new event -*/ -static void * -alloc_event_context(size_t size, ns_job_t *job __attribute__((unused))) -{ - return ns_malloc(size); -} - -static void -free_event_context(void *ev_ctx, ns_job_t *job __attribute__((unused))) -{ - ns_free(ev_ctx); -} - -static ns_job_t * -new_ns_job(ns_thrpool_t *tp, PRFileDesc *fd, ns_job_type_t job_type, ns_job_func_t func, struct ns_job_data_t *data) -{ - ns_job_t *job = ns_calloc(1, sizeof(ns_job_t)); - - pthread_mutexattr_t *monitor_attr = ns_calloc(1, sizeof(pthread_mutexattr_t)); - pthread_mutexattr_init(monitor_attr); - pthread_mutexattr_settype(monitor_attr, PTHREAD_MUTEX_RECURSIVE); - assert(pthread_mutex_init(&(job->monitor), monitor_attr) == 0); - assert(pthread_cond_init(&(job->notify), NULL) == 0); - ns_free(monitor_attr); - - job->tp = tp; - /* We have to have this due to our obsession of hiding struct contents ... */ - /* It's only used in tevent anyway .... */ - job->ns_event_fw_ctx = tp->ns_event_fw_ctx; - job->fd = fd; - job->func = func; - job->data = data; - job->alloc_event_context = alloc_event_context; - job->free_event_context = free_event_context; - job->event_cb = event_cb; - job->job_type = job_type; - job->done_cb = NULL; -#ifdef DEBUG - ns_log(LOG_DEBUG, "new_ns_job %x initial NS_JOB_WAITING\n", job); -#endif - job->state = NS_JOB_WAITING; - return job; -} - -static ns_job_t * -alloc_io_context(ns_thrpool_t *tp, PRFileDesc *fd, ns_job_type_t job_type, ns_job_func_t func, struct ns_job_data_t *data) -{ - ns_job_t *job = new_ns_job(tp, fd, job_type, func, data); - - return job; -} - -static ns_job_t * -alloc_timeout_context(ns_thrpool_t *tp, struct timeval *tv, ns_job_type_t job_type, ns_job_func_t func, struct ns_job_data_t *data) -{ - ns_job_t *job = new_ns_job(tp, NULL, NS_JOB_TIMER | job_type, func, data); - job->tv = *tv; - - return job; -} - -static ns_job_t * -alloc_signal_context(ns_thrpool_t *tp, PRInt32 signum, ns_job_type_t job_type, ns_job_func_t func, struct ns_job_data_t *data) -{ - ns_job_t *job = new_ns_job(tp, NULL, NS_JOB_SIGNAL | job_type, func, data); - job->signal = signum; - - return job; -} - -ns_result_t -ns_job_done(ns_job_t *job) -{ - PR_ASSERT(job); - if (job == NULL) { - return NS_INVALID_REQUEST; - } - - /* Get the shutdown state ONCE at the start, atomically */ - int32_t shutdown_state = ns_thrpool_is_shutdown(job->tp); - - pthread_mutex_lock(&(job->monitor)); - - if (job->state == NS_JOB_NEEDS_DELETE || job->state == NS_JOB_DELETED) { -/* Just return if the job has been marked for deletion */ -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_job_done %x tp shutdown -> %x state %d return early\n", job, shutdown_state, job->state); -#endif - pthread_mutex_unlock(&(job->monitor)); - return NS_SUCCESS; - } - - /* Do not allow an armed job to be removed UNLESS the server is shutting down */ - if (job->state == NS_JOB_ARMED && !shutdown_state) { -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_job_done %x tp shutdown -> false state %d failed to mark as done\n", job, job->state); -#endif - pthread_mutex_unlock(&(job->monitor)); - return NS_INVALID_STATE; - } - - if (job->state == NS_JOB_RUNNING || job->state == NS_JOB_NEEDS_ARM) { -/* For this to be called, and NS_JOB_RUNNING, we *must* be the callback thread! */ -/* Just mark it (ie do nothing), the work_job_execute function will trigger internal_ns_job_done */ -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_job_done %x tp shutdown -> false state %d setting to async NS_JOB_NEEDS_DELETE\n", job, job->state); -#endif - job->state = NS_JOB_NEEDS_DELETE; - pthread_mutex_unlock(&(job->monitor)); - } else if (!shutdown_state) { -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_job_done %x tp shutdown -> false state %d setting NS_JOB_NEEDS_DELETE and queuing\n", job, job->state); -#endif - job->state = NS_JOB_NEEDS_DELETE; - pthread_mutex_unlock(&(job->monitor)); - event_q_notify(job); - } else { -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_job_done %x tp shutdown -> true state %d setting NS_JOB_NEEDS_DELETE and delete immediate\n", job, job->state); -#endif - job->state = NS_JOB_NEEDS_DELETE; - /* We are shutting down, just remove it! */ - pthread_mutex_unlock(&(job->monitor)); - internal_ns_job_done(job); - } - return NS_SUCCESS; -} - -ns_result_t -ns_create_job(struct ns_thrpool_t *tp, ns_job_type_t job_type, ns_job_func_t func, struct ns_job_t **job) -{ - if (job == NULL) { - /* This won't queue the job, so to pass NULL makes no sense */ - return NS_INVALID_REQUEST; - } - - if (ns_thrpool_is_shutdown(tp)) { - return NS_SHUTDOWN; - } - - *job = new_ns_job(tp, NULL, job_type, func, NULL); - if (*job == NULL) { - return NS_ALLOCATION_FAILURE; - } - - return NS_SUCCESS; -} - -/* queue a file descriptor to listen for and accept new connections */ -ns_result_t -ns_add_io_job(ns_thrpool_t *tp, PRFileDesc *fd, ns_job_type_t job_type, ns_job_func_t func, void *data, ns_job_t **job) -{ - ns_job_t *_job = NULL; - - if (job) { - *job = NULL; - } - - /* Don't allow a job to be added if the threadpool is being shut down. */ - if (ns_thrpool_is_shutdown(tp)) { - return NS_SHUTDOWN; - } - - /* Don't allow an accept job to be run outside of the event thread. - * We do this so a listener job won't shut down while still processing - * current connections in other threads. - * TODO: Need to be able to have multiple threads accept() at the same time - * This is fine - just have to remove the listen job in the polling thread - * immediately after receiving notification - then call the job to do the - * accept(), which will add back the persistent listener job immediately after - * doing the accept() - * This will be a combination of a non-threaded job and a threaded job - * - */ - if (NS_JOB_IS_ACCEPT(job_type) && NS_JOB_IS_THREAD(job_type)) { - return NS_INVALID_REQUEST; - } - - /* get an event context for an accept */ - _job = alloc_io_context(tp, fd, job_type, func, data); - if (!_job) { - return NS_ALLOCATION_FAILURE; - } - - pthread_mutex_lock(&(_job->monitor)); -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_add_io_job state %d moving to NS_JOB_ARMED\n", (_job)->state); -#endif - _job->state = NS_JOB_NEEDS_ARM; - pthread_mutex_unlock(&(_job->monitor)); - internal_ns_job_rearm(_job); - - /* fill in a pointer to the job for the caller if requested */ - if (job) { - *job = _job; - } - - return NS_SUCCESS; -} - -ns_result_t -ns_add_timeout_job(ns_thrpool_t *tp, struct timeval *tv, ns_job_type_t job_type, ns_job_func_t func, void *data, ns_job_t **job) -{ - ns_job_t *_job = NULL; - - if (job) { - *job = NULL; - } - - /* Don't allow a job to be added if the threadpool is being shut down. */ - if (ns_thrpool_is_shutdown(tp)) { - return NS_SHUTDOWN; - } - - if (validate_event_timeout(tv)) { - return NS_INVALID_REQUEST; - } - - /* get an event context for a timer job */ - _job = alloc_timeout_context(tp, tv, job_type, func, data); - if (!_job) { - return NS_ALLOCATION_FAILURE; - } - - pthread_mutex_lock(&(_job->monitor)); -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_add_timeout_job state %d moving to NS_JOB_ARMED\n", (_job)->state); -#endif - _job->state = NS_JOB_NEEDS_ARM; - pthread_mutex_unlock(&(_job->monitor)); - internal_ns_job_rearm(_job); - - /* fill in a pointer to the job for the caller if requested */ - if (job) { - *job = _job; - } - - return NS_SUCCESS; -} - -/* queue a file descriptor to listen for and accept new connections */ -ns_result_t -ns_add_io_timeout_job(ns_thrpool_t *tp, PRFileDesc *fd, struct timeval *tv, ns_job_type_t job_type, ns_job_func_t func, void *data, ns_job_t **job) -{ - ns_job_t *_job = NULL; - - if (job) { - *job = NULL; - } - - /* Don't allow a job to be added if the threadpool is being shut down. */ - if (ns_thrpool_is_shutdown(tp)) { - return NS_SHUTDOWN; - } - - if (validate_event_timeout(tv)) { - return NS_INVALID_REQUEST; - } - - /* Don't allow an accept job to be run outside of the event thread. - * We do this so a listener job won't shut down while still processing - * current connections in other threads. - * TODO: Need to be able to have multiple threads accept() at the same time - * This is fine - just have to remove the listen job in the polling thread - * immediately after receiving notification - then call the job to do the - * accept(), which will add back the persistent listener job immediately after - * doing the accept() - * This will be a combination of a non-threaded job and a threaded job - * - */ - if (NS_JOB_IS_ACCEPT(job_type) && NS_JOB_IS_THREAD(job_type)) { - return NS_INVALID_REQUEST; - } - - /* get an event context for an accept */ - _job = alloc_io_context(tp, fd, job_type | NS_JOB_TIMER, func, data); - if (!_job) { - return NS_ALLOCATION_FAILURE; - } - pthread_mutex_lock(&(_job->monitor)); - _job->tv = *tv; - -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_add_io_timeout_job state %d moving to NS_JOB_ARMED\n", (_job)->state); -#endif - _job->state = NS_JOB_NEEDS_ARM; - pthread_mutex_unlock(&(_job->monitor)); - internal_ns_job_rearm(_job); - - /* fill in a pointer to the job for the caller if requested */ - if (job) { - *job = _job; - } - - return NS_SUCCESS; -} - -ns_result_t -ns_add_signal_job(ns_thrpool_t *tp, int32_t signum, ns_job_type_t job_type, ns_job_func_t func, void *data, ns_job_t **job) -{ - ns_job_t *_job = NULL; - - if (job) { - *job = NULL; - } - - /* Don't allow a job to be added if the threadpool is being shut down. */ - if (ns_thrpool_is_shutdown(tp)) { - return NS_SHUTDOWN; - } - - /* get an event context for a signal job */ - _job = alloc_signal_context(tp, signum, job_type, func, data); - if (!_job) { - return NS_ALLOCATION_FAILURE; - } - - pthread_mutex_lock(&(_job->monitor)); -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_add_signal_job state %d moving to NS_JOB_ARMED\n", (_job)->state); -#endif - _job->state = NS_JOB_NEEDS_ARM; - pthread_mutex_unlock(&(_job->monitor)); - internal_ns_job_rearm(_job); - - /* fill in a pointer to the job for the caller if requested */ - if (job) { - *job = _job; - } - - return NS_SUCCESS; -} - -ns_result_t -ns_add_job(ns_thrpool_t *tp, ns_job_type_t job_type, ns_job_func_t func, void *data, ns_job_t **job) -{ - ns_job_t *_job = NULL; - - if (job) { - *job = NULL; - } - - /* Don't allow a job to be added if the threadpool is being shut down. */ - if (ns_thrpool_is_shutdown(tp)) { - return NS_SHUTDOWN; - } - - _job = new_ns_job(tp, NULL, job_type, func, data); - if (!_job) { - return NS_ALLOCATION_FAILURE; - } - /* fill in a pointer to the job for the caller if requested */ - if (job) { - *job = _job; - } - -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_add_job %x state %d moving to NS_JOB_ARMED\n", _job, (_job)->state); -#endif - _job->state = NS_JOB_NEEDS_ARM; - internal_ns_job_rearm(_job); - - return NS_SUCCESS; -} - -ns_result_t -ns_add_shutdown_job(ns_thrpool_t *tp) -{ - ns_job_t *_job = NULL; - _job = new_ns_job(tp, NULL, NS_JOB_SHUTDOWN_WORKER, NULL, NULL); - if (!_job) { - return NS_ALLOCATION_FAILURE; - } - pthread_mutex_lock(&(_job->monitor)); - _job->state = NS_JOB_NEEDS_ARM; - pthread_mutex_unlock(&(_job->monitor)); - internal_ns_job_rearm(_job); - return NS_SUCCESS; -} - -/* - * Because of the design of work_job_execute, when we are in RUNNING - * we hold the monitor. As a result, we don't need to assert the current thread - * because we *already must* be the current thread, as no one else could take - * the monitor away. - * - * The same is true of DELETED, which represents that we are deleting things. - * To set NEEDS_DELETE -> DELETED, we must hold the monitor, and at that point - * the monitor is released and the job destroyed. As a result, we only need to - * assert that we are not "NEEDS_DELETE" in many cases. - */ - -void * -ns_job_get_data(ns_job_t *job) -{ - PR_ASSERT(job); - pthread_mutex_lock(&(job->monitor)); - PR_ASSERT(job->state != NS_JOB_DELETED); - if (job->state != NS_JOB_DELETED) { - pthread_mutex_unlock(&(job->monitor)); - return job->data; - } else { - pthread_mutex_unlock(&(job->monitor)); - return NULL; - } -} - -ns_result_t -ns_job_set_data(ns_job_t *job, void *data) -{ - PR_ASSERT(job); - pthread_mutex_lock(&(job->monitor)); - PR_ASSERT(job->state == NS_JOB_WAITING || job->state == NS_JOB_RUNNING); - if (job->state == NS_JOB_WAITING || job->state == NS_JOB_RUNNING) { - job->data = data; - pthread_mutex_unlock(&(job->monitor)); - return NS_SUCCESS; - } else { - pthread_mutex_unlock(&(job->monitor)); - return NS_INVALID_STATE; - } -} - -ns_thrpool_t * -ns_job_get_tp(ns_job_t *job) -{ - PR_ASSERT(job); - pthread_mutex_lock(&(job->monitor)); - PR_ASSERT(job->state != NS_JOB_DELETED); - if (job->state != NS_JOB_DELETED) { - pthread_mutex_unlock(&(job->monitor)); - return job->tp; - } else { - pthread_mutex_unlock(&(job->monitor)); - return NULL; - } -} - -ns_job_type_t -ns_job_get_output_type(ns_job_t *job) -{ - PR_ASSERT(job); - pthread_mutex_lock(&(job->monitor)); - PR_ASSERT(job->state == NS_JOB_RUNNING); - if (job->state == NS_JOB_RUNNING) { - pthread_mutex_unlock(&(job->monitor)); - return job->output_job_type; - } else { - pthread_mutex_unlock(&(job->monitor)); - return 0; - } -} - -ns_job_type_t -ns_job_get_type(ns_job_t *job) -{ - PR_ASSERT(job); - pthread_mutex_lock(&(job->monitor)); - PR_ASSERT(job->state != NS_JOB_DELETED); - if (job->state != NS_JOB_DELETED) { - pthread_mutex_unlock(&(job->monitor)); - return job->job_type; - } else { - pthread_mutex_unlock(&(job->monitor)); - return 0; - } -} - -PRFileDesc * -ns_job_get_fd(ns_job_t *job) -{ - PR_ASSERT(job); - pthread_mutex_lock(&(job->monitor)); - PR_ASSERT(job->state != NS_JOB_DELETED); - if (job->state != NS_JOB_DELETED) { - pthread_mutex_unlock(&(job->monitor)); - return job->fd; - } else { - pthread_mutex_unlock(&(job->monitor)); - return NULL; - } -} - -ns_result_t -ns_job_set_done_cb(struct ns_job_t *job, ns_job_func_t func) -{ - PR_ASSERT(job); - pthread_mutex_lock(&(job->monitor)); - PR_ASSERT(job->state == NS_JOB_WAITING || job->state == NS_JOB_RUNNING); - if (job->state == NS_JOB_WAITING || job->state == NS_JOB_RUNNING) { - job->done_cb = func; - pthread_mutex_unlock(&(job->monitor)); - return NS_SUCCESS; - } else { - pthread_mutex_unlock(&(job->monitor)); - return NS_INVALID_STATE; - } -} - -ns_result_t -ns_job_wait(struct ns_job_t *job) { - PR_ASSERT(job); - pthread_mutex_lock(&(job->monitor)); - if (job->state == NS_JOB_WAITING) { - /* It's done */ - pthread_mutex_unlock(&(job->monitor)); - return NS_SUCCESS; - } else { - pthread_cond_wait(&(job->notify), &(job->monitor)); - ns_job_state_t result = job->state; - pthread_mutex_unlock(&(job->monitor)); - if (result == NS_JOB_WAITING) { - return NS_SUCCESS; - } else if (result == NS_JOB_NEEDS_DELETE) { - return NS_DELETING; - } else { - PR_ASSERT(1 == 0); - return NS_INVALID_STATE; - } - } -} - -/* - * This is a convenience function - use if you need to re-arm the same event - * usually not needed for persistent jobs - */ -ns_result_t -ns_job_rearm(ns_job_t *job) -{ - PR_ASSERT(job); - pthread_mutex_lock(&(job->monitor)); - PR_ASSERT(job->state == NS_JOB_WAITING || job->state == NS_JOB_RUNNING); - - if (ns_thrpool_is_shutdown(job->tp)) { - return NS_SHUTDOWN; - } - - if (job->state == NS_JOB_WAITING) { -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_rearm_job %x state %d moving to NS_JOB_NEEDS_ARM\n", job, job->state); -#endif - job->state = NS_JOB_NEEDS_ARM; - internal_ns_job_rearm(job); - pthread_mutex_unlock(&(job->monitor)); - return NS_SUCCESS; - } else if (!NS_JOB_IS_PERSIST(job->job_type) && job->state == NS_JOB_RUNNING) { -/* For this to be called, and NS_JOB_RUNNING, we *must* be the callback thread! */ -/* Just mark it (ie do nothing), the work_job_execute function will trigger internal_ns_job_rearm */ -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_rearm_job %x state %d setting NS_JOB_NEEDS_ARM\n", job, job->state); -#endif - job->state = NS_JOB_NEEDS_ARM; - pthread_mutex_unlock(&(job->monitor)); - return NS_SUCCESS; - } else { - pthread_mutex_unlock(&(job->monitor)); - return NS_INVALID_STATE; - } - /* Unreachable code .... */ - return NS_INVALID_REQUEST; -} -int -ns_job_is_func(struct ns_job_t *job, ns_job_func_t func) -{ - return(job && job->func == func); -} - -static void -ns_thrpool_delete(ns_thrpool_t *tp) -{ - ns_free(tp); -} - -static ns_thrpool_t * -ns_thrpool_alloc(void) -{ - ns_thrpool_t *tp; - - tp = ns_calloc(1, sizeof(struct ns_thrpool_t)); - if (NULL == tp) { - goto failed; - } - - return tp; -failed: - ns_thrpool_delete(tp); - PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); - return NULL; -} - -/* libevent does not make public the file descriptors used to - wakeup epoll_wait - you have to send a signal - instead of - that just create our own wakeup pipe */ -static void -setup_event_q_wakeup(ns_thrpool_t *tp) -{ - ns_job_t *job; - PR_CreatePipe(&tp->event_q_wakeup_pipe_read, &tp->event_q_wakeup_pipe_write); - /* setting options is not supported on NSPR pipes - use fcntl - PRSocketOptionData prsod = {PR_SockOpt_Nonblocking, {PR_TRUE}}; - PR_SetSocketOption(tp->event_q_wakeup_pipe_read, &prsod); - PR_SetSocketOption(tp->event_q_wakeup_pipe_write, &prsod); - */ - if (fcntl(PR_FileDesc2NativeHandle(tp->event_q_wakeup_pipe_read), F_SETFD, O_NONBLOCK) == -1) { - ns_log(LOG_ERR, "setup_event_q_wakeup(): could not make read pipe non-blocking: %d\n", - PR_GetOSError()); - } - if (fcntl(PR_FileDesc2NativeHandle(tp->event_q_wakeup_pipe_write), F_SETFD, O_NONBLOCK) == -1) { - ns_log(LOG_ERR, "setup_event_q_wakeup(): could not make write pipe non-blocking: %d\n", - PR_GetOSError()); - } - /* wakeup events are processed inside the event loop thread */ - job = alloc_io_context(tp, tp->event_q_wakeup_pipe_read, - NS_JOB_READ | NS_JOB_PERSIST | NS_JOB_PRESERVE_FD, - wakeup_cb, NULL); - - pthread_mutex_lock(&(job->monitor)); - -/* The event_queue wakeup is ready, arm it. */ -#ifdef DEBUG - ns_log(LOG_DEBUG, "setup_event_q_wakeup %x state %d moving NS_JOB_ARMED\n", job, job->state); -#endif - job->state = NS_JOB_ARMED; - - /* Now allow it to process events */ - tp->ns_event_fw->ns_event_fw_add_io(tp->ns_event_fw_ctx, job); - - /* Stash the wakeup job in tp so we can release it later. */ - tp->event_q_wakeup_job = job; - pthread_mutex_unlock(&(job->monitor)); -} - -/* Initialize the thrpool config */ -#define NS_INIT_MAGIC 0xdefa014 - -void -ns_thrpool_config_init(struct ns_thrpool_config *tp_config) -{ - tp_config->init_flag = NS_INIT_MAGIC; - tp_config->max_threads = 1; - tp_config->stacksize = 0; - tp_config->log_fct = NULL; - tp_config->log_start_fct = NULL; - tp_config->log_close_fct = NULL; - tp_config->malloc_fct = NULL; - tp_config->memalign_fct = NULL; - tp_config->calloc_fct = NULL; - tp_config->realloc_fct = NULL; - tp_config->free_fct = NULL; -} - -/* - * Process the config and set the pluggable function pointers - */ -static ns_result_t -ns_thrpool_process_config(struct ns_thrpool_config *tp_config) -{ - /* Check that the config has been properly initialized */ - if (!tp_config || tp_config->init_flag != NS_INIT_MAGIC) { - return NS_INVALID_REQUEST; - } - /* - * Assign our logging function pointers - */ - if (tp_config->log_fct) { - /* Set a logger function */ - logger = tp_config->log_fct; - if (tp_config->log_start_fct) { - log_start = tp_config->log_start_fct; - } - if (tp_config->log_close_fct) { - log_close = tp_config->log_close_fct; - } - } else { - /* Default to syslog */ - logger = ns_syslog; - log_start = ns_syslog_start; - log_close = ns_syslog_close; - } - if (log_start) { - /* Start logging */ - (log_start)(); - } - - /* - * Set the memory function pointers - */ - /* malloc */ - if (tp_config->malloc_fct) { - malloc_fct = tp_config->malloc_fct; - } else { - malloc_fct = os_malloc; - } - - if (tp_config->memalign_fct) { - memalign_fct = tp_config->memalign_fct; - } else { - memalign_fct = os_memalign_malloc; - } - - /* calloc */ - if (tp_config->calloc_fct) { - calloc_fct = tp_config->calloc_fct; - } else { - calloc_fct = os_calloc; - } - /* realloc */ - if (tp_config->realloc_fct) { - realloc_fct = tp_config->realloc_fct; - } else { - realloc_fct = os_realloc; - } - /* free */ - if (tp_config->free_fct) { - free_fct = tp_config->free_fct; - } else { - free_fct = os_free; - } - - return NS_SUCCESS; -} - -ns_thrpool_t * -ns_thrpool_new(struct ns_thrpool_config *tp_config) -{ - pthread_attr_t attr; - ns_thrpool_t *tp = NULL; - ns_thread_t *thr; - size_t ii; - - if (ns_thrpool_process_config(tp_config) != NS_SUCCESS) { - ns_log(LOG_ERR, "ns_thrpool_new(): config has not been properly initialized\n"); - goto failed; - } - - tp = ns_thrpool_alloc(); - if (NULL == tp) { - ns_log(LOG_ERR, "ns_thrpool_new(): failed to allocate thread pool\n"); - goto failed; - } - - ns_log(LOG_DEBUG, "ns_thrpool_new(): max threads, (%d)\n" - "stacksize (%d), event q size (unbounded), work q size (unbounded)\n", - tp_config->max_threads, tp_config->stacksize); - - tp->stacksize = tp_config->stacksize; - - if (sds_queue_init(&(tp->thread_stack), NULL) != SDS_SUCCESS) { - goto failed; - } - - if (pthread_mutex_init(&(tp->work_q_lock), NULL) != 0) { - goto failed; - } - if (pthread_cond_init(&(tp->work_q_cv), NULL) != 0) { - goto failed; - } - - if (sds_lqueue_init(&(tp->work_q), job_queue_cleanup) != SDS_SUCCESS) { - goto failed; - } - if (sds_lqueue_init(&(tp->event_q), job_queue_cleanup) != SDS_SUCCESS) { - goto failed; - } - - /* NGK TODO - add tevent vs. libevent switch */ - /* tp->ns_event_fw = get_event_framework_tevent(); */ - tp->ns_event_fw = get_event_framework_event(); - tp->ns_event_fw_ctx = tp->ns_event_fw->ns_event_fw_init(); - - setup_event_q_wakeup(tp); - - /* Create the thread attributes. */ - if (pthread_attr_init(&attr) != 0) { - goto failed; - } - /* Setup the stack size. */ - if (tp_config->stacksize > 0) { - if (pthread_attr_setstacksize(&attr, tp_config->stacksize) != 0) { - goto failed; - } - } - - for (ii = 0; ii < tp_config->max_threads; ++ii) { - tp->thread_count += 1; - thr = ns_calloc(1, sizeof(ns_thread_t)); - PR_ASSERT(thr); - thr->tp = tp; - assert(pthread_create(&(thr->thr), &attr, &worker_thread_func, thr) == 0); - sds_queue_enqueue(tp->thread_stack, thr); - } - - assert(pthread_create(&(tp->event_thread), &attr, &event_loop_thread_func, tp) == 0); - - /* We keep the event thread separate from the stack of worker threads. */ - // tp->event_thread = event_thr; - - return tp; -failed: - ns_thrpool_destroy(tp); - return NULL; -} - -void -ns_thrpool_destroy(struct ns_thrpool_t *tp) -{ - void *retval = NULL; -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_thrpool_destroy\n"); -#endif - if (tp) { - /* Set the flag to shutdown the event loop. */ -#ifdef ATOMIC_64BIT_OPERATIONS - __atomic_add_fetch(&(tp->shutdown_event_loop), 1, __ATOMIC_RELEASE); -#else - PR_AtomicIncrement(&(tp->shutdown_event_loop)); -#endif - /* Finish the event queue wakeup job. This has the - * side effect of waking up the event loop thread, which - * will cause it to exit since we set the event loop - * shutdown flag. Fake the job to be a threaded job - * so that we can run it from outside the event loop, - * and use it to wake up the event loop. - */ - - pthread_mutex_lock(&(tp->event_q_wakeup_job->monitor)); - -// tp->event_q_wakeup_job->job_type |= NS_JOB_THREAD; -/* This triggers the job to "run", which will cause a shutdown cascade */ -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_thrpool_destroy %x state %d moving to NS_JOB_NEEDS_DELETE\n", tp->event_q_wakeup_job, tp->event_q_wakeup_job->state); -#endif - tp->event_q_wakeup_job->state = NS_JOB_NEEDS_DELETE; - pthread_mutex_unlock(&(tp->event_q_wakeup_job->monitor)); - /* Has to be event_q_notify, not internal_job_done */ - event_q_notify(tp->event_q_wakeup_job); - - /* Wait for the event thread to finish before we free the - * internals of tp. */ - int32_t rc = pthread_join(tp->event_thread, &retval); - if (rc != 0) { - ns_log(LOG_DEBUG, "Failed to join event thread %d\n", rc); - } - - if (tp->work_q) { - sds_lqueue_destroy(tp->work_q); - } - - if (tp->thread_stack) { - sds_queue_destroy(tp->thread_stack); - } - - /* Free the work queue condition variable. */ - pthread_cond_destroy(&(tp->work_q_cv)); - /* Free the work queue lock. */ - pthread_mutex_destroy(&(tp->work_q_lock)); - - if (tp->event_q) { - sds_lqueue_destroy(tp->event_q); - } - - /* Free the event queue wakeup pipe/job. */ - if (tp->event_q_wakeup_pipe_read) { - PR_Close(tp->event_q_wakeup_pipe_read); - tp->event_q_wakeup_pipe_read = NULL; - } - - if (tp->event_q_wakeup_pipe_write) { - PR_Close(tp->event_q_wakeup_pipe_write); - tp->event_q_wakeup_pipe_write = NULL; - } - /* Already destroyed in the event queue shutdown */ - tp->event_q_wakeup_job = NULL; - - /* Free the event framework context. */ - if (tp->ns_event_fw_ctx) { - tp->ns_event_fw->ns_event_fw_destroy(tp->ns_event_fw_ctx); - tp->ns_event_fw_ctx = NULL; - } - - /* Free the thread pool struct itself. */ - ns_thrpool_delete(tp); - } - /* Stop logging */ - if (log_close) { - (log_close)(); - } -} - -/* Triggers the pool of worker threads to shutdown after finishing the remaining work. - * This must be run in a worker thread, not the event thread. Running it in the event - * thread could cause a deadlock. */ -void -ns_thrpool_shutdown(struct ns_thrpool_t *tp) -{ -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_thrpool_shutdown initiated ...\n"); -#endif - if (ns_thrpool_is_shutdown(tp) != 0) { - /* Already done! */ - return; - } - - /* Set the shutdown flag. This will cause the worker - * threads to exit after they finish all remaining work. */ -#ifdef ATOMIC_64BIT_OPERATIONS - __atomic_add_fetch(&(tp->shutdown), 1, __ATOMIC_RELEASE); -#else - PR_AtomicIncrement(&(tp->shutdown)); -#endif - - /* Send worker shutdown jobs into the queues. This allows - * currently queued jobs to complete. - */ - for (size_t i = 0; i < tp->thread_count; i++) { - ns_result_t result = ns_add_shutdown_job(tp); - if (result != NS_SUCCESS) { -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_thrpool_shutdown - Failed to add shutdown job: error (%d)\n", result); -#endif - PR_ASSERT(0); - } - } - /* Make sure all threads are woken up to their shutdown jobs. */ - pthread_mutex_lock(&(tp->work_q_lock)); - pthread_cond_broadcast(&(tp->work_q_cv)); - pthread_mutex_unlock(&(tp->work_q_lock)); -} - -ns_result_t -ns_thrpool_wait(ns_thrpool_t *tp) -{ -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_thrpool_wait has begun\n"); -#endif - ns_result_t retval = NS_SUCCESS; - ns_thread_t *thr; - - while (sds_queue_dequeue(tp->thread_stack, (void **)&thr) == SDS_SUCCESS) { - /* void *thread_retval = NULL; */ - int32_t rc = pthread_join(thr->thr, NULL); -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_thrpool_wait joined thread, result %d\n", rc); -#endif - if (rc != 0) { - /* NGK TODO - this is unused right now. */ - ns_log(LOG_ERR, "ns_thrpool_wait, failed to join thread %d", rc); - retval = NS_THREAD_FAILURE; - } - ns_free(thr); - } - - -#ifdef DEBUG - ns_log(LOG_DEBUG, "ns_thrpool_wait complete, retval %d\n", retval); -#endif - return retval; -} - -/* - * nunc stans logger - */ -void -ns_log(int priority, const char *fmt, ...) -{ - va_list varg; - - va_start(varg, fmt); - ns_log_valist(priority, fmt, varg); - va_end(varg); -} - -void -ns_log_valist(int priority, const char *fmt, va_list varg) -{ - (logger)(priority, fmt, varg); -} - -/* - * Pluggable memory functions - */ -void * -ns_malloc(size_t size) -{ - return (malloc_fct)(size); -} - -void * -ns_memalign(size_t size, size_t alignment) -{ - return (memalign_fct)(size, alignment); -} - -void * -ns_calloc(size_t count, size_t size) -{ - return (calloc_fct)(count, size); -} - -void * -ns_realloc(void *ptr, size_t size) -{ - return (realloc_fct)(ptr, size); -} - -void -ns_free(void *ptr) -{ - (free_fct)(ptr); -} diff --git a/src/nunc-stans/ns/ns_tls.c b/src/nunc-stans/ns/ns_tls.c deleted file mode 100644 index 25f7052..0000000 --- a/src/nunc-stans/ns/ns_tls.c +++ /dev/null @@ -1,292 +0,0 @@ -/* --- BEGIN COPYRIGHT BLOCK --- - * Copyright (C) 2015 Red Hat - * see files 'COPYING' and 'COPYING.openssl' for use and warranty - * information - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Additional permission under GPLv3 section 7: - * - * If you modify this Program, or any covered work, by linking or - * combining it with OpenSSL, or a modified version of OpenSSL licensed - * under the OpenSSL license - * (https://www.openssl.org/source/license.html), the licensors of this - * Program grant you additional permission to convey the resulting - * work. Corresponding Source for a non-source form of such a - * combination shall include the source code for the parts that are - * licensed under the OpenSSL license as well as that of the covered - * work. - * --- END COPYRIGHT BLOCK --- - */ -#include "nspr.h" -#include "prio.h" -#include "secitem.h" -#include "nss.h" -#include "ssl.h" -#include "cert.h" -#include "pk11pub.h" -#include "keyhi.h" - -#include "ns_tls.h" -#include -#include "ns_private.h" - -struct ns_sec_ctx_t -{ - NSSInitContext *initctx; - NSSInitParameters init_params; - PRFileDesc *model_fd; - PRBool is_client; -}; - -typedef struct ns_sec_ctx_t ns_sec_ctx_t; - -static void -errExit(const char *msg) -{ - ns_log(LOG_ERR, msg); -} - -static PRFileDesc * -create_tls_model_sock(const char *certname, PRBool is_client) -{ - PRFileDesc *model_sock = NULL; - int rv; - SECStatus secStatus; - PRBool disableSSL2 = PR_TRUE; - PRBool disableSSL3 = PR_FALSE; - PRBool disableTLS = PR_FALSE; - PRBool disableRollBack = PR_FALSE; - PRBool NoReuse = PR_FALSE; - PRBool disableStepDown = PR_FALSE; - PRBool bypassPKCS11 = PR_FALSE; - PRBool disableLocking = PR_FALSE; - PRBool enableFDX = PR_FALSE; - PRBool enableSessionTickets = PR_FALSE; - PRBool enableCompression = PR_FALSE; - SSLKEAType certKEA; - - model_sock = PR_NewTCPSocket(); - if (model_sock == NULL) { - errExit("PR_NewTCPSocket on model socket"); - } - model_sock = SSL_ImportFD(NULL, model_sock); - if (model_sock == NULL) { - errExit("SSL_ImportFD"); - } - - /* do SSL configuration. */ - rv = SSL_OptionSet(model_sock, SSL_SECURITY, - !(disableSSL2 && disableSSL3 && disableTLS)); - if (rv != SECSuccess) { - errExit("SSL_OptionSet SSL_SECURITY"); - } - - rv = SSL_OptionSet(model_sock, SSL_ENABLE_SSL3, !disableSSL3); - if (rv != SECSuccess) { - errExit("error enabling SSLv3 "); - } - - rv = SSL_OptionSet(model_sock, SSL_ENABLE_TLS, !disableTLS); - if (rv != SECSuccess) { - errExit("error enabling TLS "); - } - - rv = SSL_OptionSet(model_sock, SSL_ENABLE_SSL2, !disableSSL2); - if (rv != SECSuccess) { - errExit("error enabling SSLv2 "); - } - - rv = SSL_OptionSet(model_sock, SSL_ROLLBACK_DETECTION, !disableRollBack); - if (rv != SECSuccess) { - errExit("error enabling RollBack detection "); - } - - rv = SSL_OptionSet(model_sock, SSL_HANDSHAKE_AS_CLIENT, is_client); - if (rv != SECSuccess) { - errExit("error handshake as client "); - } - - rv = SSL_OptionSet(model_sock, SSL_HANDSHAKE_AS_SERVER, !is_client); - if (rv != SECSuccess) { - errExit("error handshake as server "); - } - - if (disableStepDown) { - rv = SSL_OptionSet(model_sock, SSL_NO_STEP_DOWN, PR_TRUE); - if (rv != SECSuccess) { - errExit("error disabling SSL StepDown "); - } - } - if (bypassPKCS11) { - rv = SSL_OptionSet(model_sock, SSL_BYPASS_PKCS11, PR_TRUE); - if (rv != SECSuccess) { - errExit("error enabling PKCS11 bypass "); - } - } - if (disableLocking) { - rv = SSL_OptionSet(model_sock, SSL_NO_LOCKS, PR_TRUE); - if (rv != SECSuccess) { - errExit("error disabling SSL socket locking "); - } - } - if (enableSessionTickets) { - rv = SSL_OptionSet(model_sock, SSL_ENABLE_SESSION_TICKETS, PR_TRUE); - if (rv != SECSuccess) { - errExit("error enabling Session Ticket extension "); - } - } - - if (enableCompression) { - rv = SSL_OptionSet(model_sock, SSL_ENABLE_DEFLATE, PR_TRUE); - if (rv != SECSuccess) { - errExit("error enabling compression "); - } - } - - /* handle ciphers here - SSL_CipherPrefSetDefault etc. */ - - /* - rv = SSL_SNISocketConfigHook(model_sock, mySSLSNISocketConfig, - (void*)&virtServerNameArray); - if (rv != SECSuccess) { - errExit("error enabling SNI extension "); - } - */ - - if (!is_client) { - CERTCertificate *cert; - SECKEYPrivateKey *privKey; - - cert = PK11_FindCertFromNickname(certname, NULL); - if (cert == NULL) { - ns_log(LOG_ERR, "selfserv: Can't find certificate %s\n", certname); - exit(10); - } - privKey = PK11_FindKeyByAnyCert(cert, NULL); - if (privKey == NULL) { - ns_log(LOG_ERR, "selfserv: Can't find Private Key for cert %s\n", certname); - exit(11); - } - certKEA = NSS_FindCertKEAType(cert); - secStatus = SSL_ConfigSecureServer(model_sock, cert, privKey, certKEA); - if (secStatus != SECSuccess) { - errExit("SSL_ConfigSecureServer"); - } - CERT_DestroyCertificate(cert); - SECKEY_DestroyPrivateKey(privKey); - SSL_ConfigServerSessionIDCache(0, 0, 0, NULL); - } - - if (enableFDX) { /* doing FDX */ - rv = SSL_OptionSet(model_sock, SSL_ENABLE_FDX, 1); - if (rv != SECSuccess) { - errExit("SSL_OptionSet SSL_ENABLE_FDX"); - } - } - - if (NoReuse) { - rv = SSL_OptionSet(model_sock, SSL_NO_CACHE, 1); - if (rv != SECSuccess) { - errExit("SSL_OptionSet SSL_NO_CACHE"); - } - } - - /* - if (expectedHostNameVal) { - SSL_HandshakeCallback(model_sock, handshakeCallback, - (void*)expectedHostNameVal); - } - */ - - /* - SSL_AuthCertificateHook(model_sock, mySSLAuthCertificate, - (void *)CERT_GetDefaultCertDB()); - */ - - rv = SSL_OptionSet(model_sock, SSL_REQUEST_CERTIFICATE, PR_TRUE); - if (rv != SECSuccess) { - errExit("first SSL_OptionSet SSL_REQUEST_CERTIFICATE"); - } - rv = SSL_OptionSet(model_sock, SSL_REQUIRE_CERTIFICATE, PR_FALSE); - if (rv != SECSuccess) { - errExit("first SSL_OptionSet SSL_REQUIRE_CERTIFICATE"); - } - - /* end of ssl configuration. */ - return model_sock; -} - -void -ns_tls_done(ns_sec_ctx_t *ctx) -{ - if (ctx->model_fd) { - PR_Close(ctx->model_fd); - } - if (ctx->initctx) { - NSS_ShutdownContext(ctx->initctx); - } - ns_free(ctx); -} - -ns_sec_ctx_t * -ns_tls_init(const char *dir, const char *prefix, const char *certname, PRBool is_client) -{ - SECStatus rc = SECSuccess; - ns_sec_ctx_t *ctx = ns_calloc(1, sizeof(ns_sec_ctx_t)); - - ctx->init_params.length = sizeof(ctx->init_params); - ctx->initctx = NSS_InitContext(dir, prefix, prefix, SECMOD_DB, - &ctx->init_params, NSS_INIT_READONLY); - if (ctx->initctx == NULL) { - rc = SECFailure; - goto fail; - } - - NSS_SetDomesticPolicy(); - - ctx->model_fd = create_tls_model_sock(certname, is_client); - if (ctx->model_fd == NULL) { - rc = SECFailure; - goto fail; - } - -fail: - if (rc != SECSuccess) { - ns_tls_done(ctx); - ctx = NULL; - } - - return ctx; -} - -PRErrorCode -ns_add_sec_layer(PRFileDesc *fd, ns_sec_ctx_t *ctx) -{ - PRErrorCode err = PR_SUCCESS; - - fd = SSL_ImportFD(ctx->model_fd, fd); - if (fd == NULL) { - err = PR_GetError(); - goto fail; - } - - if (SECSuccess != SSL_ResetHandshake(fd, !ctx->is_client)) { - err = PR_GetError(); - goto fail; - } - -fail: - return err; -} diff --git a/src/nunc-stans/ns/ns_tls.h b/src/nunc-stans/ns/ns_tls.h deleted file mode 100644 index 84d322d..0000000 --- a/src/nunc-stans/ns/ns_tls.h +++ /dev/null @@ -1,49 +0,0 @@ -/* --- BEGIN COPYRIGHT BLOCK --- - * Copyright (C) 2015 Red Hat - * see files 'COPYING' and 'COPYING.openssl' for use and warranty - * information - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Additional permission under GPLv3 section 7: - * - * If you modify this Program, or any covered work, by linking or - * combining it with OpenSSL, or a modified version of OpenSSL licensed - * under the OpenSSL license - * (https://www.openssl.org/source/license.html), the licensors of this - * Program grant you additional permission to convey the resulting - * work. Corresponding Source for a non-source form of such a - * combination shall include the source code for the parts that are - * licensed under the OpenSSL license as well as that of the covered - * work. - * --- END COPYRIGHT BLOCK --- - */ -#ifndef NS_TLS_H -#define NS_TLS_H - -#include "nspr.h" -#include "prerror.h" -#include "prio.h" - -/* should be in sec_ctx.h */ -struct ns_sec_ctx_t; -/* adds the sec layer _in place_ to fd - if error returned, the sec - layer is unstable and the fd should be closed and discarded */ -PRErrorCode ns_add_sec_layer(PRFileDesc *fd, struct ns_sec_ctx_t *ctx); - -/* tls specific functions */ -void ns_tls_done(struct ns_sec_ctx_t *ctx); -struct ns_sec_ctx_t *ns_tls_init(const char *dir, const char *prefix, const char *certname, PRBool isClient); - -#endif /* NS_TLS_H */ diff --git a/src/nunc-stans/test/test_nuncstans.c b/src/nunc-stans/test/test_nuncstans.c deleted file mode 100644 index 27fd98e..0000000 --- a/src/nunc-stans/test/test_nuncstans.c +++ /dev/null @@ -1,533 +0,0 @@ -/* --- BEGIN COPYRIGHT BLOCK --- - * Copyright (C) 2016 Red Hat - * see files 'COPYING' and 'COPYING.openssl' for use and warranty - * information - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Additional permission under GPLv3 section 7: - * - * If you modify this Program, or any covered work, by linking or - * combining it with OpenSSL, or a modified version of OpenSSL licensed - * under the OpenSSL license - * (https://www.openssl.org/source/license.html), the licensors of this - * Program grant you additional permission to convey the resulting - * work. Corresponding Source for a non-source form of such a - * combination shall include the source code for the parts that are - * licensed under the OpenSSL license as well as that of the covered - * work. - * --- END COPYRIGHT BLOCK --- - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -/* For cmocka */ -#include -#include -#include -#include - -/* For string and time manipulation in tests */ -#include -#include - -/* For signaling tests */ -#include - -/* NS requries inttypes.h */ -#include - -/* For NS itself */ -#include -/* We need the internal headers for state checks */ -#include "../ns/ns_event_fw.h" - -#include - -#include - -#ifdef HAVE_STDLIB_H -#include -#endif - - -static int cb_check = 0; - -static pthread_mutex_t cb_lock; -static pthread_cond_t cb_cond; -// static PRLock *cb_lock = NULL; -// static PRCondVar *cb_cond = NULL; - -void -ns_test_logger(int priority __attribute__((unused)), const char *fmt, va_list varg) -{ - // Should we do anything with priority? - vprintf(fmt, varg); -} - -static int -cond_wait_rel(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict reltime) { - struct timespec now; - struct timespec abswait; - - clock_gettime(CLOCK_REALTIME, &now); - - abswait.tv_sec = now.tv_sec + reltime->tv_sec; - abswait.tv_nsec = now.tv_nsec + reltime->tv_nsec; - - return pthread_cond_timedwait(cond, mutex, &abswait); -} - -/* All our other tests will use this in some form. */ -static int -ns_test_setup(void **state) -{ - /* Ensure that we can create a new nunc-stans instance. */ - struct ns_thrpool_t *tp = NULL; - struct ns_thrpool_config ns_config; - /* Reset the callback check */ - cb_check = 0; - /* Create the cond var the CB check will use. */ - assert(pthread_mutex_init(&cb_lock, NULL) == 0); - assert(pthread_cond_init(&cb_cond, NULL) == 0); - - ns_thrpool_config_init(&ns_config); - - ns_config.log_fct = ns_test_logger; - ns_config.max_threads = 4; - tp = ns_thrpool_new(&ns_config); - assert_non_null(tp); - - *state = tp; - - return 0; -} - -static int -ns_test_teardown(void **state) -{ - struct ns_thrpool_t *tp = *state; - ns_thrpool_shutdown(tp); - assert_int_equal(ns_thrpool_wait(tp), 0); - - ns_thrpool_destroy(tp); - - pthread_cond_destroy(&cb_cond); - pthread_mutex_destroy(&cb_lock); - - return 0; -} - -static void -ns_init_test_job_cb(struct ns_job_t *job __attribute__((unused))) -{ - pthread_mutex_lock(&cb_lock); - cb_check += 1; - pthread_cond_signal(&cb_cond); - pthread_mutex_unlock(&cb_lock); -} - -static void -ns_init_disarm_job_cb(struct ns_job_t *job) -{ - if (ns_job_done(job) == NS_SUCCESS) { - pthread_mutex_lock(&cb_lock); - cb_check = 1; - pthread_cond_signal(&cb_cond); - pthread_mutex_unlock(&cb_lock); - } else { - assert_int_equal(1, 0); - } -} - -static void -ns_init_do_nothing_cb(struct ns_job_t *job __attribute__((unused))) -{ - /* I mean it, do nothing! */ - return; -} - -static void -ns_init_test(void **state) -{ - struct ns_thrpool_t *tp = *state; - struct ns_job_t *job = NULL; - struct timespec timeout = {1, 0}; - - pthread_mutex_lock(&cb_lock); - assert_int_equal( - ns_add_job(tp, NS_JOB_NONE | NS_JOB_THREAD, ns_init_test_job_cb, NULL, &job), - 0); - - assert(cond_wait_rel(&cb_cond, &cb_lock, &timeout) == 0); - pthread_mutex_unlock(&cb_lock); - - assert_int_equal(cb_check, 1); - - /* Once the job is done, it's not in the event queue, and it's complete */ - assert(ns_job_wait(job) == NS_SUCCESS); - assert_int_equal(ns_job_done(job), NS_SUCCESS); -} - -static void -ns_set_data_test(void **state) -{ - /* Add a job with data */ - struct ns_thrpool_t *tp = *state; - struct ns_job_t *job = NULL; - struct timespec timeout = {1, 0}; - - char *data = malloc(6); - - strcpy(data, "first"); - - pthread_mutex_lock(&cb_lock); - assert_int_equal( - ns_add_job(tp, NS_JOB_NONE | NS_JOB_THREAD, ns_init_test_job_cb, data, &job), - NS_SUCCESS); - - /* Let the job run */ - assert(cond_wait_rel(&cb_cond, &cb_lock, &timeout) == 0); - pthread_mutex_unlock(&cb_lock); - - /* Check that the data is correct */ - char *retrieved = (char *)ns_job_get_data(job); - assert_int_equal(strcmp("first", retrieved), 0); - - free(retrieved); - - /* set new data */ - data = malloc(7); - strcpy(data, "second"); - - assert(ns_job_wait(job) == NS_SUCCESS); - ns_job_set_data(job, data); - - /* Rearm, and let it run again. */ - pthread_mutex_lock(&cb_lock); - ns_job_rearm(job); - assert(cond_wait_rel(&cb_cond, &cb_lock, &timeout) == 0); - pthread_mutex_unlock(&cb_lock); - - /* Make sure it's now what we expect */ - retrieved = (char *)ns_job_get_data(job); - assert_int_equal(strcmp("second", retrieved), 0); - - free(retrieved); - - /* Because the job is not queued, we must free it */ - /* - * It's possible here, that the worker thread is still processing state - * as a result, we aren't the owning thread, and ns_job_done fails. - * So we actually have to loop on freeing this until it's released to - * waiting. we might need a load barrier here ... - */ - - assert(ns_job_wait(job) == NS_SUCCESS); - - assert_int_equal(ns_job_done(job), NS_SUCCESS); -} - -static void -ns_job_done_cb_test(void **state) -{ - struct ns_thrpool_t *tp = *state; - struct ns_job_t *job = NULL; - struct timespec timeout = {1, 0}; - - pthread_mutex_lock(&cb_lock); - assert_int_equal( - ns_create_job(tp, NS_JOB_NONE | NS_JOB_THREAD, ns_init_do_nothing_cb, &job), - NS_SUCCESS); - - ns_job_set_done_cb(job, ns_init_test_job_cb); - /* Remove it */ - assert_int_equal(ns_job_done(job), NS_SUCCESS); - - assert(cond_wait_rel(&cb_cond, &cb_lock, &timeout) == 0); - pthread_mutex_unlock(&cb_lock); - - assert_int_equal(cb_check, 1); -} - -static void -ns_init_rearm_job_cb(struct ns_job_t *job) -{ - if (ns_job_rearm(job) != NS_SUCCESS) { - pthread_mutex_lock(&cb_lock); - cb_check = 1; - /* we failed to re-arm as expected, let's go away ... */ - assert_int_equal(ns_job_done(job), NS_SUCCESS); - pthread_cond_signal(&cb_cond); - pthread_mutex_unlock(&cb_lock); - } else { - assert_int_equal(1, 0); - } -} - -static void -ns_job_persist_rearm_ignore_test(void **state) -{ - /* Test that rearm ignores the persistent job. */ - struct ns_thrpool_t *tp = *state; - struct ns_job_t *job = NULL; - struct timespec timeout = {1, 0}; - - pthread_mutex_lock(&cb_lock); - assert_int_equal( - ns_create_job(tp, NS_JOB_NONE | NS_JOB_THREAD | NS_JOB_PERSIST, ns_init_rearm_job_cb, &job), - NS_SUCCESS); - - /* This *will* arm the job, and will trigger the cb. */ - assert_int_equal(ns_job_rearm(job), 0); - /* - * Now when the CB fires, it will *try* to rearm, but will fail, so we - * should see only 1 in the cb_check. - */ - - assert(cond_wait_rel(&cb_cond, &cb_lock, &timeout) == 0); - pthread_mutex_unlock(&cb_lock); - - /* If we fail to rearm, this is set to 1 Which is what we want. */ - assert_int_equal(cb_check, 1); -} - -static void -ns_job_persist_disarm_test(void **state) -{ - /* Make a persistent job */ - struct ns_thrpool_t *tp = *state; - struct ns_job_t *job = NULL; - struct timespec timeout = {2, 0}; - - pthread_mutex_lock(&cb_lock); - assert_int_equal( - ns_create_job(tp, NS_JOB_NONE | NS_JOB_PERSIST, ns_init_disarm_job_cb, &job), - NS_SUCCESS); - - assert_int_equal(ns_job_rearm(job), NS_SUCCESS); - - /* In the callback it should disarm */ - assert(cond_wait_rel(&cb_cond, &cb_lock, &timeout) == 0); - pthread_mutex_unlock(&cb_lock); - /* Make sure it did */ - assert_int_equal(cb_check, 1); -} - -/* - * This tests a very specific issue in the directory server code. It's possible - * that a job will try to disarm itself from within the worker thread. This can - * race, as the event thread will get the work, free the job. The worker then - * returns from the fn, and will heap-use-after-free because the job was - * yanked from under it. To test this, you *will* need ASAN enabled to detect - * the failure condition (it will crash and burn) - * - * The bug happens in ns_add_job, when it calls the cb. - * The moment we add this, it will then trigger the job to run in thrpool event_cb - * this of course will call job->func(job), in this case ns_init_race_done_job_cb. - * Because the race_done job sends to ns_job_done, this will free job in the event - * thread, but then the work thread will return after the PR_Sleep to event_cb. - * At this point, the wt attempts to access job->type and job->state, to determine - * if the job needs rearm. Because the et freed job, this is now a use after - * free. - */ -static void -ns_init_race_done_job_cb(struct ns_job_t *job) -{ - ns_job_done(job); - /* We need to sleep to let the job race happen */ - PR_Sleep(PR_SecondsToInterval(2)); - pthread_mutex_lock(&cb_lock); - cb_check += 1; - pthread_cond_signal(&cb_cond); - pthread_mutex_unlock(&cb_lock); -} - -static void -ns_job_race_done_test(void **state) -{ - struct ns_thrpool_t *tp = *state; - struct ns_job_t *job = NULL; - struct timespec timeout = {5, 0}; - - pthread_mutex_lock(&cb_lock); - assert_int_equal( - ns_add_job(tp, NS_JOB_NONE | NS_JOB_THREAD, ns_init_race_done_job_cb, NULL, &job), - NS_SUCCESS); - - assert(cond_wait_rel(&cb_cond, &cb_lock, &timeout) == 0); - pthread_mutex_unlock(&cb_lock); - - assert_int_equal(cb_check, 1); -} - -/* - * This tests that when we raise a signal, we catch it and handle it correctly. - */ - -static void -ns_job_signal_cb_test(void **state) -{ - struct ns_thrpool_t *tp = *state; - struct ns_job_t *job = NULL; - struct timespec timeout = {1, 0}; - - pthread_mutex_lock(&cb_lock); - assert_int_equal( - ns_add_signal_job(tp, SIGUSR1, NS_JOB_SIGNAL, ns_init_test_job_cb, NULL, &job), - NS_SUCCESS); - - /* The addition of the signal job to the event fw is async */ - PR_Sleep(PR_SecondsToInterval(2)); - /* Send the signal ... */ - raise(SIGUSR1); - - assert(cond_wait_rel(&cb_cond, &cb_lock, &timeout) == 0); - pthread_mutex_unlock(&cb_lock); - - assert_int_equal(cb_check, 1); - - /* Remove the signal job now */ - assert_int_equal(ns_job_done(job), NS_SUCCESS); -} - -/* - * Test that given a timeout of -1, we fail to create a job. - */ - -static void -ns_job_neg_timeout_test(void **state) -{ - struct ns_thrpool_t *tp = *state; - - struct timeval tv = {-1, 0}; - - PR_ASSERT(NS_INVALID_REQUEST == ns_add_io_timeout_job(tp, 0, &tv, NS_JOB_THREAD, ns_init_do_nothing_cb, NULL, NULL)); - - PR_ASSERT(NS_INVALID_REQUEST == ns_add_timeout_job(tp, &tv, NS_JOB_THREAD, ns_init_do_nothing_cb, NULL, NULL)); -} - -/* - * Test that a timeout job fires a within a time window - */ - -static void -ns_timer_job_cb(struct ns_job_t *job) -{ - ns_job_done(job); - pthread_mutex_lock(&cb_lock); - cb_check += 1; - pthread_cond_signal(&cb_cond); - pthread_mutex_unlock(&cb_lock); -} - -static void -ns_job_timer_test(void **state) -{ - struct ns_thrpool_t *tp = *state; - struct ns_job_t *job = NULL; - struct timeval tv = {3, 0}; - struct timespec timeout = {2, 0}; - - pthread_mutex_lock(&cb_lock); - assert_true(ns_add_timeout_job(tp, &tv, NS_JOB_THREAD, ns_timer_job_cb, NULL, &job) == NS_SUCCESS); - - cond_wait_rel(&cb_cond, &cb_lock, &timeout); - // pthread_mutex_unlock(&cb_lock); - assert_int_equal(cb_check, 0); - - // pthread_mutex_lock(&cb_lock); - cond_wait_rel(&cb_cond, &cb_lock, &timeout); - pthread_mutex_unlock(&cb_lock); - assert_int_equal(cb_check, 1); -} - -/* - * Test that within a window, a looping timeout job has fired greater than X times. - */ - -static void -ns_timer_persist_job_cb(struct ns_job_t *job) -{ - pthread_mutex_lock(&cb_lock); - cb_check += 1; - pthread_mutex_unlock(&cb_lock); - if (cb_check < 10) { - ns_job_rearm(job); - } else { - ns_job_done(job); - } -} - -static void -ns_job_timer_persist_test(void **state) -{ - struct ns_thrpool_t *tp = *state; - struct ns_job_t *job = NULL; - struct timeval tv = {1, 0}; - - assert_true(ns_add_timeout_job(tp, &tv, NS_JOB_THREAD, ns_timer_persist_job_cb, NULL, &job) == NS_SUCCESS); - - PR_Sleep(PR_SecondsToInterval(5)); - - pthread_mutex_lock(&cb_lock); - assert_true(cb_check <= 6); - pthread_mutex_unlock(&cb_lock); - - PR_Sleep(PR_SecondsToInterval(6)); - - pthread_mutex_lock(&cb_lock); - assert_int_equal(cb_check, 10); - pthread_mutex_unlock(&cb_lock); -} - -int -main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test_setup_teardown(ns_init_test, - ns_test_setup, - ns_test_teardown), - cmocka_unit_test_setup_teardown(ns_set_data_test, - ns_test_setup, - ns_test_teardown), - cmocka_unit_test_setup_teardown(ns_job_done_cb_test, - ns_test_setup, - ns_test_teardown), - cmocka_unit_test_setup_teardown(ns_job_persist_rearm_ignore_test, - ns_test_setup, - ns_test_teardown), - cmocka_unit_test_setup_teardown(ns_job_persist_disarm_test, - ns_test_setup, - ns_test_teardown), - cmocka_unit_test_setup_teardown(ns_job_race_done_test, - ns_test_setup, - ns_test_teardown), - cmocka_unit_test_setup_teardown(ns_job_signal_cb_test, - ns_test_setup, - ns_test_teardown), - cmocka_unit_test_setup_teardown(ns_job_neg_timeout_test, - ns_test_setup, - ns_test_teardown), - cmocka_unit_test_setup_teardown(ns_job_timer_test, - ns_test_setup, - ns_test_teardown), - cmocka_unit_test_setup_teardown(ns_job_timer_persist_test, - ns_test_setup, - ns_test_teardown), - }; - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/src/nunc-stans/test/test_nuncstans_stress.h b/src/nunc-stans/test/test_nuncstans_stress.h deleted file mode 100644 index e0a6f8d..0000000 --- a/src/nunc-stans/test/test_nuncstans_stress.h +++ /dev/null @@ -1,44 +0,0 @@ -/** BEGIN COPYRIGHT BLOCK - * Copyright (c) 2017, Red Hat, Inc - * All rights reserved. - * - * License: GPL (version 3 or any later version). - * See LICENSE for details. - * END COPYRIGHT BLOCK **/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -/* For cmocka */ -#include -#include -#include -#include - -#include - -#include - -#include -#include - -#include -#include -#include - -#include -#include - -#include - -struct test_params -{ - int32_t client_thread_count; - int32_t server_thread_count; - int32_t jobs; - int32_t test_timeout; -}; - -int ns_stress_teardown(void **state); -void ns_stress_test(void **state); diff --git a/src/nunc-stans/test/test_nuncstans_stress_core.c b/src/nunc-stans/test/test_nuncstans_stress_core.c deleted file mode 100644 index 0b84540..0000000 --- a/src/nunc-stans/test/test_nuncstans_stress_core.c +++ /dev/null @@ -1,531 +0,0 @@ -/* --- BEGIN COPYRIGHT BLOCK --- - * Copyright (C) 2016 Red Hat - * see files 'COPYING' and 'COPYING.openssl' for use and warranty - * information - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Additional permission under GPLv3 section 7: - * - * If you modify this Program, or any covered work, by linking or - * combining it with OpenSSL, or a modified version of OpenSSL licensed - * under the OpenSSL license - * (https://www.openssl.org/source/license.html), the licensors of this - * Program grant you additional permission to convey the resulting - * work. Corresponding Source for a non-source form of such a - * combination shall include the source code for the parts that are - * licensed under the OpenSSL license as well as that of the covered - * work. - * --- END COPYRIGHT BLOCK --- - */ - -/* - * A self hosting echo server, that stress tests job addition, - * removal, timers, and more. - */ - -/* Our local stress test header */ -#include "test_nuncstans_stress.h" - -struct conn_ctx -{ - size_t offset; /* current offset into buffer for reading or writing */ - size_t len; /* size of buffer */ - size_t needed; /* content-length + start of body */ - size_t body; /* when reading, offset from buffer of beginning of http body */ - size_t cl; /* http content-length when reading */ -#define CONN_BUFFER_SIZE BUFSIZ /* default buffer size */ - char *buffer; -}; - -static FILE *logfp; - -void do_logging(int, const char *, ...); - -int64_t client_success_count = 0; -int64_t server_success_count = 0; -int64_t client_fail_count = 0; -int64_t client_timeout_count = 0; -int64_t server_fail_count = 0; - -int -ns_stress_teardown(void **state) -{ - struct test_params *tparams = (struct test_params *)*state; - free(tparams); - return 0; -} - -#define PR_WOULD_BLOCK(iii) (iii == PR_PENDING_INTERRUPT_ERROR) || (iii == PR_WOULD_BLOCK_ERROR) - -static void -setup_logging(void) -{ - logfp = stdout; -} - -static void -do_vlogging(int level, const char *format, va_list varg) -{ -#ifdef DEBUG - if (level <= LOG_DEBUG) { -#else - if (level <= LOG_ERR) { -#endif - fprintf(logfp, "%d ", level); - vfprintf(logfp, format, varg); - } -} - -void -do_logging(int level, const char *format, ...) -{ - va_list varg; - va_start(varg, format); - do_vlogging(level, format, varg); - va_end(varg); -} - -/* Server specifics */ - -static struct conn_ctx * -conn_ctx_new(void) -{ - struct conn_ctx *connctx = calloc(1, sizeof(struct conn_ctx)); - return connctx; -} - -static void -conn_ctx_free(struct conn_ctx *connctx) -{ - /* Why don't we use PR_DELETE here? */ - if (connctx->buffer != NULL) { - free(connctx->buffer); - } - free(connctx); -} - -static void -server_conn_write(struct ns_job_t *job) -{ - struct conn_ctx *connctx; - int32_t len; - - do_logging(LOG_DEBUG, "job about to write ...\n"); - assert(job != NULL); - connctx = (struct conn_ctx *)ns_job_get_data(job); - assert(connctx != NULL); - if (NS_JOB_IS_TIMER(ns_job_get_output_type(job))) { - do_logging(LOG_ERR, "conn_write: job [%p] timeout\n", job); - __atomic_add_fetch_8(&server_fail_count, 1, __ATOMIC_ACQ_REL); - conn_ctx_free(connctx); - assert_int_equal(ns_job_done(job), 0); - return; - } - - /* Get the data out of our connctx */ - char *data = calloc(1, sizeof(char) * (connctx->offset + 1)); - memcpy(data, connctx->buffer, connctx->offset); - data[connctx->offset] = '\0'; - - /* Should I write a new line also */ - len = PR_Write(ns_job_get_fd(job), data, connctx->offset); - - /* Set the buffer window back to the start */ - connctx->offset = 0; - - if (len < 0) { - /* Error */ - printf("ERROR: occured in conn_write\n"); - } - /* After we finish writing, do we stop being a thread? */ - do_logging(LOG_DEBUG, "Wrote \"%s\"\n", data); - free(data); - /* The job is still a *read* IO event job, so this should be okay */ - assert_int_equal(ns_job_rearm(job), 0); - return; -} - -static void -server_conn_read(struct ns_job_t *job) -{ - do_logging(LOG_DEBUG, "Reading from connection\n"); - - struct conn_ctx *connctx; - int32_t len; - int32_t nbytes; - - assert(job != NULL); - connctx = (struct conn_ctx *)ns_job_get_data(job); - assert(connctx != NULL); - - if (NS_JOB_IS_TIMER(ns_job_get_output_type(job))) { - /* The event that triggered this call back is because we timed out waiting for IO */ - do_logging(LOG_ERR, "conn_read: job [%p] timed out\n", job); - __atomic_add_fetch_8(&server_fail_count, 1, __ATOMIC_ACQ_REL); - conn_ctx_free(connctx); - assert_int_equal(ns_job_done(job), 0); - return; - } - - if (connctx->needed != 0) { - nbytes = connctx->needed - connctx->offset; - } else { - nbytes = CONN_BUFFER_SIZE; - } - - /* If our buffer is incorrectly sized, realloc it to match what we are about to read */ - if ((nbytes + connctx->offset) > connctx->len) { - connctx->len = nbytes + connctx->offset; - connctx->buffer = (char *)PR_Realloc(connctx->buffer, connctx->len * sizeof(char)); - } - - /* Read and append to our buffer */ - len = PR_Read(ns_job_get_fd(job), connctx->buffer + connctx->offset, nbytes); - if (len < 0) { - PRErrorCode prerr = PR_GetError(); - if (PR_WOULD_BLOCK(prerr)) { - /* We don't have poll, so we rearm the job for more data */ - if (NS_JOB_IS_PERSIST(ns_job_get_type(job)) != 0) { - assert_int_equal(ns_job_rearm(job), 0); - } - do_logging(LOG_ERR, "conn_read: block error for job [%p] %d: %s\n", job, PR_GetError(), PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT)); - return; - } else { - do_logging(LOG_ERR, "conn_read: read error for job [%p] %d: %s\n", job, PR_GetError(), PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT)); -#ifdef ATOMIC_64BIT_OPERATIONS - __atomic_add_fetch_8(&server_fail_count, 1, __ATOMIC_ACQ_REL); -#else - PR_AtomicIncrement(&server_fail_count); -#endif - conn_ctx_free(connctx); - assert_int_equal(ns_job_done(job), 0); - return; - } - /* Error */ - } else if (len == 0) { - /* Didn't read anything */ - do_logging(LOG_DEBUG, "conn_read: job [%p] closed\n", job); - /* Increment the success */ - __atomic_add_fetch_8(&server_success_count, 1, __ATOMIC_ACQ_REL); - conn_ctx_free(connctx); - assert_int_equal(ns_job_done(job), 0); - return; - } else { - /* Wild data appears! */ - connctx->offset += len; - char *data = PR_Malloc(sizeof(char) * (connctx->offset + 1)); - memcpy(data, connctx->buffer, connctx->offset); - data[connctx->offset] = '\0'; - do_logging(LOG_DEBUG, "[%p] data received \n", job); - do_logging(LOG_DEBUG, "data: \"%s\" \n", data); - free(data); - server_conn_write(job); - do_logging(LOG_DEBUG, "job rearmed for write ...\n"); - return; - } -} - -static void -server_conn_handler(struct ns_job_t *job) -{ - do_logging(LOG_DEBUG, "Handling a connection\n"); - - assert(job != NULL); - - if (NS_JOB_IS_READ(ns_job_get_type(job)) != 0) { - server_conn_read(job); - } else { - /* We should not be able to get here! */ - assert(0); - } - - return; -} - -static void -server_listen_accept(struct ns_job_t *job) -{ - PRFileDesc *connfd = NULL; - struct conn_ctx *connctx; - PRSocketOptionData prsod = {PR_SockOpt_Nonblocking, {PR_TRUE}}; - - PR_ASSERT(job); - - PRFileDesc *listenfd = ns_job_get_fd(job); - PR_ASSERT(listenfd); - connfd = PR_Accept(listenfd, NULL, PR_INTERVAL_NO_WAIT); - - if (connfd != NULL) { - PR_SetSocketOption(connfd, &prsod); - connctx = conn_ctx_new(); - - assert_int_equal(ns_add_io_job(ns_job_get_tp(job), connfd, NS_JOB_READ | NS_JOB_THREAD, server_conn_handler, connctx, NULL), 0); - - do_logging(LOG_DEBUG, "server_listen_accept: accepting connection to job [%p]\n", job); - - } else { - PRErrorCode prerr = PR_GetError(); - if (PR_WOULD_BLOCK(prerr)) { - /* Let it go .... let it gooooo! */ - /* Can't hold up connection dispatch anymore! */ - } else { - do_logging(LOG_ERR, "server_listen_accept: accept error for job [%p] %d %s\n", job, prerr, PR_ErrorToString(prerr, PR_LANGUAGE_I_DEFAULT)); - } - } -} - -static struct ns_job_t * -server_listener_init(ns_thrpool_t *tp, PRFileDesc *listenfd) -{ - struct ns_job_t *listen_job = NULL; - ns_add_io_job(tp, listenfd, NS_JOB_ACCEPT | NS_JOB_PERSIST, server_listen_accept, NULL, &listen_job); - return listen_job; -} - -/* Client specifics */ - -static void -test_client_shutdown(struct ns_job_t *job) -{ - do_logging(LOG_DEBUG, "Received shutdown signal\n"); - do_logging(LOG_DEBUG, "status .... fail_count: %d success_count: %d\n", client_fail_count, client_success_count); - ns_thrpool_shutdown(ns_job_get_tp(job)); - /* This also needs to start the thrpool shutdown for the server. */ - ns_thrpool_shutdown(ns_job_get_data(job)); -} - -static void -client_response_cb(struct ns_job_t *job) -{ - - char *buffer = calloc(1, 20); - int32_t buflen = 20; - int32_t len = 0; - - len = PR_Read(ns_job_get_fd(job), buffer, buflen); - if (len < 0) { - /* PRErrorCode prerr = PR_GetError(); */ - do_logging(LOG_ERR, "FAIL: connection error, no data \n"); - __atomic_add_fetch_8(&client_fail_count, 1, __ATOMIC_ACQ_REL); - goto done; - } else if (len == 0) { - do_logging(LOG_ERR, "FAIL: connection closed, no data \n"); - __atomic_add_fetch_8(&client_fail_count, 1, __ATOMIC_ACQ_REL); - goto done; - } else { - /* Be paranoid, force last byte null */ - buffer[buflen - 1] = '\0'; - if (strncmp("this is a test!\n", buffer, strlen("this is a test!\n")) != 0) { - do_logging(LOG_ERR, "FAIL: connection incorrect response, no data \n"); - __atomic_add_fetch_8(&client_fail_count, 1, __ATOMIC_ACQ_REL); - goto done; - } - } - - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - __atomic_add_fetch_8(&client_success_count, 1, __ATOMIC_ACQ_REL); - do_logging(LOG_ERR, "PASS: %ld.%ld %d\n", ts.tv_sec, ts.tv_nsec, client_success_count); - -done: - free(buffer); - assert_int_equal(ns_job_done(job), 0); -} - -static void -client_initiate_connection_cb(struct ns_job_t *job) -{ - /* Create a socket */ - PRFileDesc *sock = NULL; - PRNetAddr netaddr = {{0}}; - char *data = "this is a test!\n"; - - sock = PR_OpenTCPSocket(PR_AF_INET); - if (sock == NULL) { - char *err = NULL; - PR_GetErrorText(err); - do_logging(LOG_ERR, "FAIL: Socket failed, %d -> %s\n", PR_GetError(), err); - __atomic_add_fetch_8(&client_fail_count, 1, __ATOMIC_ACQ_REL); - goto done; - } - - PR_SetNetAddr(PR_IpAddrLoopback, PR_AF_INET, 12345, &netaddr); - - /* Connect */ - /* */ - if (PR_Connect(sock, &netaddr, PR_SecondsToInterval(5)) != 0) { - /* char *err = malloc(PR_GetErrorTextLength()); */ - char *err = NULL; - PR_GetErrorText(err); - do_logging(LOG_ERR, "FAIL: cannot connect, timeout %d -> %s check nspr4/prerr.h \n", PR_GetError(), err); - /* Atomic increment fail */ - __atomic_add_fetch_8(&client_timeout_count, 1, __ATOMIC_ACQ_REL); - if (sock != NULL) { - PR_Close(sock); - } - goto done; - } - /* Now write data. */ - assert_true(PR_Write(sock, data, strlen(data) + 1) > 0); - /* create the read job to respond to events on the socket. */ - assert_int_equal(ns_add_io_job(ns_job_get_tp(job), sock, NS_JOB_READ | NS_JOB_THREAD, client_response_cb, NULL, NULL), 0); - -done: - assert_int_equal(ns_job_done(job), 0); -} - -static void -client_create_work(struct ns_job_t *job) -{ - struct test_params *tparams = ns_job_get_data(job); - - struct timespec ts; - PR_Sleep(PR_SecondsToInterval(1)); - clock_gettime(CLOCK_MONOTONIC, &ts); - printf("BEGIN: %ld.%ld\n", ts.tv_sec, ts.tv_nsec); - for (int32_t i = 0; i < tparams->jobs; i++) { - assert_int_equal(ns_add_job(ns_job_get_tp(job), NS_JOB_NONE | NS_JOB_THREAD, client_initiate_connection_cb, NULL, NULL), 0); - } - assert_int_equal(ns_job_done(job), 0); - - printf("Create work thread complete!\n"); -} - -void -ns_stress_test(void **state) -{ - - struct test_params *tparams = *state; - - /* Setup both thread pools. */ - - /* Client first */ - - int64_t job_count = tparams->jobs * tparams->client_thread_count; - struct ns_thrpool_t *ctp; - struct ns_thrpool_config client_ns_config; - struct ns_job_t *sigterm_job = NULL; - struct ns_job_t *sighup_job = NULL; - struct ns_job_t *sigint_job = NULL; - struct ns_job_t *sigtstp_job = NULL; - struct ns_job_t *sigusr1_job = NULL; - struct ns_job_t *sigusr2_job = NULL; - struct ns_job_t *final_job = NULL; - - struct timeval timeout = {tparams->test_timeout, 0}; - - setup_logging(); - - ns_thrpool_config_init(&client_ns_config); - client_ns_config.max_threads = tparams->client_thread_count; - client_ns_config.log_fct = do_vlogging; - ctp = ns_thrpool_new(&client_ns_config); - - /* Now the server */ - - struct ns_thrpool_t *stp; - struct ns_thrpool_config server_ns_config; - struct ns_job_t *listen_job = NULL; - - ns_thrpool_config_init(&server_ns_config); - server_ns_config.max_threads = tparams->server_thread_count; - server_ns_config.log_fct = do_vlogging; - stp = ns_thrpool_new(&server_ns_config); - - /* Now, add the signal handlers. */ - - assert_int_equal(ns_add_signal_job(ctp, SIGTERM, NS_JOB_PERSIST, test_client_shutdown, stp, &sigterm_job), 0); - assert_int_equal(ns_add_signal_job(ctp, SIGHUP, NS_JOB_PERSIST, test_client_shutdown, stp, &sighup_job), 0); - assert_int_equal(ns_add_signal_job(ctp, SIGINT, NS_JOB_PERSIST, test_client_shutdown, stp, &sigint_job), 0); - assert_int_equal(ns_add_signal_job(ctp, SIGTSTP, NS_JOB_PERSIST, test_client_shutdown, stp, &sigtstp_job), 0); - assert_int_equal(ns_add_signal_job(ctp, SIGUSR1, NS_JOB_PERSIST, test_client_shutdown, stp, &sigusr1_job), 0); - assert_int_equal(ns_add_signal_job(ctp, SIGUSR2, NS_JOB_PERSIST, test_client_shutdown, stp, &sigusr2_job), 0); - - /* Create the socket for the server, and set it to listen. */ - - PRFileDesc *listenfd = NULL; - PRNetAddr netaddr; - PRSocketOptionData prsod = {PR_SockOpt_Nonblocking, {PR_TRUE}}; - - PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET, 12345, &netaddr); - - listenfd = PR_OpenTCPSocket(PR_NetAddrFamily(&netaddr)); - PR_SetSocketOption(listenfd, &prsod); - prsod.option = PR_SockOpt_Reuseaddr; - PR_SetSocketOption(listenfd, &prsod); - assert_true(PR_Bind(listenfd, &netaddr) == PR_SUCCESS); - assert_true(PR_Listen(listenfd, 32) == PR_SUCCESS); - - listen_job = server_listener_init(stp, listenfd); - - /* Add the timeout. */ - - assert_int_equal(ns_add_timeout_job(ctp, &timeout, NS_JOB_NONE | NS_JOB_THREAD, test_client_shutdown, stp, &final_job), 0); - - /* While true, add connect / write jobs */ - for (PRInt32 i = 0; i < tparams->client_thread_count; i++) { - assert_int_equal(ns_add_job(ctp, NS_JOB_NONE | NS_JOB_THREAD, client_create_work, tparams, NULL), 0); - } - - /* Wait for all the clients to be done dispatching jobs to the server */ - - if (ns_thrpool_wait(ctp) != 0) { - printf("Error in ctp?\n"); - } - - if (ns_thrpool_wait(stp) != 0) { - printf("Error in stp?\n"); - } - - /* Can mark as done becaus shutdown has begun. */ - assert_int_equal(ns_job_done(listen_job), 0); - - - assert_int_equal(ns_job_done(sigterm_job), 0); - assert_int_equal(ns_job_done(sighup_job), 0); - assert_int_equal(ns_job_done(sigint_job), 0); - assert_int_equal(ns_job_done(sigtstp_job), 0); - assert_int_equal(ns_job_done(sigusr1_job), 0); - assert_int_equal(ns_job_done(sigusr2_job), 0); - assert_int_equal(ns_job_done(final_job), 0); - - ns_thrpool_destroy(stp); - ns_thrpool_destroy(ctp); - - /* Destroy the server thread pool. */ - - if (client_success_count != job_count) { - do_logging(LOG_ERR, "FAIL, not all client jobs succeeded!\n"); - } - if (server_success_count != job_count) { - do_logging(LOG_ERR, "FAIL, not all server jobs succeeded!\n"); - } - do_logging(LOG_ERR, "job_count: %d client_fail_count: %d client_timeout_count: %d server_fail_count: %d client_success_count: %d server_success_count: %d\n", job_count, client_fail_count, client_timeout_count, server_fail_count, client_success_count, server_success_count); - - assert_int_equal(client_fail_count, 0); - /* We don't check the client timeout count, because it's often non 0, and it's generally because we *really* overwhelm the framework, we can handle the loss */ - /* assert_int_equal(client_timeout_count, 0); */ - assert_int_equal(server_fail_count, 0); - /* Can't assert this due to timeout: instead guarantee if we connect to the server, we WORKED 100% of the time. */ - /* - assert_int_equal(server_success_count, job_count); - assert_int_equal(client_success_count, job_count); - */ - assert_int_equal(server_success_count, client_success_count); - int32_t job_threshold = (tparams->jobs * tparams->client_thread_count) * 0.95; - assert_true(client_success_count >= job_threshold); - - PR_Cleanup(); -} diff --git a/src/nunc-stans/test/test_nuncstans_stress_large.c b/src/nunc-stans/test/test_nuncstans_stress_large.c deleted file mode 100644 index 07472f7..0000000 --- a/src/nunc-stans/test/test_nuncstans_stress_large.c +++ /dev/null @@ -1,32 +0,0 @@ -/** BEGIN COPYRIGHT BLOCK - * Copyright (c) 2017, Red Hat, Inc - * All rights reserved. - * - * License: GPL (version 3 or any later version). - * See LICENSE for details. - * END COPYRIGHT BLOCK **/ - -#include "test_nuncstans_stress.h" - -int -ns_stress_large_setup(void **state) -{ - struct test_params *tparams = malloc(sizeof(struct test_params)); - tparams->client_thread_count = 80; - tparams->server_thread_count = 20; - tparams->jobs = 200; - tparams->test_timeout = 70; - *state = tparams; - return 0; -} - -int -main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test_setup_teardown(ns_stress_test, - ns_stress_large_setup, - ns_stress_teardown), - }; - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/src/nunc-stans/test/test_nuncstans_stress_small.c b/src/nunc-stans/test/test_nuncstans_stress_small.c deleted file mode 100644 index 1aaa383..0000000 --- a/src/nunc-stans/test/test_nuncstans_stress_small.c +++ /dev/null @@ -1,32 +0,0 @@ -/** BEGIN COPYRIGHT BLOCK - * Copyright (c) 2017, Red Hat, Inc - * All rights reserved. - * - * License: GPL (version 3 or any later version). - * See LICENSE for details. - * END COPYRIGHT BLOCK **/ - -#include "test_nuncstans_stress.h" - -int -ns_stress_small_setup(void **state) -{ - struct test_params *tparams = malloc(sizeof(struct test_params)); - tparams->client_thread_count = 4; - tparams->server_thread_count = 1; - tparams->jobs = 64; - tparams->test_timeout = 30; - *state = tparams; - return 0; -} - -int -main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test_setup_teardown(ns_stress_test, - ns_stress_small_setup, - ns_stress_teardown), - }; - return cmocka_run_group_tests(tests, NULL, NULL); -}