Tests: add missing kernel test cases to make check target
[lttng-tools.git] / src / bin / lttng-sessiond / thread.cpp
1 /*
2 * Copyright (C) 2018 Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7
8 #include "thread.h"
9 #include <urcu/list.h>
10 #include <urcu/ref.h>
11 #include <pthread.h>
12 #include <common/macros.h>
13 #include <common/error.h>
14 #include <common/defaults.h>
15
16 static struct thread_list {
17 struct cds_list_head head;
18 pthread_mutex_t lock;
19 } thread_list = {
20 .head = CDS_LIST_HEAD_INIT(thread_list.head),
21 .lock = PTHREAD_MUTEX_INITIALIZER,
22 };
23
24 struct lttng_thread {
25 struct urcu_ref ref;
26 struct cds_list_head node;
27 pthread_t thread;
28 const char *name;
29 /* Main thread function */
30 lttng_thread_entry_point entry;
31 /*
32 * Thread-specific shutdown method. Allows threads to implement their
33 * own shutdown mechanism as some of them use a structured message
34 * passed through a command queue and some rely on a dedicated "quit"
35 * pipe.
36 */
37 lttng_thread_shutdown_cb shutdown;
38 lttng_thread_cleanup_cb cleanup;
39 /* Thread implementation-specific data. */
40 void *data;
41 };
42
43 static
44 void lttng_thread_destroy(struct lttng_thread *thread)
45 {
46 if (thread->cleanup) {
47 thread->cleanup(thread->data);
48 }
49 free(thread);
50 }
51
52 static
53 void lttng_thread_release(struct urcu_ref *ref)
54 {
55 lttng_thread_destroy(container_of(ref, struct lttng_thread, ref));
56 }
57
58 static
59 void *launch_thread(void *data)
60 {
61 void *ret;
62 struct lttng_thread *thread = (struct lttng_thread *) data;
63
64 logger_set_thread_name(thread->name, true);
65 DBG("Entering thread entry point");
66 ret = thread->entry(thread->data);
67 DBG("Thread entry point has returned");
68 return ret;
69 }
70
71 struct lttng_thread *lttng_thread_create(const char *name,
72 lttng_thread_entry_point entry,
73 lttng_thread_shutdown_cb shutdown,
74 lttng_thread_cleanup_cb cleanup,
75 void *thread_data)
76 {
77 int ret;
78 struct lttng_thread *thread;
79
80 thread = (lttng_thread *) zmalloc(sizeof(*thread));
81 if (!thread) {
82 goto error_alloc;
83 }
84
85 urcu_ref_init(&thread->ref);
86 CDS_INIT_LIST_HEAD(&thread->node);
87 /*
88 * Thread names are assumed to be statically allocated strings.
89 * It is unnecessary to copy this attribute.
90 */
91 thread->name = name;
92 thread->entry = entry;
93 thread->shutdown = shutdown;
94 thread->cleanup = cleanup;
95 thread->data = thread_data;
96
97 pthread_mutex_lock(&thread_list.lock);
98 /*
99 * Add the thread at the head of the list to shutdown threads in the
100 * opposite order of their creation. A reference is taken for the
101 * thread list which will be released on shutdown of the thread.
102 */
103 cds_list_add(&thread->node, &thread_list.head);
104 (void) lttng_thread_get(thread);
105
106 ret = pthread_create(&thread->thread, default_pthread_attr(),
107 launch_thread, thread);
108 if (ret) {
109 PERROR("Failed to create \"%s\" thread", thread->name);
110 goto error_pthread_create;
111 }
112
113 pthread_mutex_unlock(&thread_list.lock);
114 return thread;
115
116 error_pthread_create:
117 cds_list_del(&thread->node);
118 /* Release list reference. */
119 lttng_thread_put(thread);
120 pthread_mutex_unlock(&thread_list.lock);
121 /* Release initial reference. */
122 lttng_thread_put(thread);
123 error_alloc:
124 return NULL;
125 }
126
127 bool lttng_thread_get(struct lttng_thread *thread)
128 {
129 return urcu_ref_get_unless_zero(&thread->ref);
130 }
131
132 void lttng_thread_put(struct lttng_thread *thread)
133 {
134 if (!thread) {
135 return;
136 }
137 LTTNG_ASSERT(thread->ref.refcount);
138 urcu_ref_put(&thread->ref, lttng_thread_release);
139 }
140
141 const char *lttng_thread_get_name(const struct lttng_thread *thread)
142 {
143 return thread->name;
144 }
145
146 static
147 bool _lttng_thread_shutdown(struct lttng_thread *thread)
148 {
149 int ret;
150 void *status;
151 bool result = true;
152
153 DBG("Shutting down \"%s\" thread", thread->name);
154 if (thread->shutdown) {
155 result = thread->shutdown(thread->data);
156 if (!result) {
157 result = false;
158 goto end;
159 }
160 }
161
162 ret = pthread_join(thread->thread, &status);
163 if (ret) {
164 PERROR("Failed to join \"%s\" thread", thread->name);
165 result = false;
166 goto end;
167 }
168 DBG("Joined thread \"%s\"", thread->name);
169 end:
170 return result;
171 }
172
173 bool lttng_thread_shutdown(struct lttng_thread *thread)
174 {
175 const bool result = _lttng_thread_shutdown(thread);
176
177 if (result) {
178 /* Release the list's reference to the thread. */
179 pthread_mutex_lock(&thread_list.lock);
180 cds_list_del(&thread->node);
181 lttng_thread_put(thread);
182 pthread_mutex_unlock(&thread_list.lock);
183 }
184 return result;
185 }
186
187 void lttng_thread_list_shutdown_orphans(void)
188 {
189 struct lttng_thread *thread, *tmp;
190
191 pthread_mutex_lock(&thread_list.lock);
192 cds_list_for_each_entry_safe(thread, tmp, &thread_list.head, node) {
193 bool result;
194 const long ref = uatomic_read(&thread->ref.refcount);
195
196 if (ref != 1) {
197 /*
198 * Other external references to the thread exist, skip.
199 */
200 continue;
201 }
202
203 result = _lttng_thread_shutdown(thread);
204 if (!result) {
205 ERR("Failed to shutdown thread \"%s\"", thread->name);
206 }
207 }
208 pthread_mutex_unlock(&thread_list.lock);
209 }
This page took 0.034302 seconds and 4 git commands to generate.