153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2006 Lennart Poettering
553a5a1b3Sopenharmony_ci  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
653a5a1b3Sopenharmony_ci
753a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
853a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
953a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
1053a5a1b3Sopenharmony_ci  or (at your option) any later version.
1153a5a1b3Sopenharmony_ci
1253a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1353a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1453a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1553a5a1b3Sopenharmony_ci  General Public License for more details.
1653a5a1b3Sopenharmony_ci
1753a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1853a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1953a5a1b3Sopenharmony_ci***/
2053a5a1b3Sopenharmony_ci
2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2253a5a1b3Sopenharmony_ci#include <config.h>
2353a5a1b3Sopenharmony_ci#endif
2453a5a1b3Sopenharmony_ci
2553a5a1b3Sopenharmony_ci#include <pthread.h>
2653a5a1b3Sopenharmony_ci#include <sched.h>
2753a5a1b3Sopenharmony_ci#include <errno.h>
2853a5a1b3Sopenharmony_ci
2953a5a1b3Sopenharmony_ci#ifdef __linux__
3053a5a1b3Sopenharmony_ci#include <sys/prctl.h>
3153a5a1b3Sopenharmony_ci#endif
3253a5a1b3Sopenharmony_ci
3353a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
3453a5a1b3Sopenharmony_ci#include <pulsecore/atomic.h>
3553a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
3653a5a1b3Sopenharmony_ci
3753a5a1b3Sopenharmony_ci#include "thread.h"
3853a5a1b3Sopenharmony_ci
3953a5a1b3Sopenharmony_cistruct pa_thread {
4053a5a1b3Sopenharmony_ci    pthread_t id;
4153a5a1b3Sopenharmony_ci    pa_thread_func_t thread_func;
4253a5a1b3Sopenharmony_ci    void *userdata;
4353a5a1b3Sopenharmony_ci    pa_atomic_t running;
4453a5a1b3Sopenharmony_ci    bool joined;
4553a5a1b3Sopenharmony_ci    char *name;
4653a5a1b3Sopenharmony_ci};
4753a5a1b3Sopenharmony_ci
4853a5a1b3Sopenharmony_cistruct pa_tls {
4953a5a1b3Sopenharmony_ci    pthread_key_t key;
5053a5a1b3Sopenharmony_ci};
5153a5a1b3Sopenharmony_ci
5253a5a1b3Sopenharmony_cistatic void thread_free_cb(void *p) {
5353a5a1b3Sopenharmony_ci    pa_thread *t = p;
5453a5a1b3Sopenharmony_ci
5553a5a1b3Sopenharmony_ci    pa_assert(t);
5653a5a1b3Sopenharmony_ci
5753a5a1b3Sopenharmony_ci    if (!t->thread_func) {
5853a5a1b3Sopenharmony_ci        /* This is a foreign thread, we need to free the struct */
5953a5a1b3Sopenharmony_ci        pa_xfree(t->name);
6053a5a1b3Sopenharmony_ci        pa_xfree(t);
6153a5a1b3Sopenharmony_ci    }
6253a5a1b3Sopenharmony_ci}
6353a5a1b3Sopenharmony_ci
6453a5a1b3Sopenharmony_ciPA_STATIC_TLS_DECLARE(current_thread, thread_free_cb);
6553a5a1b3Sopenharmony_ci
6653a5a1b3Sopenharmony_cistatic void* internal_thread_func(void *userdata) {
6753a5a1b3Sopenharmony_ci    pa_thread *t = userdata;
6853a5a1b3Sopenharmony_ci    pa_assert(t);
6953a5a1b3Sopenharmony_ci
7053a5a1b3Sopenharmony_ci#ifdef __linux__
7153a5a1b3Sopenharmony_ci    prctl(PR_SET_NAME, t->name);
7253a5a1b3Sopenharmony_ci#elif defined(HAVE_PTHREAD_SETNAME_NP) && defined(OS_IS_DARWIN)
7353a5a1b3Sopenharmony_ci    pthread_setname_np(t->name);
7453a5a1b3Sopenharmony_ci#endif
7553a5a1b3Sopenharmony_ci
7653a5a1b3Sopenharmony_ci    t->id = pthread_self();
7753a5a1b3Sopenharmony_ci
7853a5a1b3Sopenharmony_ci    PA_STATIC_TLS_SET(current_thread, t);
7953a5a1b3Sopenharmony_ci
8053a5a1b3Sopenharmony_ci    pa_atomic_inc(&t->running);
8153a5a1b3Sopenharmony_ci    t->thread_func(t->userdata);
8253a5a1b3Sopenharmony_ci    pa_atomic_sub(&t->running, 2);
8353a5a1b3Sopenharmony_ci
8453a5a1b3Sopenharmony_ci    return NULL;
8553a5a1b3Sopenharmony_ci}
8653a5a1b3Sopenharmony_ci
8753a5a1b3Sopenharmony_cipa_thread* pa_thread_new(const char *name, pa_thread_func_t thread_func, void *userdata) {
8853a5a1b3Sopenharmony_ci    pa_thread *t;
8953a5a1b3Sopenharmony_ci
9053a5a1b3Sopenharmony_ci    pa_assert(thread_func);
9153a5a1b3Sopenharmony_ci
9253a5a1b3Sopenharmony_ci    t = pa_xnew0(pa_thread, 1);
9353a5a1b3Sopenharmony_ci    t->name = pa_xstrdup(name);
9453a5a1b3Sopenharmony_ci    t->thread_func = thread_func;
9553a5a1b3Sopenharmony_ci    t->userdata = userdata;
9653a5a1b3Sopenharmony_ci
9753a5a1b3Sopenharmony_ci    if (pthread_create(&t->id, NULL, internal_thread_func, t) < 0) {
9853a5a1b3Sopenharmony_ci        pa_xfree(t);
9953a5a1b3Sopenharmony_ci        return NULL;
10053a5a1b3Sopenharmony_ci    }
10153a5a1b3Sopenharmony_ci
10253a5a1b3Sopenharmony_ci    pa_atomic_inc(&t->running);
10353a5a1b3Sopenharmony_ci
10453a5a1b3Sopenharmony_ci    return t;
10553a5a1b3Sopenharmony_ci}
10653a5a1b3Sopenharmony_ci
10753a5a1b3Sopenharmony_ciint pa_thread_is_running(pa_thread *t) {
10853a5a1b3Sopenharmony_ci    pa_assert(t);
10953a5a1b3Sopenharmony_ci
11053a5a1b3Sopenharmony_ci    /* Unfortunately there is no way to tell whether a "foreign"
11153a5a1b3Sopenharmony_ci     * thread is still running. See
11253a5a1b3Sopenharmony_ci     * http://udrepper.livejournal.com/16844.html for more
11353a5a1b3Sopenharmony_ci     * information */
11453a5a1b3Sopenharmony_ci    pa_assert(t->thread_func);
11553a5a1b3Sopenharmony_ci
11653a5a1b3Sopenharmony_ci    return pa_atomic_load(&t->running) > 0;
11753a5a1b3Sopenharmony_ci}
11853a5a1b3Sopenharmony_ci
11953a5a1b3Sopenharmony_civoid pa_thread_free(pa_thread *t) {
12053a5a1b3Sopenharmony_ci    pa_assert(t);
12153a5a1b3Sopenharmony_ci
12253a5a1b3Sopenharmony_ci    pa_thread_join(t);
12353a5a1b3Sopenharmony_ci
12453a5a1b3Sopenharmony_ci    pa_xfree(t->name);
12553a5a1b3Sopenharmony_ci    pa_xfree(t);
12653a5a1b3Sopenharmony_ci}
12753a5a1b3Sopenharmony_ci
12853a5a1b3Sopenharmony_civoid pa_thread_free_nojoin(pa_thread *t) {
12953a5a1b3Sopenharmony_ci    pa_assert(t);
13053a5a1b3Sopenharmony_ci
13153a5a1b3Sopenharmony_ci    pa_xfree(t->name);
13253a5a1b3Sopenharmony_ci    pa_xfree(t);
13353a5a1b3Sopenharmony_ci}
13453a5a1b3Sopenharmony_ci
13553a5a1b3Sopenharmony_ciint pa_thread_join(pa_thread *t) {
13653a5a1b3Sopenharmony_ci    pa_assert(t);
13753a5a1b3Sopenharmony_ci    pa_assert(t->thread_func);
13853a5a1b3Sopenharmony_ci
13953a5a1b3Sopenharmony_ci    if (t->joined)
14053a5a1b3Sopenharmony_ci        return -1;
14153a5a1b3Sopenharmony_ci
14253a5a1b3Sopenharmony_ci    t->joined = true;
14353a5a1b3Sopenharmony_ci    return pthread_join(t->id, NULL);
14453a5a1b3Sopenharmony_ci}
14553a5a1b3Sopenharmony_ci
14653a5a1b3Sopenharmony_cipa_thread* pa_thread_self(void) {
14753a5a1b3Sopenharmony_ci    pa_thread *t;
14853a5a1b3Sopenharmony_ci
14953a5a1b3Sopenharmony_ci    if ((t = PA_STATIC_TLS_GET(current_thread)))
15053a5a1b3Sopenharmony_ci        return t;
15153a5a1b3Sopenharmony_ci
15253a5a1b3Sopenharmony_ci    /* This is a foreign thread, let's create a pthread structure to
15353a5a1b3Sopenharmony_ci     * make sure that we can always return a sensible pointer */
15453a5a1b3Sopenharmony_ci
15553a5a1b3Sopenharmony_ci    t = pa_xnew0(pa_thread, 1);
15653a5a1b3Sopenharmony_ci    t->id = pthread_self();
15753a5a1b3Sopenharmony_ci    t->joined = true;
15853a5a1b3Sopenharmony_ci    pa_atomic_store(&t->running, 2);
15953a5a1b3Sopenharmony_ci
16053a5a1b3Sopenharmony_ci    PA_STATIC_TLS_SET(current_thread, t);
16153a5a1b3Sopenharmony_ci
16253a5a1b3Sopenharmony_ci    return t;
16353a5a1b3Sopenharmony_ci}
16453a5a1b3Sopenharmony_ci
16553a5a1b3Sopenharmony_civoid* pa_thread_get_data(pa_thread *t) {
16653a5a1b3Sopenharmony_ci    pa_assert(t);
16753a5a1b3Sopenharmony_ci
16853a5a1b3Sopenharmony_ci    return t->userdata;
16953a5a1b3Sopenharmony_ci}
17053a5a1b3Sopenharmony_ci
17153a5a1b3Sopenharmony_civoid pa_thread_set_data(pa_thread *t, void *userdata) {
17253a5a1b3Sopenharmony_ci    pa_assert(t);
17353a5a1b3Sopenharmony_ci
17453a5a1b3Sopenharmony_ci    t->userdata = userdata;
17553a5a1b3Sopenharmony_ci}
17653a5a1b3Sopenharmony_ci
17753a5a1b3Sopenharmony_civoid pa_thread_set_name(pa_thread *t, const char *name) {
17853a5a1b3Sopenharmony_ci    pa_assert(t);
17953a5a1b3Sopenharmony_ci
18053a5a1b3Sopenharmony_ci    pa_xfree(t->name);
18153a5a1b3Sopenharmony_ci    t->name = pa_xstrdup(name);
18253a5a1b3Sopenharmony_ci
18353a5a1b3Sopenharmony_ci#ifdef __linux__
18453a5a1b3Sopenharmony_ci    prctl(PR_SET_NAME, name);
18553a5a1b3Sopenharmony_ci#elif defined(HAVE_PTHREAD_SETNAME_NP) && defined(OS_IS_DARWIN)
18653a5a1b3Sopenharmony_ci    pthread_setname_np(name);
18753a5a1b3Sopenharmony_ci#endif
18853a5a1b3Sopenharmony_ci}
18953a5a1b3Sopenharmony_ci
19053a5a1b3Sopenharmony_ciconst char *pa_thread_get_name(pa_thread *t) {
19153a5a1b3Sopenharmony_ci    pa_assert(t);
19253a5a1b3Sopenharmony_ci
19353a5a1b3Sopenharmony_ci#ifdef __linux__
19453a5a1b3Sopenharmony_ci    if (!t->name) {
19553a5a1b3Sopenharmony_ci        t->name = pa_xmalloc(17);
19653a5a1b3Sopenharmony_ci
19753a5a1b3Sopenharmony_ci        if (prctl(PR_GET_NAME, t->name) >= 0)
19853a5a1b3Sopenharmony_ci            t->name[16] = 0;
19953a5a1b3Sopenharmony_ci        else {
20053a5a1b3Sopenharmony_ci            pa_xfree(t->name);
20153a5a1b3Sopenharmony_ci            t->name = NULL;
20253a5a1b3Sopenharmony_ci        }
20353a5a1b3Sopenharmony_ci    }
20453a5a1b3Sopenharmony_ci#elif defined(HAVE_PTHREAD_GETNAME_NP) && defined(OS_IS_DARWIN)
20553a5a1b3Sopenharmony_ci    if (!t->name) {
20653a5a1b3Sopenharmony_ci        t->name = pa_xmalloc0(17);
20753a5a1b3Sopenharmony_ci        pthread_getname_np(t->id, t->name, 16);
20853a5a1b3Sopenharmony_ci    }
20953a5a1b3Sopenharmony_ci#endif
21053a5a1b3Sopenharmony_ci
21153a5a1b3Sopenharmony_ci    return t->name;
21253a5a1b3Sopenharmony_ci}
21353a5a1b3Sopenharmony_ci
21453a5a1b3Sopenharmony_civoid pa_thread_yield(void) {
21553a5a1b3Sopenharmony_ci#ifdef HAVE_PTHREAD_YIELD
21653a5a1b3Sopenharmony_ci    pthread_yield();
21753a5a1b3Sopenharmony_ci#else
21853a5a1b3Sopenharmony_ci    pa_assert_se(sched_yield() == 0);
21953a5a1b3Sopenharmony_ci#endif
22053a5a1b3Sopenharmony_ci}
22153a5a1b3Sopenharmony_ci
22253a5a1b3Sopenharmony_cipa_tls* pa_tls_new(pa_free_cb_t free_cb) {
22353a5a1b3Sopenharmony_ci    pa_tls *t;
22453a5a1b3Sopenharmony_ci
22553a5a1b3Sopenharmony_ci    t = pa_xnew(pa_tls, 1);
22653a5a1b3Sopenharmony_ci
22753a5a1b3Sopenharmony_ci    if (pthread_key_create(&t->key, free_cb) < 0) {
22853a5a1b3Sopenharmony_ci        pa_xfree(t);
22953a5a1b3Sopenharmony_ci        return NULL;
23053a5a1b3Sopenharmony_ci    }
23153a5a1b3Sopenharmony_ci
23253a5a1b3Sopenharmony_ci    return t;
23353a5a1b3Sopenharmony_ci}
23453a5a1b3Sopenharmony_ci
23553a5a1b3Sopenharmony_civoid pa_tls_free(pa_tls *t) {
23653a5a1b3Sopenharmony_ci    pa_assert(t);
23753a5a1b3Sopenharmony_ci
23853a5a1b3Sopenharmony_ci    pa_assert_se(pthread_key_delete(t->key) == 0);
23953a5a1b3Sopenharmony_ci    pa_xfree(t);
24053a5a1b3Sopenharmony_ci}
24153a5a1b3Sopenharmony_ci
24253a5a1b3Sopenharmony_civoid *pa_tls_get(pa_tls *t) {
24353a5a1b3Sopenharmony_ci    pa_assert(t);
24453a5a1b3Sopenharmony_ci
24553a5a1b3Sopenharmony_ci    return pthread_getspecific(t->key);
24653a5a1b3Sopenharmony_ci}
24753a5a1b3Sopenharmony_ci
24853a5a1b3Sopenharmony_civoid *pa_tls_set(pa_tls *t, void *userdata) {
24953a5a1b3Sopenharmony_ci    void *r;
25053a5a1b3Sopenharmony_ci
25153a5a1b3Sopenharmony_ci    r = pthread_getspecific(t->key);
25253a5a1b3Sopenharmony_ci    pa_assert_se(pthread_setspecific(t->key, userdata) == 0);
25353a5a1b3Sopenharmony_ci    return r;
25453a5a1b3Sopenharmony_ci}
255