void sessiond_notify_ready(void);
void sessiond_signal_parents(void);
+void sessiond_set_client_thread_state(bool running);
+void sessiond_wait_client_thread_stopped(void);
+
#endif /* _LTT_SESSIOND_H */
static void sessiond_cleanup(void)
{
int ret;
- struct ltt_session *sess, *stmp;
struct ltt_session_list *session_list = session_get_list();
DBG("Cleanup sessiond");
DBG("Removing directory %s", config.consumerd64_path.value);
(void) rmdir(config.consumerd64_path.value);
- DBG("Cleaning up all sessions");
-
- /* Destroy session list mutex */
- if (session_list) {
- session_lock_list();
- /* Cleanup ALL session */
- cds_list_for_each_entry_safe(sess, stmp,
- &session_list->head, list) {
- if (sess->destroyed) {
- continue;
- }
- cmd_destroy_session(sess,
- notification_thread_handle);
- }
- session_unlock_list();
- pthread_mutex_destroy(&session_list->lock);
- }
+ pthread_mutex_destroy(&session_list->lock);
wait_consumer(&kconsumer_data);
wait_consumer(&ustconsumer64_data);
health_code_update();
+ /* Set state as running. */
+ sessiond_set_client_thread_state(true);
+
while (1) {
const struct cmd_completion_handler *cmd_completion_handler;
errno = ret;
PERROR("join_consumer ust64");
}
+
+ /* Set state as non-running. */
+ sessiond_set_client_thread_state(false);
return NULL;
}
return ret;
}
+static void destroy_all_sessions_and_wait(void)
+{
+ struct ltt_session *session, *tmp;
+ struct ltt_session_list *session_list;
+
+ session_list = session_get_list();
+ DBG("Initiating destruction of all sessions");
+
+ if (!session_list) {
+ return;
+ }
+
+ /*
+ * Ensure that the client thread is no longer accepting new commands,
+ * which could cause new sessions to be created.
+ */
+ sessiond_wait_client_thread_stopped();
+
+ session_lock_list();
+ /* Initiate the destruction of all sessions. */
+ cds_list_for_each_entry_safe(session, tmp,
+ &session_list->head, list) {
+ if (!session_get(session)) {
+ continue;
+ }
+
+ session_lock(session);
+ if (session->destroyed) {
+ goto unlock_session;
+ }
+ (void) cmd_destroy_session(session,
+ notification_thread_handle);
+ unlock_session:
+ session_unlock(session);
+ session_put(session);
+ }
+ session_unlock_list();
+
+ /* Wait for the destruction of all sessions to complete. */
+ DBG("Waiting for the destruction of all sessions to complete");
+ session_list_wait_empty();
+ DBG("Destruction of all sessions completed");
+}
+
/*
* main
*/
PERROR("pthread_join load_session_thread");
retval = -1;
}
+
+ /* Initiate teardown once activity occurs on the quit pipe. */
+ sessiond_wait_for_quit_pipe(-1U);
+ destroy_all_sessions_and_wait();
exit_load_session:
if (is_root && !config.no_kernel) {
#include <urcu.h>
#include <dirent.h>
#include <sys/types.h>
+#include <pthread.h>
#include <common/common.h>
#include <common/sessiond-comm/sessiond-comm.h>
static struct ltt_session_list ltt_session_list = {
.head = CDS_LIST_HEAD_INIT(ltt_session_list.head),
.lock = PTHREAD_MUTEX_INITIALIZER,
+ .removal_cond = PTHREAD_COND_INITIALIZER,
.next_uuid = 0,
};
return <t_session_list;
}
+/*
+ * Returns once the session list is empty.
+ */
+void session_list_wait_empty(void)
+{
+ pthread_mutex_lock(<t_session_list.lock);
+ while (!cds_list_empty(<t_session_list.head)) {
+ pthread_cond_wait(<t_session_list.removal_cond,
+ <t_session_list.lock);
+ }
+ pthread_mutex_unlock(<t_session_list.lock);
+}
+
/*
* Acquire session list lock
*/
consumer_output_put(session->consumer);
snapshot_destroy(&session->snapshot);
+
+ ASSERT_LOCKED(ltt_session_list.lock);
del_session_list(session);
del_session_ht(session);
+ pthread_cond_broadcast(<t_session_list.removal_cond);
free(session);
}
* iterate or/and do any actions on that list.
*/
pthread_mutex_t lock;
+ /*
+ * This condition variable is signaled on every removal from
+ * the session list.
+ */
+ pthread_cond_t removal_cond;
/*
* Session unique ID generator. The session list lock MUST be
struct ltt_session *session_find_by_name(const char *name);
struct ltt_session *session_find_by_id(uint64_t id);
+
struct ltt_session_list *session_get_list(void);
+void session_list_wait_empty(void);
int session_access_ok(struct ltt_session *session, uid_t uid, gid_t gid);
#include "lttng-sessiond.h"
#include "utils.h"
#include <common/utils.h>
+#include <pthread.h>
+
+#define USEC_PER_SEC 1000000
/*
* Quit pipe for all threads. This permits a single cancellation point
*/
static int thread_quit_pipe[2] = { -1, -1 };
+/*
+ * Allows threads to query the state of the client thread.
+ */
+static struct client_thread_state {
+ pthread_cond_t cond;
+ pthread_mutex_t lock;
+ bool is_running;
+} client_thread_state = {
+ .cond = PTHREAD_COND_INITIALIZER,
+ .lock = PTHREAD_MUTEX_INITIALIZER,
+ .is_running = false
+};
+
/*
* Init thread quit pipe.
*
/*
* Wait for a notification on the quit pipe (with a timeout).
*
+ * A timeout value of -1U means no timeout.
+ *
* Returns 1 if the caller should quit, 0 if the timeout was reached, and
* -1 if an error was encountered.
*/
FD_ZERO(&read_fds);
FD_SET(thread_quit_pipe[0], &read_fds);
memset(&timeout, 0, sizeof(timeout));
- timeout.tv_usec = timeout_us;
+ timeout.tv_sec = timeout_us / USEC_PER_SEC;
+ timeout.tv_usec = timeout_us % USEC_PER_SEC;
while (true) {
ret = select(thread_quit_pipe[0] + 1, &read_fds, NULL, NULL,
- &timeout);
+ timeout_us != -1U ? &timeout : NULL);
if (ret < 0 && errno == EINTR) {
/* Retry on interrupt. */
continue;
utils_close_pipe(thread_quit_pipe);
}
+void sessiond_set_client_thread_state(bool running)
+{
+ pthread_mutex_lock(&client_thread_state.lock);
+ client_thread_state.is_running = running;
+ pthread_cond_broadcast(&client_thread_state.cond);
+ pthread_mutex_unlock(&client_thread_state.lock);
+}
+
+void sessiond_wait_client_thread_stopped(void)
+{
+ pthread_mutex_lock(&client_thread_state.lock);
+ while (client_thread_state.is_running) {
+ pthread_cond_wait(&client_thread_state.cond,
+ &client_thread_state.lock);
+ }
+ pthread_mutex_unlock(&client_thread_state.lock);
+}
+
static
int __sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size,
int *a_pipe)