1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "apr.h"
18#include "apr_portable.h"
19#include "apr_arch_threadproc.h"
20
21#if APR_HAS_THREADS
22
23#if APR_HAVE_PTHREAD_H
24
25/* Destroy the threadattr object */
26static apr_status_t threadattr_cleanup(void *data)
27{
28    apr_threadattr_t *attr = data;
29    apr_status_t rv;
30
31    rv = pthread_attr_destroy(&attr->attr);
32#ifdef HAVE_ZOS_PTHREADS
33    if (rv) {
34        rv = errno;
35    }
36#endif
37    return rv;
38}
39
40APR_DECLARE(apr_status_t) apr_threadattr_create(apr_threadattr_t **new,
41                                                apr_pool_t *pool)
42{
43    apr_status_t stat;
44
45    (*new) = apr_palloc(pool, sizeof(apr_threadattr_t));
46    (*new)->pool = pool;
47    stat = pthread_attr_init(&(*new)->attr);
48
49    if (stat == 0) {
50        apr_pool_cleanup_register(pool, *new, threadattr_cleanup,
51                                  apr_pool_cleanup_null);
52        return APR_SUCCESS;
53    }
54#ifdef HAVE_ZOS_PTHREADS
55    stat = errno;
56#endif
57
58    return stat;
59}
60
61#if defined(PTHREAD_CREATE_DETACHED)
62#define DETACH_ARG(v) ((v) ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE)
63#else
64#define DETACH_ARG(v) ((v) ? 1 : 0)
65#endif
66
67APR_DECLARE(apr_status_t) apr_threadattr_detach_set(apr_threadattr_t *attr,
68                                                    apr_int32_t on)
69{
70    apr_status_t stat;
71#ifdef HAVE_ZOS_PTHREADS
72    int arg = DETACH_ARG(on);
73
74    if ((stat = pthread_attr_setdetachstate(&attr->attr, &arg)) == 0) {
75#else
76    if ((stat = pthread_attr_setdetachstate(&attr->attr,
77                                            DETACH_ARG(on))) == 0) {
78#endif
79        return APR_SUCCESS;
80    }
81    else {
82#ifdef HAVE_ZOS_PTHREADS
83        stat = errno;
84#endif
85
86        return stat;
87    }
88}
89
90APR_DECLARE(apr_status_t) apr_threadattr_detach_get(apr_threadattr_t *attr)
91{
92    int state;
93
94#ifdef PTHREAD_ATTR_GETDETACHSTATE_TAKES_ONE_ARG
95    state = pthread_attr_getdetachstate(&attr->attr);
96#else
97    pthread_attr_getdetachstate(&attr->attr, &state);
98#endif
99    if (state == DETACH_ARG(1))
100        return APR_DETACH;
101    return APR_NOTDETACH;
102}
103
104APR_DECLARE(apr_status_t) apr_threadattr_stacksize_set(apr_threadattr_t *attr,
105                                                       apr_size_t stacksize)
106{
107    int stat;
108
109    stat = pthread_attr_setstacksize(&attr->attr, stacksize);
110    if (stat == 0) {
111        return APR_SUCCESS;
112    }
113#ifdef HAVE_ZOS_PTHREADS
114    stat = errno;
115#endif
116
117    return stat;
118}
119
120APR_DECLARE(apr_status_t) apr_threadattr_guardsize_set(apr_threadattr_t *attr,
121                                                       apr_size_t size)
122{
123#ifdef HAVE_PTHREAD_ATTR_SETGUARDSIZE
124    apr_status_t rv;
125
126    rv = pthread_attr_setguardsize(&attr->attr, size);
127    if (rv == 0) {
128        return APR_SUCCESS;
129    }
130#ifdef HAVE_ZOS_PTHREADS
131    rv = errno;
132#endif
133    return rv;
134#else
135    return APR_ENOTIMPL;
136#endif
137}
138
139static void *dummy_worker(void *opaque)
140{
141    apr_thread_t *thread = (apr_thread_t*)opaque;
142    return thread->func(thread, thread->data);
143}
144
145APR_DECLARE(apr_status_t) apr_thread_create(apr_thread_t **new,
146                                            apr_threadattr_t *attr,
147                                            apr_thread_start_t func,
148                                            void *data,
149                                            apr_pool_t *pool)
150{
151    apr_status_t stat;
152    pthread_attr_t *temp;
153
154    (*new) = (apr_thread_t *)apr_pcalloc(pool, sizeof(apr_thread_t));
155
156    if ((*new) == NULL) {
157        return APR_ENOMEM;
158    }
159
160    (*new)->td = (pthread_t *)apr_pcalloc(pool, sizeof(pthread_t));
161
162    if ((*new)->td == NULL) {
163        return APR_ENOMEM;
164    }
165
166    (*new)->data = data;
167    (*new)->func = func;
168
169    if (attr)
170        temp = &attr->attr;
171    else
172        temp = NULL;
173
174    stat = apr_pool_create(&(*new)->pool, pool);
175    if (stat != APR_SUCCESS) {
176        return stat;
177    }
178
179    if ((stat = pthread_create((*new)->td, temp, dummy_worker, (*new))) == 0) {
180        return APR_SUCCESS;
181    }
182    else {
183#ifdef HAVE_ZOS_PTHREADS
184        stat = errno;
185#endif
186
187        return stat;
188    }
189}
190
191APR_DECLARE(apr_os_thread_t) apr_os_thread_current(void)
192{
193    return pthread_self();
194}
195
196APR_DECLARE(int) apr_os_thread_equal(apr_os_thread_t tid1,
197                                     apr_os_thread_t tid2)
198{
199    return pthread_equal(tid1, tid2);
200}
201
202APR_DECLARE(apr_status_t) apr_thread_exit(apr_thread_t *thd,
203                                          apr_status_t retval)
204{
205    thd->exitval = retval;
206    apr_pool_destroy(thd->pool);
207    pthread_exit(NULL);
208    return APR_SUCCESS;
209}
210
211APR_DECLARE(apr_status_t) apr_thread_join(apr_status_t *retval,
212                                          apr_thread_t *thd)
213{
214    apr_status_t stat;
215    apr_status_t *thread_stat;
216
217    if ((stat = pthread_join(*thd->td,(void *)&thread_stat)) == 0) {
218        *retval = thd->exitval;
219        return APR_SUCCESS;
220    }
221    else {
222#ifdef HAVE_ZOS_PTHREADS
223        stat = errno;
224#endif
225
226        return stat;
227    }
228}
229
230APR_DECLARE(apr_status_t) apr_thread_detach(apr_thread_t *thd)
231{
232    apr_status_t stat;
233
234#ifdef HAVE_ZOS_PTHREADS
235    if ((stat = pthread_detach(thd->td)) == 0) {
236#else
237    if ((stat = pthread_detach(*thd->td)) == 0) {
238#endif
239
240        return APR_SUCCESS;
241    }
242    else {
243#ifdef HAVE_ZOS_PTHREADS
244        stat = errno;
245#endif
246
247        return stat;
248    }
249}
250
251APR_DECLARE(void) apr_thread_yield(void)
252{
253#ifdef HAVE_PTHREAD_YIELD
254#ifdef HAVE_ZOS_PTHREADS
255    pthread_yield(NULL);
256#else
257    pthread_yield();
258#endif /* HAVE_ZOS_PTHREADS */
259#else
260#ifdef HAVE_SCHED_YIELD
261    sched_yield();
262#endif
263#endif
264}
265
266APR_DECLARE(apr_status_t) apr_thread_data_get(void **data, const char *key,
267                                              apr_thread_t *thread)
268{
269    return apr_pool_userdata_get(data, key, thread->pool);
270}
271
272APR_DECLARE(apr_status_t) apr_thread_data_set(void *data, const char *key,
273                              apr_status_t (*cleanup)(void *),
274                              apr_thread_t *thread)
275{
276    return apr_pool_userdata_set(data, key, cleanup, thread->pool);
277}
278
279APR_DECLARE(apr_status_t) apr_os_thread_get(apr_os_thread_t **thethd,
280                                            apr_thread_t *thd)
281{
282    *thethd = thd->td;
283    return APR_SUCCESS;
284}
285
286APR_DECLARE(apr_status_t) apr_os_thread_put(apr_thread_t **thd,
287                                            apr_os_thread_t *thethd,
288                                            apr_pool_t *pool)
289{
290    if (pool == NULL) {
291        return APR_ENOPOOL;
292    }
293
294    if ((*thd) == NULL) {
295        (*thd) = (apr_thread_t *)apr_pcalloc(pool, sizeof(apr_thread_t));
296        (*thd)->pool = pool;
297    }
298
299    (*thd)->td = thethd;
300    return APR_SUCCESS;
301}
302
303APR_DECLARE(apr_status_t) apr_thread_once_init(apr_thread_once_t **control,
304                                               apr_pool_t *p)
305{
306    static const pthread_once_t once_init = PTHREAD_ONCE_INIT;
307
308    *control = apr_palloc(p, sizeof(**control));
309    (*control)->once = once_init;
310    return APR_SUCCESS;
311}
312
313APR_DECLARE(apr_status_t) apr_thread_once(apr_thread_once_t *control,
314                                          void (*func)(void))
315{
316    return pthread_once(&control->once, func);
317}
318
319APR_POOL_IMPLEMENT_ACCESSOR(thread)
320
321#endif  /* HAVE_PTHREAD_H */
322#endif  /* APR_HAS_THREADS */
323
324#if !APR_HAS_THREADS
325
326/* avoid warning for no prototype */
327APR_DECLARE(apr_status_t) apr_os_thread_get(void);
328
329APR_DECLARE(apr_status_t) apr_os_thread_get(void)
330{
331    return APR_ENOTIMPL;
332}
333
334#endif
335