1/***
2  This file is part of PulseAudio.
3
4  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
5
6  PulseAudio is free software; you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as published
8  by the Free Software Foundation; either version 2.1 of the License,
9  or (at your option) any later version.
10
11  PulseAudio is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  General Public License for more details.
15
16  You should have received a copy of the GNU Lesser General Public License
17  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18***/
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <stdio.h>
25
26#include <windows.h>
27
28#include <pulse/xmalloc.h>
29#include <pulsecore/once.h>
30
31#include "thread.h"
32
33struct pa_thread {
34    HANDLE thread;
35    pa_thread_func_t thread_func;
36    void *userdata;
37};
38
39struct pa_tls {
40    DWORD index;
41    pa_free_cb_t free_func;
42};
43
44struct pa_tls_monitor {
45    HANDLE thread;
46    pa_free_cb_t free_func;
47    void *data;
48};
49
50static pa_tls *thread_tls;
51static pa_once thread_tls_once = PA_ONCE_INIT;
52static pa_tls *monitor_tls;
53
54static void thread_tls_once_func(void) {
55    thread_tls = pa_tls_new(NULL);
56    assert(thread_tls);
57}
58
59static DWORD WINAPI internal_thread_func(LPVOID param) {
60    pa_thread *t = param;
61    assert(t);
62
63    pa_run_once(&thread_tls_once, thread_tls_once_func);
64    pa_tls_set(thread_tls, t);
65
66    t->thread_func(t->userdata);
67
68    return 0;
69}
70
71pa_thread* pa_thread_new(const char *name, pa_thread_func_t thread_func, void *userdata) {
72    pa_thread *t;
73    DWORD thread_id;
74
75    assert(thread_func);
76
77    t = pa_xnew(pa_thread, 1);
78    t->thread_func = thread_func;
79    t->userdata = userdata;
80
81    t->thread = CreateThread(NULL, 0, internal_thread_func, t, 0, &thread_id);
82
83    if (!t->thread) {
84        pa_xfree(t);
85        return NULL;
86    }
87
88    return t;
89}
90
91int pa_thread_is_running(pa_thread *t) {
92    DWORD code;
93
94    assert(t);
95
96    if (!GetExitCodeThread(t->thread, &code))
97        return 0;
98
99    return code == STILL_ACTIVE;
100}
101
102void pa_thread_free(pa_thread *t) {
103    assert(t);
104
105    pa_thread_join(t);
106    CloseHandle(t->thread);
107    pa_xfree(t);
108}
109
110void pa_thread_free_nojoin(pa_thread *t) {
111    pa_assert(t);
112
113    CloseHandle(t->thread);
114    pa_xfree(t);
115}
116
117int pa_thread_join(pa_thread *t) {
118    assert(t);
119
120    if (WaitForSingleObject(t->thread, INFINITE) == WAIT_FAILED)
121        return -1;
122
123    return 0;
124}
125
126pa_thread* pa_thread_self(void) {
127    pa_run_once(&thread_tls_once, thread_tls_once_func);
128    return pa_tls_get(thread_tls);
129}
130
131void* pa_thread_get_data(pa_thread *t) {
132    pa_assert(t);
133
134    return t->userdata;
135}
136
137void pa_thread_set_data(pa_thread *t, void *userdata) {
138    pa_assert(t);
139
140    t->userdata = userdata;
141}
142
143void pa_thread_set_name(pa_thread *t, const char *name) {
144    /* Not implemented */
145}
146
147const char *pa_thread_get_name(pa_thread *t) {
148    /* Not implemented */
149    return NULL;
150}
151
152void pa_thread_yield(void) {
153    Sleep(0);
154}
155
156static DWORD WINAPI monitor_thread_func(LPVOID param) {
157    struct pa_tls_monitor *m = param;
158    assert(m);
159
160    WaitForSingleObject(m->thread, INFINITE);
161
162    CloseHandle(m->thread);
163
164    m->free_func(m->data);
165
166    pa_xfree(m);
167
168    return 0;
169}
170
171pa_tls* pa_tls_new(pa_free_cb_t free_cb) {
172    pa_tls *t;
173
174    t = pa_xnew(pa_tls, 1);
175    t->index = TlsAlloc();
176    t->free_func = free_cb;
177
178    if (t->index == TLS_OUT_OF_INDEXES) {
179        pa_xfree(t);
180        return NULL;
181    }
182
183    return t;
184}
185
186void pa_tls_free(pa_tls *t) {
187    assert(t);
188
189    TlsFree(t->index);
190    pa_xfree(t);
191}
192
193void *pa_tls_get(pa_tls *t) {
194    assert(t);
195
196    return TlsGetValue(t->index);
197}
198
199void *pa_tls_set(pa_tls *t, void *userdata) {
200    void *r;
201
202    assert(t);
203
204    r = TlsGetValue(t->index);
205
206    TlsSetValue(t->index, userdata);
207
208    if (t->free_func) {
209        struct pa_tls_monitor *m;
210
211        PA_ONCE_BEGIN {
212            monitor_tls = pa_tls_new(NULL);
213            assert(monitor_tls);
214            pa_tls_set(monitor_tls, NULL);
215        } PA_ONCE_END;
216
217        m = pa_tls_get(monitor_tls);
218        if (!m) {
219            HANDLE thread;
220
221            m = pa_xnew(struct pa_tls_monitor, 1);
222
223            DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
224                GetCurrentProcess(), &m->thread, 0, FALSE,
225                DUPLICATE_SAME_ACCESS);
226
227            m->free_func = t->free_func;
228
229            pa_tls_set(monitor_tls, m);
230
231            thread = CreateThread(NULL, 0, monitor_thread_func, m, 0, NULL);
232            assert(thread);
233            CloseHandle(thread);
234        }
235
236        m->data = userdata;
237    }
238
239    return r;
240}
241