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