X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fthread.c;fp=src%2Fbin%2Flttng-sessiond%2Fthread.c;h=7e16b69ed9ca5ddb113875b87cb6fa7be64c48e0;hp=0000000000000000000000000000000000000000;hb=b878f05a6e62779ddf2364d5bb51821cb6d6d572;hpb=124473a3e6a82e10bbb23727fdff6e1f0c0415cb diff --git a/src/bin/lttng-sessiond/thread.c b/src/bin/lttng-sessiond/thread.c new file mode 100644 index 000000000..7e16b69ed --- /dev/null +++ b/src/bin/lttng-sessiond/thread.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2018 - Jérémie Galarneau + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2 only, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "thread.h" +#include +#include +#include +#include +#include +#include + +static struct thread_list { + struct cds_list_head head; + pthread_mutex_t lock; +} thread_list = { + .head = CDS_LIST_HEAD_INIT(thread_list.head), + .lock = PTHREAD_MUTEX_INITIALIZER, +}; + +struct lttng_thread { + struct urcu_ref ref; + struct cds_list_head node; + pthread_t thread; + const char *name; + /* Main thread function */ + lttng_thread_entry_point entry; + /* + * Thread-specific shutdown method. Allows threads to implement their + * own shutdown mechanism as some of them use a structured message + * passed through a command queue and some rely on a dedicated "quit" + * pipe. + */ + lttng_thread_shutdown_cb shutdown; + lttng_thread_cleanup_cb cleanup; + /* Thread implementation-specific data. */ + void *data; +}; + +static +void lttng_thread_destroy(struct lttng_thread *thread) +{ + if (thread->cleanup) { + thread->cleanup(thread->data); + } + free(thread); +} + +static +void lttng_thread_release(struct urcu_ref *ref) +{ + lttng_thread_destroy(container_of(ref, struct lttng_thread, ref)); +} + +static +void *launch_thread(void *data) +{ + void *ret; + struct lttng_thread *thread = (struct lttng_thread *) data; + + DBG("Launching \"%s\" thread", thread->name); + ret = thread->entry(thread->data); + DBG("Thread \"%s\" has returned", thread->name); + return ret; +} + +struct lttng_thread *lttng_thread_create(const char *name, + lttng_thread_entry_point entry, + lttng_thread_shutdown_cb shutdown, + lttng_thread_cleanup_cb cleanup, + void *thread_data) +{ + int ret; + struct lttng_thread *thread; + + thread = zmalloc(sizeof(*thread)); + if (!thread) { + goto error; + } + + urcu_ref_init(&thread->ref); + CDS_INIT_LIST_HEAD(&thread->node); + /* + * Thread names are assumed to be statically allocated strings. + * It is unnecessary to copy this attribute. + */ + thread->name = name; + thread->entry = entry; + thread->shutdown = shutdown; + thread->cleanup = cleanup; + thread->data = thread_data; + + pthread_mutex_lock(&thread_list.lock); + /* + * Add the thread at the head of the list to shutdown threads in the + * opposite order of their creation. A reference is taken for the + * thread list which will be released on shutdown of the thread. + */ + cds_list_add(&thread->node, &thread_list.head); + (void) lttng_thread_get(thread); + + ret = pthread_create(&thread->thread, default_pthread_attr(), + launch_thread, thread); + if (ret) { + PERROR("Failed to create \"%s\" thread", thread->name); + goto error_pthread_create; + } + + pthread_mutex_unlock(&thread_list.lock); + return thread; + +error_pthread_create: + cds_list_del(&thread->node); + /* Release list reference. */ + lttng_thread_put(thread); + pthread_mutex_unlock(&thread_list.lock); +error: + /* Release initial reference. */ + lttng_thread_put(thread); + return NULL; +} + +bool lttng_thread_get(struct lttng_thread *thread) +{ + return urcu_ref_get_unless_zero(&thread->ref); +} + +void lttng_thread_put(struct lttng_thread *thread) +{ + assert(thread->ref.refcount); + urcu_ref_put(&thread->ref, lttng_thread_release); +} + +const char *lttng_thread_get_name(const struct lttng_thread *thread) +{ + return thread->name; +} + +static +bool _lttng_thread_shutdown(struct lttng_thread *thread) +{ + int ret; + void *status; + bool result = true; + + DBG("Shutting down \"%s\" thread", thread->name); + if (thread->shutdown) { + result = thread->shutdown(thread->data); + if (!result) { + result = false; + goto end; + } + } + + ret = pthread_join(thread->thread, &status); + if (ret) { + PERROR("Failed to join \"%s\" thread", thread->name); + result = false; + } + /* Release the list's reference to the thread. */ + cds_list_del(&thread->node); + lttng_thread_put(thread); +end: + return result; +} + +bool lttng_thread_shutdown(struct lttng_thread *thread) +{ + bool result; + + pthread_mutex_lock(&thread_list.lock); + result = _lttng_thread_shutdown(thread); + pthread_mutex_unlock(&thread_list.lock); + return result; +} + +void lttng_thread_list_shutdown_orphans(void) +{ + struct lttng_thread *thread, *tmp; + + pthread_mutex_lock(&thread_list.lock); + cds_list_for_each_entry_safe(thread, tmp, &thread_list.head, node) { + bool result; + const long ref = uatomic_read(&thread->ref.refcount); + + if (ref != 1) { + /* + * Other external references to the thread exist, skip. + */ + continue; + } + + result = _lttng_thread_shutdown(thread); + if (!result) { + ERR("Failed to shutdown thread \"%s\"", thread->name); + } + } + pthread_mutex_unlock(&thread_list.lock); +}