Add missing listener threads data vs fork() protection
[ust.git] / libust / tracectl.c
index 1bd722949aaca5c4335f53b16b270d30e68f238d..bef4537dd4ffeb5311c4e83dabbf60adfbb3fbd3 100644 (file)
@@ -63,6 +63,16 @@ static char receive_buffer[USTCOMM_BUFFER_SIZE];
 static char send_buffer[USTCOMM_BUFFER_SIZE];
 
 static int epoll_fd;
+
+/*
+ * Listener thread data vs fork() protection mechanism. Ensures that no listener
+ * thread mutexes and data structures are being concurrently modified or held by
+ * other threads when fork() is executed.
+ */
+static pthread_mutex_t listener_thread_data_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Mutex protecting listen_sock. Nests inside listener_thread_data_mutex. */
+static pthread_mutex_t listen_sock_mutex = PTHREAD_MUTEX_INITIALIZER;
 static struct ustcomm_sock *listen_sock;
 
 extern struct chan_info_struct chan_infos[];
@@ -535,7 +545,12 @@ unlock_traces:
 
 static void listener_cleanup(void *ptr)
 {
-       ustcomm_del_named_sock(listen_sock, 0);
+       pthread_mutex_lock(&listen_sock_mutex);
+       if (listen_sock) {
+               ustcomm_del_named_sock(listen_sock, 0);
+               listen_sock = NULL;
+       }
+       pthread_mutex_unlock(&listen_sock_mutex);
 }
 
 static void force_subbuf_switch()
@@ -1080,6 +1095,7 @@ void *listener_main(void *p)
                }
 
                for (i = 0; i < nfds; i++) {
+                       pthread_mutex_lock(&listener_thread_data_mutex);
                        epoll_sock = (struct ustcomm_sock *)events[i].data.ptr;
                        if (epoll_sock == listen_sock) {
                                addr_size = sizeof(struct sockaddr);
@@ -1108,6 +1124,7 @@ void *listener_main(void *p)
                                                           epoll_sock->fd);
                                }
                        }
+                       pthread_mutex_unlock(&listener_thread_data_mutex);
                }
        }
 
@@ -1569,6 +1586,7 @@ static void ust_fork(void)
 {
        struct ust_buffer *buf, *buf_tmp;
        struct ustcomm_sock *sock, *sock_tmp;
+       struct ust_trace *trace, *trace_tmp;
        int result;
 
        /* FIXME: technically, the locks could have been taken before the fork */
@@ -1577,10 +1595,14 @@ static void ust_fork(void)
        /* Get the pid of the new process */
        processpid = getpid();
 
-       /* break lock if necessary */
-       ltt_unlock_traces();
+       /*
+        * FIXME: This could be prettier, we loop over the list twice and
+        * following good locking practice should lock around the loop
+        */
+       cds_list_for_each_entry_safe(trace, trace_tmp, &ltt_traces.head, list) {
+               ltt_trace_stop(trace->trace_name);
+       }
 
-       ltt_trace_stop("auto");
        /* Delete all active connections, but leave them in the epoll set */
        cds_list_for_each_entry_safe(sock, sock_tmp, &ust_socks, list) {
                ustcomm_del_sock(sock, 1);
@@ -1589,21 +1611,22 @@ static void ust_fork(void)
        /* Delete all blocked consumers */
        cds_list_for_each_entry_safe(buf, buf_tmp, &open_buffers_list,
                                 open_buffers_list) {
-               result = close(buf->data_ready_fd_read);
-               if (result == -1) {
-                       PERROR("close");
-               }
-               result = close(buf->data_ready_fd_write);
-               if (result == -1) {
-                       PERROR("close");
-               }
                cds_list_del(&buf->open_buffers_list);
        }
 
-       ltt_trace_destroy("auto", 1);
+       /*
+        * FIXME: This could be prettier, we loop over the list twice and
+        * following good locking practice should lock around the loop
+        */
+       cds_list_for_each_entry_safe(trace, trace_tmp, &ltt_traces.head, list) {
+               ltt_trace_destroy(trace->trace_name, 1);
+       }
 
-       /* Clean up the listener socket and epoll, keeping the scoket file */
-       ustcomm_del_named_sock(listen_sock, 1);
+       /* Clean up the listener socket and epoll, keeping the socket file */
+       if (listen_sock) {
+               ustcomm_del_named_sock(listen_sock, 1);
+               listen_sock = NULL;
+       }
        close(epoll_fd);
 
        /* Re-start the launch sequence */
@@ -1658,6 +1681,16 @@ void ust_before_fork(ust_fork_info_t *fork_info)
                 PERROR("sigprocmask");
                 return;
         }
+
+       /*
+        * Take the fork lock to make sure we are not in the middle of
+        * something in the listener thread.
+        */
+       pthread_mutex_lock(&listener_thread_data_mutex);
+       /*
+        * Hold listen_sock_mutex to protect from listen_sock teardown.
+        */
+       pthread_mutex_lock(&listen_sock_mutex);
 }
 
 /* Don't call this function directly in a traced program */
@@ -1665,6 +1698,9 @@ static void ust_after_fork_common(ust_fork_info_t *fork_info)
 {
        int result;
 
+       pthread_mutex_unlock(&listen_sock_mutex);
+       pthread_mutex_unlock(&listener_thread_data_mutex);
+
         /* Restore signals */
         result = sigprocmask(SIG_SETMASK, &fork_info->orig_sigs, NULL);
         if (result == -1) {
This page took 0.023585 seconds and 4 git commands to generate.