Sessiond timer thread
[lttng-tools.git] / src / bin / lttng-sessiond / sessiond-timer.c
CommitLineData
d086f507
JD
1/*
2 * Copyright (C) 2017 - Julien Desfossez <jdesfossez@efficios.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License, version 2 only, as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program; if not, write to the Free Software Foundation, Inc., 51
15 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18#define _LGPL_SOURCE
19#include <assert.h>
20#include <inttypes.h>
21#include <signal.h>
22
23#include "sessiond-timer.h"
24#include "health-sessiond.h"
25#include "rotation-thread.h"
26
27static
28struct timer_signal_data timer_signal = {
29 .tid = 0,
30 .qs_done = 0,
31 .lock = PTHREAD_MUTEX_INITIALIZER,
32};
33
34/*
35 * Set custom signal mask to current thread.
36 */
37static
38void setmask(sigset_t *mask)
39{
40 int ret;
41
42 ret = sigemptyset(mask);
43 if (ret) {
44 PERROR("sigemptyset");
45 }
46 ret = sigaddset(mask, LTTNG_SESSIOND_SIG_TEARDOWN);
47 if (ret) {
48 PERROR("sigaddset teardown");
49 }
50 ret = sigaddset(mask, LTTNG_SESSIOND_SIG_EXIT);
51 if (ret) {
52 PERROR("sigaddset exit");
53 }
54}
55
56/*
57 * This is the same function as consumer_timer_signal_thread_qs, when it
58 * returns, it means that no timer signr is currently pending or being handled
59 * by the timer thread. This cannot be called from the timer thread.
60 */
61static
62void sessiond_timer_signal_thread_qs(unsigned int signr)
63{
64 sigset_t pending_set;
65 int ret;
66
67 /*
68 * We need to be the only thread interacting with the thread
69 * that manages signals for teardown synchronization.
70 */
71 pthread_mutex_lock(&timer_signal.lock);
72
73 /* Ensure we don't have any signal queued for this session. */
74 for (;;) {
75 ret = sigemptyset(&pending_set);
76 if (ret == -1) {
77 PERROR("sigemptyset");
78 }
79 ret = sigpending(&pending_set);
80 if (ret == -1) {
81 PERROR("sigpending");
82 }
83 if (!sigismember(&pending_set, signr)) {
84 break;
85 }
86 caa_cpu_relax();
87 }
88
89 /*
90 * From this point, no new signal handler will be fired that would try to
91 * access "session". However, we still need to wait for any currently
92 * executing handler to complete.
93 */
94 cmm_smp_mb();
95 CMM_STORE_SHARED(timer_signal.qs_done, 0);
96 cmm_smp_mb();
97
98 /*
99 * Kill with LTTNG_SESSIOND_SIG_TEARDOWN, so signal management thread
100 * wakes up.
101 */
102 kill(getpid(), LTTNG_SESSIOND_SIG_TEARDOWN);
103
104 while (!CMM_LOAD_SHARED(timer_signal.qs_done)) {
105 caa_cpu_relax();
106 }
107 cmm_smp_mb();
108
109 pthread_mutex_unlock(&timer_signal.lock);
110}
111
112/*
113 * Start a timer on a session that will fire at a given interval
114 * (timer_interval_us) and fire a given signal (signal).
115 *
116 * Returns a negative value on error, 0 if a timer was created, and
117 * a positive value if no timer was created (not an error).
118 */
119static
120int session_timer_start(timer_t *timer_id, struct ltt_session *session,
121 unsigned int timer_interval_us, int signal, bool one_shot)
122{
123 int ret = 0, delete_ret;
124 struct sigevent sev;
125 struct itimerspec its;
126
127 assert(session);
128
129 sev.sigev_notify = SIGEV_SIGNAL;
130 sev.sigev_signo = signal;
131 sev.sigev_value.sival_ptr = session;
132 ret = timer_create(CLOCKID, &sev, timer_id);
133 if (ret == -1) {
134 PERROR("timer_create");
135 goto end;
136 }
137
138 its.it_value.tv_sec = timer_interval_us / 1000000;
139 its.it_value.tv_nsec = (timer_interval_us % 1000000) * 1000;
140 if (one_shot) {
141 its.it_interval.tv_sec = 0;
142 its.it_interval.tv_nsec = 0;
143 } else {
144 its.it_interval.tv_sec = its.it_value.tv_sec;
145 its.it_interval.tv_nsec = its.it_value.tv_nsec;
146 }
147
148 ret = timer_settime(*timer_id, 0, &its, NULL);
149 if (ret == -1) {
150 PERROR("timer_settime");
151 goto error_destroy_timer;
152 }
153 goto end;
154
155error_destroy_timer:
156 delete_ret = timer_delete(*timer_id);
157 if (delete_ret == -1) {
158 PERROR("timer_delete");
159 }
160
161end:
162 return ret;
163}
164
165static
166int session_timer_stop(timer_t *timer_id, int signal)
167{
168 int ret = 0;
169
170 ret = timer_delete(*timer_id);
171 if (ret == -1) {
172 PERROR("timer_delete");
173 goto end;
174 }
175
176 sessiond_timer_signal_thread_qs(signal);
177 *timer_id = 0;
178end:
179 return ret;
180}
181
182/*
183 * Block the RT signals for the entire process. It must be called from the
184 * sessiond main before creating the threads
185 */
186int sessiond_timer_signal_init(void)
187{
188 int ret;
189 sigset_t mask;
190
191 /* Block signal for entire process, so only our thread processes it. */
192 setmask(&mask);
193 ret = pthread_sigmask(SIG_BLOCK, &mask, NULL);
194 if (ret) {
195 errno = ret;
196 PERROR("pthread_sigmask");
197 return -1;
198 }
199 return 0;
200}
201
202/*
203 * This thread is the sighandler for the timer signals.
204 */
205void *sessiond_timer_thread(void *data)
206{
207 int signr;
208 sigset_t mask;
209 siginfo_t info;
210 struct timer_thread_parameters *ctx = data;
211
212 rcu_register_thread();
213 rcu_thread_online();
214
215 health_register(health_sessiond, HEALTH_SESSIOND_TYPE_TIMER);
216
217 health_code_update();
218
219 /* Only self thread will receive signal mask. */
220 setmask(&mask);
221 CMM_STORE_SHARED(timer_signal.tid, pthread_self());
222
223 while (1) {
224 health_code_update();
225
226 health_poll_entry();
227 signr = sigwaitinfo(&mask, &info);
228 health_poll_exit();
229
230 /*
231 * NOTE: cascading conditions are used instead of a switch case
232 * since the use of SIGRTMIN in the definition of the signals'
233 * values prevents the reduction to an integer constant.
234 */
235 if (signr == -1) {
236 if (errno != EINTR) {
237 PERROR("sigwaitinfo");
238 }
239 continue;
240 } else if (signr == LTTNG_SESSIOND_SIG_TEARDOWN) {
241 cmm_smp_mb();
242 CMM_STORE_SHARED(timer_signal.qs_done, 1);
243 cmm_smp_mb();
244 DBG("Signal timer metadata thread teardown");
245 } else if (signr == LTTNG_SESSIOND_SIG_EXIT) {
246 goto end;
247 } else {
248 ERR("Unexpected signal %d\n", info.si_signo);
249 }
250 }
251
252end:
253 DBG("[timer-thread] Exit");
254 health_unregister(health_sessiond);
255 rcu_thread_offline();
256 rcu_unregister_thread();
257 return NULL;
258}
This page took 0.033566 seconds and 5 git commands to generate.