Fix: file-descriptor: missing include guards
[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.hpp"
9
10 #include <common/defaults.hpp>
11 #include <common/error.hpp>
12 #include <common/macros.hpp>
13
14 #include <pthread.h>
15 #include <urcu/list.h>
16 #include <urcu/ref.h>
17
18 namespace {
19 struct thread_list {
20 struct cds_list_head head;
21 pthread_mutex_t lock;
22 } thread_list = {
23 .head = CDS_LIST_HEAD_INIT(thread_list.head),
24 .lock = PTHREAD_MUTEX_INITIALIZER,
25 };
26 } /* namespace */
27
28 struct lttng_thread {
29 struct urcu_ref ref;
30 struct cds_list_head node;
31 pthread_t thread;
32 const char *name;
33 /* Main thread function */
34 lttng_thread_entry_point entry;
35 /*
36 * Thread-specific shutdown method. Allows threads to implement their
37 * own shutdown mechanism as some of them use a structured message
38 * passed through a command queue and some rely on a dedicated "quit"
39 * pipe.
40 */
41 lttng_thread_shutdown_cb shutdown;
42 lttng_thread_cleanup_cb cleanup;
43 /* Thread implementation-specific data. */
44 void *data;
45 };
46
47 static void lttng_thread_destroy(struct lttng_thread *thread)
48 {
49 if (thread->cleanup) {
50 thread->cleanup(thread->data);
51 }
52 free(thread);
53 }
54
55 static void lttng_thread_release(struct urcu_ref *ref)
56 {
57 lttng_thread_destroy(lttng::utils::container_of(ref, &lttng_thread::ref));
58 }
59
60 static void *launch_thread(void *data)
61 {
62 void *ret;
63 struct lttng_thread *thread = (struct lttng_thread *) data;
64
65 logger_set_thread_name(thread->name, true);
66 DBG("Entering thread entry point");
67 ret = thread->entry(thread->data);
68 DBG("Thread entry point has returned");
69 return ret;
70 }
71
72 struct lttng_thread *lttng_thread_create(const char *name,
73 lttng_thread_entry_point entry,
74 lttng_thread_shutdown_cb shutdown,
75 lttng_thread_cleanup_cb cleanup,
76 void *thread_data)
77 {
78 int ret;
79 struct lttng_thread *thread;
80
81 thread = zmalloc<lttng_thread>();
82 if (!thread) {
83 goto error_alloc;
84 }
85
86 urcu_ref_init(&thread->ref);
87 CDS_INIT_LIST_HEAD(&thread->node);
88 /*
89 * Thread names are assumed to be statically allocated strings.
90 * It is unnecessary to copy this attribute.
91 */
92 thread->name = name;
93 thread->entry = entry;
94 thread->shutdown = shutdown;
95 thread->cleanup = cleanup;
96 thread->data = thread_data;
97
98 pthread_mutex_lock(&thread_list.lock);
99 /*
100 * Add the thread at the head of the list to shutdown threads in the
101 * opposite order of their creation. A reference is taken for the
102 * thread list which will be released on shutdown of the thread.
103 */
104 cds_list_add(&thread->node, &thread_list.head);
105 (void) lttng_thread_get(thread);
106
107 ret = pthread_create(&thread->thread, default_pthread_attr(), 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 nullptr;
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 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 DBG("Joined thread \"%s\"", thread->name);
168 end:
169 return result;
170 }
171
172 bool lttng_thread_shutdown(struct lttng_thread *thread)
173 {
174 const bool result = _lttng_thread_shutdown(thread);
175
176 if (result) {
177 /* Release the list's reference to the thread. */
178 pthread_mutex_lock(&thread_list.lock);
179 cds_list_del(&thread->node);
180 lttng_thread_put(thread);
181 pthread_mutex_unlock(&thread_list.lock);
182 }
183 return result;
184 }
185
186 void lttng_thread_list_shutdown_orphans()
187 {
188 struct lttng_thread *thread, *tmp;
189
190 pthread_mutex_lock(&thread_list.lock);
191 cds_list_for_each_entry_safe (thread, tmp, &thread_list.head, node) {
192 bool result;
193 const long ref = uatomic_read(&thread->ref.refcount);
194
195 if (ref != 1) {
196 /*
197 * Other external references to the thread exist, skip.
198 */
199 continue;
200 }
201
202 result = _lttng_thread_shutdown(thread);
203 if (!result) {
204 ERR("Failed to shutdown thread \"%s\"", thread->name);
205 }
206 }
207 pthread_mutex_unlock(&thread_list.lock);
208 }
This page took 0.0338 seconds and 4 git commands to generate.