2 * Copyright (C) 2017 - Julien Desfossez <jdesfossez@efficios.com>
3 * Copyright (C) 2018 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License, version 2 only, as
7 * published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 51
16 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include <lttng/trigger/trigger.h>
21 #include <common/error.h>
22 #include <common/config/session-config.h>
23 #include <common/defaults.h>
24 #include <common/utils.h>
25 #include <common/futex.h>
26 #include <common/align.h>
27 #include <common/time.h>
28 #include <common/hashtable/utils.h>
29 #include <sys/eventfd.h>
35 #include <common/kernel-ctl/kernel-ctl.h>
36 #include <lttng/notification/channel-internal.h>
37 #include <lttng/rotate-internal.h>
39 #include "rotation-thread.h"
40 #include "lttng-sessiond.h"
41 #include "health-sessiond.h"
46 #include "notification-thread-commands.h"
51 #include <urcu/list.h>
53 struct lttng_notification_channel
*rotate_notification_channel
= NULL
;
55 struct rotation_thread
{
56 struct lttng_poll_event events
;
59 struct rotation_thread_job
{
60 enum rotation_thread_job_type type
;
61 struct ltt_session
*session
;
62 /* List member in struct rotation_thread_timer_queue. */
63 struct cds_list_head head
;
67 * The timer thread enqueues jobs and wakes up the rotation thread.
68 * When the rotation thread wakes up, it empties the queue.
70 struct rotation_thread_timer_queue
{
71 struct lttng_pipe
*event_pipe
;
72 struct cds_list_head list
;
76 struct rotation_thread_handle
{
77 struct rotation_thread_timer_queue
*rotation_timer_queue
;
78 /* Access to the notification thread cmd_queue */
79 struct notification_thread_handle
*notification_thread_handle
;
80 /* Thread-specific quit pipe. */
81 struct lttng_pipe
*quit_pipe
;
85 const char *get_job_type_str(enum rotation_thread_job_type job_type
)
88 case ROTATION_THREAD_JOB_TYPE_CHECK_PENDING_ROTATION
:
89 return "CHECK_PENDING_ROTATION";
90 case ROTATION_THREAD_JOB_TYPE_SCHEDULED_ROTATION
:
91 return "SCHEDULED_ROTATION";
97 struct rotation_thread_timer_queue
*rotation_thread_timer_queue_create(void)
99 struct rotation_thread_timer_queue
*queue
= NULL
;
101 queue
= zmalloc(sizeof(*queue
));
103 PERROR("Failed to allocate timer rotate queue");
107 queue
->event_pipe
= lttng_pipe_open(FD_CLOEXEC
| O_NONBLOCK
);
108 CDS_INIT_LIST_HEAD(&queue
->list
);
109 pthread_mutex_init(&queue
->lock
, NULL
);
114 void log_job_destruction(const struct rotation_thread_job
*job
)
116 enum lttng_error_level log_level
;
117 const char *job_type_str
= get_job_type_str(job
->type
);
120 case ROTATION_THREAD_JOB_TYPE_SCHEDULED_ROTATION
:
122 * Not a problem, the scheduled rotation is racing with the teardown
123 * of the daemon. In this case, the rotation will not happen, which
124 * is not a problem (or at least, not important enough to delay
125 * the shutdown of the session daemon).
127 log_level
= PRINT_DBG
;
129 case ROTATION_THREAD_JOB_TYPE_CHECK_PENDING_ROTATION
:
130 /* This is not expected to happen; warn the user. */
131 log_level
= PRINT_WARN
;
137 LOG(log_level
, "Rotation thread timer queue still contains job of type %s targeting session \"%s\" on destruction",
138 job_type_str
, job
->session
->name
);
141 void rotation_thread_timer_queue_destroy(
142 struct rotation_thread_timer_queue
*queue
)
148 lttng_pipe_destroy(queue
->event_pipe
);
150 pthread_mutex_lock(&queue
->lock
);
151 assert(cds_list_empty(&queue
->list
));
152 pthread_mutex_unlock(&queue
->lock
);
153 pthread_mutex_destroy(&queue
->lock
);
158 * Destroy the thread data previously created by the init function.
160 void rotation_thread_handle_destroy(
161 struct rotation_thread_handle
*handle
)
163 lttng_pipe_destroy(handle
->quit_pipe
);
167 struct rotation_thread_handle
*rotation_thread_handle_create(
168 struct rotation_thread_timer_queue
*rotation_timer_queue
,
169 struct notification_thread_handle
*notification_thread_handle
)
171 struct rotation_thread_handle
*handle
;
173 handle
= zmalloc(sizeof(*handle
));
178 handle
->rotation_timer_queue
= rotation_timer_queue
;
179 handle
->notification_thread_handle
= notification_thread_handle
;
180 handle
->quit_pipe
= lttng_pipe_open(FD_CLOEXEC
);
181 if (!handle
->quit_pipe
) {
188 rotation_thread_handle_destroy(handle
);
193 * Called with the rotation_thread_timer_queue lock held.
194 * Return true if the same timer job already exists in the queue, false if not.
197 bool timer_job_exists(const struct rotation_thread_timer_queue
*queue
,
198 enum rotation_thread_job_type job_type
,
199 struct ltt_session
*session
)
202 struct rotation_thread_job
*job
;
204 cds_list_for_each_entry(job
, &queue
->list
, head
) {
205 if (job
->session
== session
&& job
->type
== job_type
) {
214 void rotation_thread_enqueue_job(struct rotation_thread_timer_queue
*queue
,
215 enum rotation_thread_job_type job_type
,
216 struct ltt_session
*session
)
219 const char dummy
= '!';
220 struct rotation_thread_job
*job
= NULL
;
221 const char *job_type_str
= get_job_type_str(job_type
);
223 pthread_mutex_lock(&queue
->lock
);
224 if (timer_job_exists(queue
, job_type
, session
)) {
226 * This timer job is already pending, we don't need to add
232 job
= zmalloc(sizeof(struct rotation_thread_job
));
234 PERROR("Failed to allocate rotation thread job of type \"%s\" for session \"%s\"",
235 job_type_str
, session
->name
);
238 /* No reason for this to fail as the caller must hold a reference. */
239 (void) session_get(session
);
241 job
->session
= session
;
242 job
->type
= job_type
;
243 cds_list_add_tail(&job
->head
, &queue
->list
);
245 ret
= lttng_write(lttng_pipe_get_writefd(queue
->event_pipe
), &dummy
,
249 * We do not want to block in the timer handler, the job has
250 * been enqueued in the list, the wakeup pipe is probably full,
251 * the job will be processed when the rotation_thread catches
254 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
) {
256 * Not an error, but would be surprising and indicate
257 * that the rotation thread can't keep up with the
260 DBG("Wake-up pipe of rotation thread job queue is full");
263 PERROR("Failed to wake-up the rotation thread after pushing a job of type \"%s\" for session \"%s\"",
264 job_type_str
, session
->name
);
269 pthread_mutex_unlock(&queue
->lock
);
273 int init_poll_set(struct lttng_poll_event
*poll_set
,
274 struct rotation_thread_handle
*handle
)
279 * Create pollset with size 3:
280 * - rotation thread quit pipe,
281 * - rotation thread timer queue pipe,
282 * - notification channel sock,
284 ret
= lttng_poll_create(poll_set
, 5, LTTNG_CLOEXEC
);
289 ret
= lttng_poll_add(poll_set
,
290 lttng_pipe_get_readfd(handle
->quit_pipe
),
293 ERR("[rotation-thread] Failed to add quit pipe read fd to poll set");
297 ret
= lttng_poll_add(poll_set
,
298 lttng_pipe_get_readfd(handle
->rotation_timer_queue
->event_pipe
),
301 ERR("[rotation-thread] Failed to add rotate_pending fd to poll set");
307 lttng_poll_clean(poll_set
);
312 void fini_thread_state(struct rotation_thread
*state
)
314 lttng_poll_clean(&state
->events
);
315 if (rotate_notification_channel
) {
316 lttng_notification_channel_destroy(rotate_notification_channel
);
321 int init_thread_state(struct rotation_thread_handle
*handle
,
322 struct rotation_thread
*state
)
326 memset(state
, 0, sizeof(*state
));
327 lttng_poll_init(&state
->events
);
329 ret
= init_poll_set(&state
->events
, handle
);
331 ERR("[rotation-thread] Failed to initialize rotation thread poll set");
335 rotate_notification_channel
= lttng_notification_channel_create(
336 lttng_session_daemon_notification_endpoint
);
337 if (!rotate_notification_channel
) {
338 ERR("[rotation-thread] Could not create notification channel");
342 ret
= lttng_poll_add(&state
->events
, rotate_notification_channel
->socket
,
345 ERR("[rotation-thread] Failed to add notification fd to pollset");
354 void check_session_rotation_pending_on_consumers(struct ltt_session
*session
,
355 bool *_rotation_completed
)
358 struct consumer_socket
*socket
;
359 struct cds_lfht_iter iter
;
360 enum consumer_trace_chunk_exists_status exists_status
;
362 bool chunk_exists_on_peer
= false;
363 enum lttng_trace_chunk_status chunk_status
;
365 assert(session
->chunk_being_archived
);
368 * Check for a local pending rotation on all consumers (32-bit
369 * user space, 64-bit user space, and kernel).
372 if (!session
->ust_session
) {
375 cds_lfht_for_each_entry(session
->ust_session
->consumer
->socks
->ht
,
376 &iter
, socket
, node
.node
) {
377 relayd_id
= session
->ust_session
->consumer
->type
== CONSUMER_DST_LOCAL
?
379 session
->ust_session
->consumer
->net_seq_index
;
381 pthread_mutex_lock(socket
->lock
);
382 ret
= consumer_trace_chunk_exists(socket
,
384 session
->id
, session
->chunk_being_archived
,
387 pthread_mutex_unlock(socket
->lock
);
388 ERR("Error occured while checking rotation status on consumer daemon");
392 if (exists_status
!= CONSUMER_TRACE_CHUNK_EXISTS_STATUS_UNKNOWN_CHUNK
) {
393 pthread_mutex_unlock(socket
->lock
);
394 chunk_exists_on_peer
= true;
397 pthread_mutex_unlock(socket
->lock
);
401 if (!session
->kernel_session
) {
404 cds_lfht_for_each_entry(session
->kernel_session
->consumer
->socks
->ht
,
405 &iter
, socket
, node
.node
) {
406 pthread_mutex_lock(socket
->lock
);
407 relayd_id
= session
->kernel_session
->consumer
->type
== CONSUMER_DST_LOCAL
?
409 session
->kernel_session
->consumer
->net_seq_index
;
411 ret
= consumer_trace_chunk_exists(socket
,
413 session
->id
, session
->chunk_being_archived
,
416 pthread_mutex_unlock(socket
->lock
);
417 ERR("Error occured while checking rotation status on consumer daemon");
421 if (exists_status
!= CONSUMER_TRACE_CHUNK_EXISTS_STATUS_UNKNOWN_CHUNK
) {
422 pthread_mutex_unlock(socket
->lock
);
423 chunk_exists_on_peer
= true;
426 pthread_mutex_unlock(socket
->lock
);
432 if (!chunk_exists_on_peer
) {
433 uint64_t chunk_being_archived_id
;
435 chunk_status
= lttng_trace_chunk_get_id(
436 session
->chunk_being_archived
,
437 &chunk_being_archived_id
);
438 assert(chunk_status
== LTTNG_TRACE_CHUNK_STATUS_OK
);
439 DBG("[rotation-thread] Rotation of trace archive %" PRIu64
" of session \"%s\" is complete on all consumers",
440 chunk_being_archived_id
,
443 *_rotation_completed
= !chunk_exists_on_peer
;
445 ret
= session_reset_rotation_state(session
,
446 LTTNG_ROTATION_STATE_ERROR
);
448 ERR("Failed to reset rotation state of session \"%s\"",
455 * Check if the last rotation was completed, called with session lock held.
456 * Should only return non-zero in the event of a fatal error. Doing so will
457 * shutdown the thread.
460 int check_session_rotation_pending(struct ltt_session
*session
,
461 struct notification_thread_handle
*notification_thread_handle
)
464 struct lttng_trace_archive_location
*location
;
465 enum lttng_trace_chunk_status chunk_status
;
466 bool rotation_completed
= false;
467 const char *archived_chunk_name
;
468 uint64_t chunk_being_archived_id
;
470 chunk_status
= lttng_trace_chunk_get_id(session
->chunk_being_archived
,
471 &chunk_being_archived_id
);
472 assert(chunk_status
== LTTNG_TRACE_CHUNK_STATUS_OK
);
474 DBG("[rotation-thread] Checking for pending rotation on session \"%s\", trace archive %" PRIu64
,
475 session
->name
, chunk_being_archived_id
);
477 if (!session
->chunk_being_archived
) {
483 * The rotation-pending check timer of a session is launched in
484 * one-shot mode. If the rotation is incomplete, the rotation
485 * thread will re-enable the pending-check timer.
487 * The timer thread can't stop the timer itself since it is involved
488 * in the check for the timer's quiescence.
490 ret
= timer_session_rotation_pending_check_stop(session
);
495 check_session_rotation_pending_on_consumers(session
,
496 &rotation_completed
);
497 if (!rotation_completed
||
498 session
->rotation_state
== LTTNG_ROTATION_STATE_ERROR
) {
503 * Now we can clear the "ONGOING" state in the session. New
504 * rotations can start now.
506 chunk_status
= lttng_trace_chunk_get_name(session
->chunk_being_archived
,
507 &archived_chunk_name
, NULL
);
508 assert(chunk_status
== LTTNG_TRACE_CHUNK_STATUS_OK
);
509 free(session
->last_archived_chunk_name
);
510 session
->last_archived_chunk_name
= strdup(archived_chunk_name
);
511 if (!session
->last_archived_chunk_name
) {
512 PERROR("Failed to duplicate archived chunk name");
514 session_reset_rotation_state(session
, LTTNG_ROTATION_STATE_COMPLETED
);
516 if (!session
->quiet_rotation
) {
517 location
= session_get_trace_archive_location(session
);
518 /* Ownership of location is transferred. */
519 ret
= notification_thread_command_session_rotation_completed(
520 notification_thread_handle
,
524 session
->last_archived_chunk_id
.value
,
526 if (ret
!= LTTNG_OK
) {
527 ERR("[rotation-thread] Failed to notify notification thread of completed rotation for session %s",
532 if (!session
->active
&& !session
->quiet_rotation
) {
534 * A stop command was issued during the rotation, it is
535 * up to the rotation completion check to perform the
536 * renaming of the last chunk that was produced.
538 ret
= notification_thread_command_session_rotation_ongoing(
539 notification_thread_handle
,
543 session
->most_recent_chunk_id
.value
);
544 if (ret
!= LTTNG_OK
) {
545 ERR("[rotation-thread] Failed to notify notification thread of completed rotation for session %s",
549 /* Ownership of location is transferred. */
550 location
= session_get_trace_archive_location(session
);
551 ret
= notification_thread_command_session_rotation_completed(
552 notification_thread_handle
,
556 session
->most_recent_chunk_id
.value
,
558 if (ret
!= LTTNG_OK
) {
559 ERR("[rotation-thread] Failed to notify notification thread of completed rotation for session %s",
566 if (session
->rotation_state
== LTTNG_ROTATION_STATE_ONGOING
) {
567 uint64_t chunk_being_archived_id
;
569 chunk_status
= lttng_trace_chunk_get_id(
570 session
->chunk_being_archived
,
571 &chunk_being_archived_id
);
572 assert(chunk_status
== LTTNG_TRACE_CHUNK_STATUS_OK
);
574 DBG("[rotation-thread] Rotation of trace archive %" PRIu64
" is still pending for session %s",
575 chunk_being_archived_id
, session
->name
);
576 ret
= timer_session_rotation_pending_check_start(session
,
577 DEFAULT_ROTATE_PENDING_TIMER
);
579 ERR("Failed to re-enable rotation pending timer");
588 /* Call with the session and session_list locks held. */
590 int launch_session_rotation(struct ltt_session
*session
)
593 struct lttng_rotate_session_return rotation_return
;
595 DBG("[rotation-thread] Launching scheduled time-based rotation on session \"%s\"",
598 ret
= cmd_rotate_session(session
, &rotation_return
, false);
599 if (ret
== LTTNG_OK
) {
600 DBG("[rotation-thread] Scheduled time-based rotation successfully launched on session \"%s\"",
603 /* Don't consider errors as fatal. */
604 DBG("[rotation-thread] Scheduled time-based rotation aborted for session %s: %s",
605 session
->name
, lttng_strerror(ret
));
611 int run_job(struct rotation_thread_job
*job
, struct ltt_session
*session
,
612 struct notification_thread_handle
*notification_thread_handle
)
617 case ROTATION_THREAD_JOB_TYPE_SCHEDULED_ROTATION
:
618 ret
= launch_session_rotation(session
);
620 case ROTATION_THREAD_JOB_TYPE_CHECK_PENDING_ROTATION
:
621 ret
= check_session_rotation_pending(session
,
622 notification_thread_handle
);
631 int handle_job_queue(struct rotation_thread_handle
*handle
,
632 struct rotation_thread
*state
,
633 struct rotation_thread_timer_queue
*queue
)
638 struct ltt_session
*session
;
639 struct rotation_thread_job
*job
;
641 /* Take the queue lock only to pop an element from the list. */
642 pthread_mutex_lock(&queue
->lock
);
643 if (cds_list_empty(&queue
->list
)) {
644 pthread_mutex_unlock(&queue
->lock
);
647 job
= cds_list_first_entry(&queue
->list
,
649 cds_list_del(&job
->head
);
650 pthread_mutex_unlock(&queue
->lock
);
653 session
= job
->session
;
655 DBG("[rotation-thread] Session \"%s\" not found",
658 * This is a non-fatal error, and we cannot report it to
659 * the user (timer), so just print the error and
660 * continue the processing.
662 * While the timer thread will purge pending signals for
663 * a session on the session's destruction, it is
664 * possible for a job targeting that session to have
665 * already been queued before it was destroyed.
668 session_put(session
);
669 session_unlock_list();
673 session_lock(session
);
674 ret
= run_job(job
, session
, handle
->notification_thread_handle
);
675 session_unlock(session
);
676 /* Release reference held by the job. */
677 session_put(session
);
678 session_unlock_list();
692 int handle_condition(const struct lttng_condition
*condition
,
693 const struct lttng_evaluation
*evaluation
,
694 struct notification_thread_handle
*notification_thread_handle
)
697 const char *condition_session_name
= NULL
;
698 enum lttng_condition_type condition_type
;
699 enum lttng_condition_status condition_status
;
700 enum lttng_evaluation_status evaluation_status
;
702 struct ltt_session
*session
;
704 condition_type
= lttng_condition_get_type(condition
);
706 if (condition_type
!= LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE
) {
708 ERR("[rotation-thread] Condition type and session usage type are not the same");
712 /* Fetch info to test */
713 condition_status
= lttng_condition_session_consumed_size_get_session_name(
714 condition
, &condition_session_name
);
715 if (condition_status
!= LTTNG_CONDITION_STATUS_OK
) {
716 ERR("[rotation-thread] Session name could not be fetched");
720 evaluation_status
= lttng_evaluation_session_consumed_size_get_consumed_size(evaluation
,
722 if (evaluation_status
!= LTTNG_EVALUATION_STATUS_OK
) {
723 ERR("[rotation-thread] Failed to get evaluation");
729 session
= session_find_by_name(condition_session_name
);
732 session_unlock_list();
733 ERR("[rotation-thread] Session \"%s\" not found",
734 condition_session_name
);
737 session_lock(session
);
739 ret
= unsubscribe_session_consumed_size_rotation(session
,
740 notification_thread_handle
);
745 ret
= cmd_rotate_session(session
, NULL
, false);
746 if (ret
== -LTTNG_ERR_ROTATION_PENDING
) {
747 DBG("Rotate already pending, subscribe to the next threshold value");
748 } else if (ret
!= LTTNG_OK
) {
749 ERR("[rotation-thread] Failed to rotate on size notification with error: %s",
750 lttng_strerror(ret
));
754 ret
= subscribe_session_consumed_size_rotation(session
,
755 consumed
+ session
->rotate_size
,
756 notification_thread_handle
);
758 ERR("[rotation-thread] Failed to subscribe to session consumed size condition");
764 session_unlock(session
);
765 session_put(session
);
766 session_unlock_list();
772 int handle_notification_channel(int fd
,
773 struct rotation_thread_handle
*handle
,
774 struct rotation_thread
*state
)
777 bool notification_pending
;
778 struct lttng_notification
*notification
= NULL
;
779 enum lttng_notification_channel_status status
;
780 const struct lttng_evaluation
*notification_evaluation
;
781 const struct lttng_condition
*notification_condition
;
783 status
= lttng_notification_channel_has_pending_notification(
784 rotate_notification_channel
, ¬ification_pending
);
785 if (status
!= LTTNG_NOTIFICATION_CHANNEL_STATUS_OK
) {
786 ERR("[rotation-thread ]Error occurred while checking for pending notification");
791 if (!notification_pending
) {
796 /* Receive the next notification. */
797 status
= lttng_notification_channel_get_next_notification(
798 rotate_notification_channel
,
802 case LTTNG_NOTIFICATION_CHANNEL_STATUS_OK
:
804 case LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED
:
805 /* Not an error, we will wait for the next one */
808 case LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED
:
809 ERR("Notification channel was closed");
813 /* Unhandled conditions / errors. */
814 ERR("Unknown notification channel status");
819 notification_condition
= lttng_notification_get_condition(notification
);
820 notification_evaluation
= lttng_notification_get_evaluation(notification
);
822 ret
= handle_condition(notification_condition
, notification_evaluation
,
823 handle
->notification_thread_handle
);
826 lttng_notification_destroy(notification
);
830 void *thread_rotation(void *data
)
833 struct rotation_thread_handle
*handle
= data
;
834 struct rotation_thread thread
;
837 DBG("[rotation-thread] Started rotation thread");
840 ERR("[rotation-thread] Invalid thread context provided");
844 queue_pipe_fd
= lttng_pipe_get_readfd(
845 handle
->rotation_timer_queue
->event_pipe
);
847 rcu_register_thread();
850 health_register(health_sessiond
, HEALTH_SESSIOND_TYPE_ROTATION
);
851 health_code_update();
853 ret
= init_thread_state(handle
, &thread
);
862 DBG("[rotation-thread] Entering poll wait");
863 ret
= lttng_poll_wait(&thread
.events
, -1);
864 DBG("[rotation-thread] Poll wait returned (%i)", ret
);
868 * Restart interrupted system call.
870 if (errno
== EINTR
) {
873 ERR("[rotation-thread] Error encountered during lttng_poll_wait (%i)", ret
);
878 for (i
= 0; i
< fd_count
; i
++) {
879 int fd
= LTTNG_POLL_GETFD(&thread
.events
, i
);
880 uint32_t revents
= LTTNG_POLL_GETEV(&thread
.events
, i
);
882 DBG("[rotation-thread] Handling fd (%i) activity (%u)",
885 if (revents
& LPOLLERR
) {
886 ERR("[rotation-thread] Polling returned an error on fd %i", fd
);
890 if (fd
== rotate_notification_channel
->socket
) {
891 ret
= handle_notification_channel(fd
, handle
,
894 ERR("[rotation-thread] Error occurred while handling activity on notification channel socket");
898 /* Job queue or quit pipe activity. */
901 * The job queue is serviced if there is
902 * activity on the quit pipe to ensure it is
903 * flushed and all references held in the queue
906 ret
= handle_job_queue(handle
, &thread
,
907 handle
->rotation_timer_queue
);
909 ERR("[rotation-thread] Failed to handle rotation timer pipe event");
913 if (fd
== queue_pipe_fd
) {
916 ret
= lttng_read(fd
, &buf
, 1);
918 ERR("[rotation-thread] Failed to read from wakeup pipe (fd = %i)", fd
);
922 DBG("[rotation-thread] Quit pipe activity");
930 DBG("[rotation-thread] Exit");
931 fini_thread_state(&thread
);
932 health_unregister(health_sessiond
);
933 rcu_thread_offline();
934 rcu_unregister_thread();
940 bool shutdown_rotation_thread(void *thread_data
)
942 struct rotation_thread_handle
*handle
= thread_data
;
943 const int write_fd
= lttng_pipe_get_writefd(handle
->quit_pipe
);
945 return notify_thread_pipe(write_fd
) == 1;
948 bool launch_rotation_thread(struct rotation_thread_handle
*handle
)
950 struct lttng_thread
*thread
;
952 thread
= lttng_thread_create("Rotation",
954 shutdown_rotation_thread
,
960 lttng_thread_put(thread
);