/src/httpd/srclib/apr/threadproc/unix/thread.c
Line | Count | Source (jump to first uncovered line) |
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 | | /* Unfortunately the kernel headers do not export the TASK_COMM_LEN |
26 | | macro. So we have to define it here. Used in apr_thread_name_get and |
27 | | apr_thread_name_set functions */ |
28 | 0 | #define TASK_COMM_LEN 16 |
29 | | |
30 | | /* Destroy the threadattr object */ |
31 | | static apr_status_t threadattr_cleanup(void *data) |
32 | 0 | { |
33 | 0 | apr_threadattr_t *attr = data; |
34 | 0 | apr_status_t rv; |
35 | |
|
36 | 0 | rv = pthread_attr_destroy(&attr->attr); |
37 | | #ifdef HAVE_ZOS_PTHREADS |
38 | | if (rv) { |
39 | | rv = errno; |
40 | | } |
41 | | #endif |
42 | 0 | return rv; |
43 | 0 | } |
44 | | |
45 | | APR_DECLARE(apr_status_t) apr_threadattr_create(apr_threadattr_t **new, |
46 | | apr_pool_t *pool) |
47 | 0 | { |
48 | 0 | apr_status_t stat; |
49 | |
|
50 | 0 | (*new) = apr_palloc(pool, sizeof(apr_threadattr_t)); |
51 | 0 | (*new)->pool = pool; |
52 | 0 | stat = pthread_attr_init(&(*new)->attr); |
53 | |
|
54 | 0 | if (stat == 0) { |
55 | 0 | apr_pool_cleanup_register(pool, *new, threadattr_cleanup, |
56 | 0 | apr_pool_cleanup_null); |
57 | 0 | return APR_SUCCESS; |
58 | 0 | } |
59 | | #ifdef HAVE_ZOS_PTHREADS |
60 | | stat = errno; |
61 | | #endif |
62 | | |
63 | 0 | return stat; |
64 | 0 | } |
65 | | |
66 | | #if defined(PTHREAD_CREATE_DETACHED) |
67 | 0 | #define DETACH_ARG(v) ((v) ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE) |
68 | | #else |
69 | | #define DETACH_ARG(v) ((v) ? 1 : 0) |
70 | | #endif |
71 | | |
72 | | APR_DECLARE(apr_status_t) apr_threadattr_detach_set(apr_threadattr_t *attr, |
73 | | apr_int32_t on) |
74 | 0 | { |
75 | 0 | apr_status_t stat; |
76 | | #ifdef HAVE_ZOS_PTHREADS |
77 | | int arg = DETACH_ARG(on); |
78 | | |
79 | | if ((stat = pthread_attr_setdetachstate(&attr->attr, &arg)) == 0) { |
80 | | #else |
81 | 0 | if ((stat = pthread_attr_setdetachstate(&attr->attr, |
82 | 0 | DETACH_ARG(on))) == 0) { |
83 | 0 | #endif |
84 | 0 | return APR_SUCCESS; |
85 | 0 | } |
86 | 0 | else { |
87 | | #ifdef HAVE_ZOS_PTHREADS |
88 | | stat = errno; |
89 | | #endif |
90 | |
|
91 | 0 | return stat; |
92 | 0 | } |
93 | 0 | } |
94 | | |
95 | | APR_DECLARE(apr_status_t) apr_threadattr_detach_get(apr_threadattr_t *attr) |
96 | 0 | { |
97 | 0 | int state; |
98 | |
|
99 | | #ifdef PTHREAD_ATTR_GETDETACHSTATE_TAKES_ONE_ARG |
100 | | state = pthread_attr_getdetachstate(&attr->attr); |
101 | | #else |
102 | 0 | pthread_attr_getdetachstate(&attr->attr, &state); |
103 | 0 | #endif |
104 | 0 | if (state == DETACH_ARG(1)) |
105 | 0 | return APR_DETACH; |
106 | 0 | return APR_NOTDETACH; |
107 | 0 | } |
108 | | |
109 | | APR_DECLARE(apr_status_t) apr_threadattr_stacksize_set(apr_threadattr_t *attr, |
110 | | apr_size_t stacksize) |
111 | 0 | { |
112 | 0 | int stat; |
113 | |
|
114 | 0 | stat = pthread_attr_setstacksize(&attr->attr, stacksize); |
115 | 0 | if (stat == 0) { |
116 | 0 | return APR_SUCCESS; |
117 | 0 | } |
118 | | #ifdef HAVE_ZOS_PTHREADS |
119 | | stat = errno; |
120 | | #endif |
121 | | |
122 | 0 | return stat; |
123 | 0 | } |
124 | | |
125 | | APR_DECLARE(apr_status_t) apr_threadattr_guardsize_set(apr_threadattr_t *attr, |
126 | | apr_size_t size) |
127 | 0 | { |
128 | 0 | #ifdef HAVE_PTHREAD_ATTR_SETGUARDSIZE |
129 | 0 | apr_status_t rv; |
130 | |
|
131 | 0 | rv = pthread_attr_setguardsize(&attr->attr, size); |
132 | 0 | if (rv == 0) { |
133 | 0 | return APR_SUCCESS; |
134 | 0 | } |
135 | | #ifdef HAVE_ZOS_PTHREADS |
136 | | rv = errno; |
137 | | #endif |
138 | 0 | return rv; |
139 | | #else |
140 | | return APR_ENOTIMPL; |
141 | | #endif |
142 | 0 | } |
143 | | |
144 | | APR_DECLARE(apr_status_t) apr_threadattr_max_free_set(apr_threadattr_t *attr, |
145 | | apr_size_t size) |
146 | 0 | { |
147 | 0 | attr->max_free = size; |
148 | 0 | return APR_SUCCESS; |
149 | 0 | } |
150 | | |
151 | | #if APR_HAS_THREAD_LOCAL |
152 | | static APR_THREAD_LOCAL apr_thread_t *current_thread = NULL; |
153 | | #endif |
154 | | |
155 | | static void *dummy_worker(void *opaque) |
156 | 0 | { |
157 | 0 | apr_thread_t *thread = (apr_thread_t*)opaque; |
158 | 0 | void *ret; |
159 | |
|
160 | 0 | #if APR_HAS_THREAD_LOCAL |
161 | 0 | current_thread = thread; |
162 | 0 | #endif |
163 | |
|
164 | 0 | apr_pool_owner_set(thread->pool, 0); |
165 | 0 | ret = thread->func(thread, thread->data); |
166 | 0 | if (thread->detached) { |
167 | 0 | apr_pool_destroy(thread->pool); |
168 | 0 | } |
169 | |
|
170 | 0 | return ret; |
171 | 0 | } |
172 | | |
173 | | static apr_status_t alloc_thread(apr_thread_t **new, |
174 | | apr_threadattr_t *attr, |
175 | | apr_thread_start_t func, void *data, |
176 | | apr_pool_t *pool) |
177 | 0 | { |
178 | 0 | apr_status_t stat; |
179 | 0 | apr_abortfunc_t abort_fn = apr_pool_abort_get(pool); |
180 | 0 | apr_pool_t *p; |
181 | | |
182 | | /* The thread can be detached anytime (from the creation or later with |
183 | | * apr_thread_detach), so it needs its own pool and allocator to not |
184 | | * depend on a parent pool which could be destroyed before the thread |
185 | | * exits. The allocator needs no mutex obviously since the pool should |
186 | | * not be used nor create children pools outside the thread. Passing |
187 | | * NULL allocator will create one like that. |
188 | | */ |
189 | 0 | stat = apr_pool_create_unmanaged_ex(&p, abort_fn, NULL); |
190 | 0 | if (stat != APR_SUCCESS) { |
191 | 0 | return stat; |
192 | 0 | } |
193 | 0 | if (attr && attr->max_free) { |
194 | 0 | apr_allocator_max_free_set(apr_pool_allocator_get(p), attr->max_free); |
195 | 0 | } |
196 | |
|
197 | 0 | (*new) = (apr_thread_t *)apr_pcalloc(p, sizeof(apr_thread_t)); |
198 | 0 | if ((*new) == NULL) { |
199 | 0 | apr_pool_destroy(p); |
200 | 0 | return APR_ENOMEM; |
201 | 0 | } |
202 | | |
203 | 0 | (*new)->pool = p; |
204 | 0 | (*new)->data = data; |
205 | 0 | (*new)->func = func; |
206 | 0 | (*new)->detached = (attr && apr_threadattr_detach_get(attr) == APR_DETACH); |
207 | 0 | (*new)->td = (pthread_t *)apr_pcalloc(p, sizeof(pthread_t)); |
208 | 0 | if ((*new)->td == NULL) { |
209 | 0 | apr_pool_destroy(p); |
210 | 0 | return APR_ENOMEM; |
211 | 0 | } |
212 | | |
213 | 0 | return APR_SUCCESS; |
214 | 0 | } |
215 | | |
216 | | APR_DECLARE(apr_status_t) apr_thread_create(apr_thread_t **new, |
217 | | apr_threadattr_t *attr, |
218 | | apr_thread_start_t func, |
219 | | void *data, |
220 | | apr_pool_t *pool) |
221 | 0 | { |
222 | 0 | apr_status_t stat; |
223 | 0 | pthread_attr_t *temp; |
224 | |
|
225 | 0 | stat = alloc_thread(new, attr, func, data, pool); |
226 | 0 | if (stat != APR_SUCCESS) { |
227 | 0 | return stat; |
228 | 0 | } |
229 | | |
230 | 0 | if (attr) |
231 | 0 | temp = &attr->attr; |
232 | 0 | else |
233 | 0 | temp = NULL; |
234 | |
|
235 | 0 | if ((stat = pthread_create((*new)->td, temp, dummy_worker, (*new)))) { |
236 | | #ifdef HAVE_ZOS_PTHREADS |
237 | | stat = errno; |
238 | | #endif |
239 | 0 | apr_pool_destroy((*new)->pool); |
240 | 0 | return stat; |
241 | 0 | } |
242 | | |
243 | 0 | return APR_SUCCESS; |
244 | 0 | } |
245 | | |
246 | | APR_DECLARE(apr_status_t) apr_thread_current_create(apr_thread_t **current, |
247 | | apr_threadattr_t *attr, |
248 | | apr_pool_t *pool) |
249 | 0 | { |
250 | 0 | #if APR_HAS_THREAD_LOCAL |
251 | 0 | apr_status_t stat; |
252 | |
|
253 | 0 | *current = apr_thread_current(); |
254 | 0 | if (*current) { |
255 | 0 | return APR_EEXIST; |
256 | 0 | } |
257 | | |
258 | 0 | stat = alloc_thread(current, attr, NULL, NULL, pool); |
259 | 0 | if (stat != APR_SUCCESS) { |
260 | 0 | *current = NULL; |
261 | 0 | return stat; |
262 | 0 | } |
263 | | |
264 | 0 | *(*current)->td = apr_os_thread_current(); |
265 | |
|
266 | 0 | current_thread = *current; |
267 | 0 | return APR_SUCCESS; |
268 | | #else |
269 | | return APR_ENOTIMPL; |
270 | | #endif |
271 | 0 | } |
272 | | |
273 | | APR_DECLARE(void) apr_thread_current_after_fork(void) |
274 | 0 | { |
275 | 0 | #if APR_HAS_THREAD_LOCAL |
276 | 0 | current_thread = NULL; |
277 | 0 | #endif |
278 | 0 | } |
279 | | |
280 | | APR_DECLARE(apr_thread_t *) apr_thread_current(void) |
281 | 0 | { |
282 | 0 | #if APR_HAS_THREAD_LOCAL |
283 | 0 | return current_thread; |
284 | | #else |
285 | | return NULL; |
286 | | #endif |
287 | 0 | } |
288 | | |
289 | | APR_DECLARE(apr_status_t) apr_thread_name_set(const char *name, |
290 | | apr_thread_t *thread, |
291 | | apr_pool_t *pool) |
292 | 0 | { |
293 | 0 | #if HAVE_PTHREAD_SETNAME_NP |
294 | 0 | pthread_t td; |
295 | |
|
296 | 0 | size_t name_len; |
297 | 0 | if (!name) { |
298 | 0 | return APR_BADARG; |
299 | 0 | } |
300 | | |
301 | 0 | if (thread) { |
302 | 0 | td = *thread->td; |
303 | 0 | } |
304 | 0 | else { |
305 | 0 | td = pthread_self(); |
306 | 0 | } |
307 | |
|
308 | 0 | name_len = strlen(name); |
309 | 0 | if (name_len >= TASK_COMM_LEN) { |
310 | 0 | name = name + name_len - TASK_COMM_LEN + 1; |
311 | 0 | } |
312 | |
|
313 | 0 | return pthread_setname_np(td, name); |
314 | | #else |
315 | | return APR_ENOTIMPL; |
316 | | #endif |
317 | 0 | } |
318 | | |
319 | | APR_DECLARE(apr_status_t) apr_thread_name_get(char **name, |
320 | | apr_thread_t *thread, |
321 | | apr_pool_t *pool) |
322 | 0 | { |
323 | 0 | #if HAVE_PTHREAD_SETNAME_NP |
324 | 0 | pthread_t td; |
325 | 0 | if (thread) { |
326 | 0 | td = *thread->td; |
327 | 0 | } |
328 | 0 | else { |
329 | 0 | td = pthread_self(); |
330 | 0 | } |
331 | |
|
332 | 0 | *name = apr_pcalloc(pool, TASK_COMM_LEN); |
333 | 0 | return pthread_getname_np(td, *name, TASK_COMM_LEN); |
334 | | #else |
335 | | return APR_ENOTIMPL; |
336 | | #endif |
337 | 0 | } |
338 | | |
339 | | APR_DECLARE(apr_os_thread_t) apr_os_thread_current(void) |
340 | 11.2k | { |
341 | 11.2k | return pthread_self(); |
342 | 11.2k | } |
343 | | |
344 | | APR_DECLARE(int) apr_os_thread_equal(apr_os_thread_t tid1, |
345 | | apr_os_thread_t tid2) |
346 | 0 | { |
347 | 0 | return pthread_equal(tid1, tid2); |
348 | 0 | } |
349 | | |
350 | | APR_DECLARE(void) apr_thread_exit(apr_thread_t *thd, |
351 | | apr_status_t retval) |
352 | 0 | { |
353 | 0 | thd->exitval = retval; |
354 | 0 | if (thd->detached) { |
355 | 0 | apr_pool_destroy(thd->pool); |
356 | 0 | } |
357 | 0 | pthread_exit(NULL); |
358 | 0 | } |
359 | | |
360 | | APR_DECLARE(apr_status_t) apr_thread_join(apr_status_t *retval, |
361 | | apr_thread_t *thd) |
362 | 0 | { |
363 | 0 | apr_status_t stat; |
364 | 0 | void *thread_stat; |
365 | |
|
366 | 0 | if (thd->detached) { |
367 | 0 | return APR_EINVAL; |
368 | 0 | } |
369 | | |
370 | 0 | if ((stat = pthread_join(*thd->td, &thread_stat))) { |
371 | | #ifdef HAVE_ZOS_PTHREADS |
372 | | stat = errno; |
373 | | #endif |
374 | 0 | return stat; |
375 | 0 | } |
376 | | |
377 | 0 | *retval = thd->exitval; |
378 | 0 | apr_pool_destroy(thd->pool); |
379 | 0 | return APR_SUCCESS; |
380 | 0 | } |
381 | | |
382 | | APR_DECLARE(apr_status_t) apr_thread_detach(apr_thread_t *thd) |
383 | 0 | { |
384 | 0 | apr_status_t stat; |
385 | |
|
386 | 0 | if (thd->detached) { |
387 | 0 | return APR_EINVAL; |
388 | 0 | } |
389 | | |
390 | | #ifdef HAVE_ZOS_PTHREADS |
391 | | if ((stat = pthread_detach(thd->td)) == 0) { |
392 | | #else |
393 | 0 | if ((stat = pthread_detach(*thd->td)) == 0) { |
394 | 0 | #endif |
395 | 0 | thd->detached = 1; |
396 | |
|
397 | 0 | return APR_SUCCESS; |
398 | 0 | } |
399 | 0 | else { |
400 | | #ifdef HAVE_ZOS_PTHREADS |
401 | | stat = errno; |
402 | | #endif |
403 | |
|
404 | 0 | return stat; |
405 | 0 | } |
406 | 0 | } |
407 | | |
408 | | APR_DECLARE(void) apr_thread_yield(void) |
409 | 0 | { |
410 | 0 | #ifdef HAVE_PTHREAD_YIELD |
411 | | #ifdef HAVE_ZOS_PTHREADS |
412 | | pthread_yield(NULL); |
413 | | #else |
414 | 0 | pthread_yield(); |
415 | 0 | #endif /* HAVE_ZOS_PTHREADS */ |
416 | | #else |
417 | | #ifdef HAVE_SCHED_YIELD |
418 | | sched_yield(); |
419 | | #endif |
420 | | #endif |
421 | 0 | } |
422 | | |
423 | | APR_DECLARE(apr_status_t) apr_thread_data_get(void **data, const char *key, |
424 | | apr_thread_t *thread) |
425 | 0 | { |
426 | 0 | if (thread == NULL) { |
427 | 0 | *data = NULL; |
428 | 0 | return APR_ENOTHREAD; |
429 | 0 | } |
430 | 0 | return apr_pool_userdata_get(data, key, thread->pool); |
431 | 0 | } |
432 | | |
433 | | APR_DECLARE(apr_status_t) apr_thread_data_set(void *data, const char *key, |
434 | | apr_status_t (*cleanup)(void *), |
435 | | apr_thread_t *thread) |
436 | 0 | { |
437 | 0 | if (thread == NULL) { |
438 | 0 | return APR_ENOTHREAD; |
439 | 0 | } |
440 | 0 | return apr_pool_userdata_set(data, key, cleanup, thread->pool); |
441 | 0 | } |
442 | | |
443 | | APR_DECLARE(apr_status_t) apr_os_thread_get(apr_os_thread_t **thethd, |
444 | | apr_thread_t *thd) |
445 | 0 | { |
446 | 0 | *thethd = thd->td; |
447 | 0 | return APR_SUCCESS; |
448 | 0 | } |
449 | | |
450 | | APR_DECLARE(apr_status_t) apr_os_thread_put(apr_thread_t **thd, |
451 | | apr_os_thread_t *thethd, |
452 | | apr_pool_t *pool) |
453 | 0 | { |
454 | 0 | if (pool == NULL) { |
455 | 0 | return APR_ENOPOOL; |
456 | 0 | } |
457 | | |
458 | 0 | if ((*thd) == NULL) { |
459 | 0 | (*thd) = (apr_thread_t *)apr_pcalloc(pool, sizeof(apr_thread_t)); |
460 | 0 | (*thd)->pool = pool; |
461 | 0 | } |
462 | |
|
463 | 0 | (*thd)->td = thethd; |
464 | 0 | return APR_SUCCESS; |
465 | 0 | } |
466 | | |
467 | | APR_DECLARE(apr_status_t) apr_thread_once_init(apr_thread_once_t **control, |
468 | | apr_pool_t *p) |
469 | 0 | { |
470 | 0 | static const pthread_once_t once_init = PTHREAD_ONCE_INIT; |
471 | |
|
472 | 0 | *control = apr_palloc(p, sizeof(**control)); |
473 | 0 | (*control)->once = once_init; |
474 | 0 | return APR_SUCCESS; |
475 | 0 | } |
476 | | |
477 | | APR_DECLARE(apr_status_t) apr_thread_once(apr_thread_once_t *control, |
478 | | void (*func)(void)) |
479 | 0 | { |
480 | 0 | return pthread_once(&control->once, func); |
481 | 0 | } |
482 | | |
483 | | APR_POOL_IMPLEMENT_ACCESSOR(thread) |
484 | | |
485 | | #endif /* HAVE_PTHREAD_H */ |
486 | | #endif /* APR_HAS_THREADS */ |
487 | | |
488 | | #if !APR_HAS_THREADS |
489 | | |
490 | | /* avoid warning for no prototype */ |
491 | | APR_DECLARE(apr_status_t) apr_os_thread_get(void); |
492 | | |
493 | | APR_DECLARE(apr_status_t) apr_os_thread_get(void) |
494 | | { |
495 | | return APR_ENOTIMPL; |
496 | | } |
497 | | |
498 | | #endif |