Move to kernel style SPDX license identifiers
[lttng-tools.git] / src / bin / lttng-sessiond / thread.c
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 DBG("Launching \"%s\" thread", thread->name);
65 ret = thread->entry(thread->data);
66 DBG("Thread \"%s\" has returned", thread->name);
67 return ret;
68 }
69
70 struct lttng_thread *lttng_thread_create(const char *name,
71 lttng_thread_entry_point entry,
72 lttng_thread_shutdown_cb shutdown,
73 lttng_thread_cleanup_cb cleanup,
74 void *thread_data)
75 {
76 int ret;
77 struct lttng_thread *thread;
78
79 thread = zmalloc(sizeof(*thread));
80 if (!thread) {
81 goto error_alloc;
82 }
83
84 urcu_ref_init(&thread->ref);
85 CDS_INIT_LIST_HEAD(&thread->node);
86 /*
87 * Thread names are assumed to be statically allocated strings.
88 * It is unnecessary to copy this attribute.
89 */
90 thread->name = name;
91 thread->entry = entry;
92 thread->shutdown = shutdown;
93 thread->cleanup = cleanup;
94 thread->data = thread_data;
95
96 pthread_mutex_lock(&thread_list.lock);
97 /*
98 * Add the thread at the head of the list to shutdown threads in the
99 * opposite order of their creation. A reference is taken for the
100 * thread list which will be released on shutdown of the thread.
101 */
102 cds_list_add(&thread->node, &thread_list.head);
103 (void) lttng_thread_get(thread);
104
105 ret = pthread_create(&thread->thread, default_pthread_attr(),
106 launch_thread, thread);
107 if (ret) {
108 PERROR("Failed to create \"%s\" thread", thread->name);
109 goto error_pthread_create;
110 }
111
112 pthread_mutex_unlock(&thread_list.lock);
113 return thread;
114
115 error_pthread_create:
116 cds_list_del(&thread->node);
117 /* Release list reference. */
118 lttng_thread_put(thread);
119 pthread_mutex_unlock(&thread_list.lock);
120 /* Release initial reference. */
121 lttng_thread_put(thread);
122 error_alloc:
123 return NULL;
124 }
125
126 bool lttng_thread_get(struct lttng_thread *thread)
127 {
128 return urcu_ref_get_unless_zero(&thread->ref);
129 }
130
131 void lttng_thread_put(struct lttng_thread *thread)
132 {
133 if (!thread) {
134 return;
135 }
136 assert(thread->ref.refcount);
137 urcu_ref_put(&thread->ref, lttng_thread_release);
138 }
139
140 const char *lttng_thread_get_name(const struct lttng_thread *thread)
141 {
142 return thread->name;
143 }
144
145 static
146 bool _lttng_thread_shutdown(struct lttng_thread *thread)
147 {
148 int ret;
149 void *status;
150 bool result = true;
151
152 DBG("Shutting down \"%s\" thread", thread->name);
153 if (thread->shutdown) {
154 result = thread->shutdown(thread->data);
155 if (!result) {
156 result = false;
157 goto end;
158 }
159 }
160
161 ret = pthread_join(thread->thread, &status);
162 if (ret) {
163 PERROR("Failed to join \"%s\" thread", thread->name);
164 result = false;
165 goto end;
166 }
167 /* Release the list's reference to the thread. */
168 cds_list_del(&thread->node);
169 lttng_thread_put(thread);
170 end:
171 return result;
172 }
173
174 bool lttng_thread_shutdown(struct lttng_thread *thread)
175 {
176 bool result;
177
178 pthread_mutex_lock(&thread_list.lock);
179 result = _lttng_thread_shutdown(thread);
180 pthread_mutex_unlock(&thread_list.lock);
181 return result;
182 }
183
184 void lttng_thread_list_shutdown_orphans(void)
185 {
186 struct lttng_thread *thread, *tmp;
187
188 pthread_mutex_lock(&thread_list.lock);
189 cds_list_for_each_entry_safe(thread, tmp, &thread_list.head, node) {
190 bool result;
191 const long ref = uatomic_read(&thread->ref.refcount);
192
193 if (ref != 1) {
194 /*
195 * Other external references to the thread exist, skip.
196 */
197 continue;
198 }
199
200 result = _lttng_thread_shutdown(thread);
201 if (!result) {
202 ERR("Failed to shutdown thread \"%s\"", thread->name);
203 }
204 }
205 pthread_mutex_unlock(&thread_list.lock);
206 }
This page took 0.051257 seconds and 6 git commands to generate.