/src/libcups/cups/thread.c
Line | Count | Source (jump to first uncovered line) |
1 | | // |
2 | | // Threading primitives for CUPS. |
3 | | // |
4 | | // Copyright © 2021-2023 by OpenPrinting. |
5 | | // Copyright © 2009-2018 by Apple Inc. |
6 | | // |
7 | | // Licensed under Apache License v2.0. See the file "LICENSE" for more |
8 | | // information. |
9 | | // |
10 | | |
11 | | #include "cups-private.h" |
12 | | #include "thread.h" |
13 | | |
14 | | |
15 | | // |
16 | | // Windows threading... |
17 | | // |
18 | | |
19 | | #if _WIN32 |
20 | | # include <setjmp.h> |
21 | | |
22 | | |
23 | | // |
24 | | // Private structures... |
25 | | // |
26 | | |
27 | | struct _cups_thread_s |
28 | | { |
29 | | HANDLE h; // Thread handle |
30 | | void *(*func)(void *); // Thread start function |
31 | | void *arg; // Argument to pass to function |
32 | | void *retval; // Return value from function |
33 | | bool canceled; // Is the thread canceled? |
34 | | jmp_buf jumpbuf; // Jump buffer for error recovery |
35 | | }; |
36 | | |
37 | | |
38 | | // |
39 | | // Local functions... |
40 | | // |
41 | | |
42 | | static cups_thread_t win32_self(void); |
43 | | static void win32_testcancel(void); |
44 | | static DWORD win32_tls(void); |
45 | | static int win32_wrapper(cups_thread_t thread); |
46 | | |
47 | | |
48 | | // |
49 | | // 'cupsCondBroadcast()' - Wake up waiting threads. |
50 | | // |
51 | | |
52 | | void |
53 | | cupsCondBroadcast(cups_cond_t *cond) // I - Condition variable |
54 | | { |
55 | | if (cond) |
56 | | WakeAllConditionVariable(cond); |
57 | | } |
58 | | |
59 | | |
60 | | // |
61 | | // 'cupsCondDestroy()' - Destroy a condition variable. |
62 | | // |
63 | | |
64 | | void |
65 | | cupsCondDestroy(cups_cond_t *cond) // I - Condition variable |
66 | | { |
67 | | (void)cond; |
68 | | } |
69 | | |
70 | | |
71 | | // |
72 | | // 'cupsCondInit()' - Initialize a condition variable. |
73 | | // |
74 | | |
75 | | void |
76 | | cupsCondInit(cups_cond_t *cond) // I - Condition variable |
77 | | { |
78 | | if (cond) |
79 | | InitializeConditionVariable(cond); |
80 | | } |
81 | | |
82 | | |
83 | | // |
84 | | // 'cupsCondWait()' - Wait for a condition with optional timeout. |
85 | | // |
86 | | |
87 | | void |
88 | | cupsCondWait(cups_cond_t *cond, // I - Condition |
89 | | cups_mutex_t *mutex, // I - Mutex |
90 | | double timeout) // I - Timeout in seconds (`0` or negative for none) |
91 | | { |
92 | | win32_testcancel(); |
93 | | |
94 | | if (cond && mutex) |
95 | | { |
96 | | if (timeout > 0.0) |
97 | | SleepConditionVariableCS(cond, mutex, (int)(1000.0 * timeout)); |
98 | | else |
99 | | SleepConditionVariableCS(cond, mutex, INFINITE); |
100 | | } |
101 | | } |
102 | | |
103 | | |
104 | | // |
105 | | // 'cupsMutexDestroy()' - Destroy a mutex. |
106 | | // |
107 | | |
108 | | void |
109 | | cupsMutexDestroy(cups_mutex_t *mutex) // I - Mutex |
110 | | { |
111 | | (void)mutex; |
112 | | } |
113 | | |
114 | | |
115 | | // |
116 | | // 'cupsMutexInit()' - Initialize a mutex. |
117 | | // |
118 | | |
119 | | void |
120 | | cupsMutexInit(cups_mutex_t *mutex) // I - Mutex |
121 | | { |
122 | | if (mutex) |
123 | | InitializeCriticalSection(mutex); |
124 | | } |
125 | | |
126 | | |
127 | | // |
128 | | // 'cupsMutexLock()' - Lock a mutex. |
129 | | // |
130 | | |
131 | | void |
132 | | cupsMutexLock(cups_mutex_t *mutex) // I - Mutex |
133 | | { |
134 | | if (mutex) |
135 | | EnterCriticalSection(mutex); |
136 | | } |
137 | | |
138 | | |
139 | | // |
140 | | // 'cupsMutexUnlock()' - Unlock a mutex. |
141 | | // |
142 | | |
143 | | void |
144 | | cupsMutexUnlock(cups_mutex_t *mutex) // I - Mutex |
145 | | { |
146 | | if (mutex) |
147 | | LeaveCriticalSection(mutex); |
148 | | } |
149 | | |
150 | | |
151 | | // |
152 | | // 'cupsRWDestroy()' - Destroy a reader/writer lock. |
153 | | // |
154 | | |
155 | | void |
156 | | cupsRWDestroy(cups_rwlock_t *rwlock) // I - Reader/writer lock |
157 | | { |
158 | | (void)rwlock; |
159 | | } |
160 | | |
161 | | |
162 | | // |
163 | | // 'cupsRWInit()' - Initialize a reader/writer lock. |
164 | | // |
165 | | |
166 | | void |
167 | | cupsRWInit(cups_rwlock_t *rwlock) // I - Reader/writer lock |
168 | | { |
169 | | if (rwlock) |
170 | | InitializeSRWLock(rwlock); |
171 | | } |
172 | | |
173 | | |
174 | | // |
175 | | // 'cupsRWLockRead()' - Acquire a reader/writer lock for reading. |
176 | | // |
177 | | |
178 | | void |
179 | | cupsRWLockRead(cups_rwlock_t *rwlock) // I - Reader/writer lock |
180 | | { |
181 | | if (rwlock) |
182 | | AcquireSRWLockShared(rwlock); |
183 | | } |
184 | | |
185 | | |
186 | | // |
187 | | // 'cupsRWLockWrite()' - Acquire a reader/writer lock for writing. |
188 | | // |
189 | | |
190 | | void |
191 | | cupsRWLockWrite(cups_rwlock_t *rwlock)// I - Reader/writer lock |
192 | | { |
193 | | if (rwlock) |
194 | | AcquireSRWLockExclusive(rwlock); |
195 | | } |
196 | | |
197 | | |
198 | | // |
199 | | // 'cupsRWUnlock()' - Release a reader/writer lock. |
200 | | // |
201 | | |
202 | | void |
203 | | cupsRWUnlock(cups_rwlock_t *rwlock) // I - Reader/writer lock |
204 | | { |
205 | | if (rwlock) |
206 | | { |
207 | | void *val = *(void **)rwlock;// Lock value |
208 | | |
209 | | if (val == (void *)1) |
210 | | ReleaseSRWLockExclusive(rwlock); |
211 | | else |
212 | | ReleaseSRWLockShared(rwlock); |
213 | | } |
214 | | } |
215 | | |
216 | | |
217 | | // |
218 | | // 'cupsThreadCancel()' - Cancel (kill) a thread. |
219 | | // |
220 | | |
221 | | void |
222 | | cupsThreadCancel(cups_thread_t thread)// I - Thread ID |
223 | | { |
224 | | if (thread) |
225 | | thread->canceled = true; |
226 | | } |
227 | | |
228 | | |
229 | | // |
230 | | // 'cupsThreadCreate()' - Create a thread. |
231 | | // |
232 | | |
233 | | cups_thread_t // O - Thread ID or `CUPS_THREAD_INVALID` on failure |
234 | | cupsThreadCreate( |
235 | | cups_thread_func_t func, // I - Entry point |
236 | | void *arg) // I - Entry point context |
237 | | { |
238 | | cups_thread_t thread; // Thread data |
239 | | |
240 | | |
241 | | if (!func) |
242 | | return (CUPS_THREAD_INVALID); |
243 | | |
244 | | if ((thread = (cups_thread_t)calloc(1, sizeof(struct _cups_thread_s))) == NULL) |
245 | | return (CUPS_THREAD_INVALID); |
246 | | |
247 | | thread->func = func; |
248 | | thread->arg = arg; |
249 | | thread->h = (HANDLE)_beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE)win32_wrapper, thread, 0, NULL); |
250 | | |
251 | | if (thread->h == 0 || thread->h == (HANDLE)-1) |
252 | | { |
253 | | free(thread); |
254 | | return (CUPS_THREAD_INVALID); |
255 | | } |
256 | | |
257 | | return (thread); |
258 | | } |
259 | | |
260 | | |
261 | | // |
262 | | // 'cupsThreadDetach()' - Tell the OS that the thread is running independently. |
263 | | // |
264 | | |
265 | | void |
266 | | cupsThreadDetach(cups_thread_t thread)// I - Thread ID |
267 | | { |
268 | | if (thread) |
269 | | { |
270 | | CloseHandle(thread->h); |
271 | | thread->h = 0; |
272 | | } |
273 | | } |
274 | | |
275 | | |
276 | | // |
277 | | // 'cupsThreadWait()' - Wait for a thread to exit. |
278 | | // |
279 | | |
280 | | void * // O - Return value |
281 | | cupsThreadWait(cups_thread_t thread) // I - Thread ID |
282 | | { |
283 | | void *retval; // Return value |
284 | | |
285 | | |
286 | | if (!thread) |
287 | | return (NULL); |
288 | | |
289 | | win32_testcancel(); |
290 | | |
291 | | if (thread->h) |
292 | | { |
293 | | WaitForSingleObject(thread->h, INFINITE); |
294 | | CloseHandle(thread->h); |
295 | | } |
296 | | |
297 | | retval = thread->retval; |
298 | | |
299 | | free(thread); |
300 | | |
301 | | return (retval); |
302 | | } |
303 | | |
304 | | |
305 | | // |
306 | | // 'win32_self()' - Return the current thread. |
307 | | // |
308 | | |
309 | | static cups_thread_t // O - Thread |
310 | | win32_self(void) |
311 | | { |
312 | | cups_thread_t thread; // Thread |
313 | | |
314 | | |
315 | | if ((thread = TlsGetValue(win32_tls())) == NULL) |
316 | | { |
317 | | // Main thread, so create the info we need... |
318 | | if ((thread = (cups_thread_t)calloc(1, sizeof(struct _cups_thread_s))) != NULL) |
319 | | { |
320 | | thread->h = GetCurrentThread(); |
321 | | TlsSetValue(win32_tls(), thread); |
322 | | |
323 | | if (setjmp(thread->jumpbuf)) |
324 | | { |
325 | | if (!thread->h) |
326 | | free(thread); |
327 | | |
328 | | _endthreadex(0); |
329 | | } |
330 | | } |
331 | | } |
332 | | |
333 | | return (thread); |
334 | | } |
335 | | |
336 | | |
337 | | // |
338 | | // 'win32_testcancel()' - Mark a safe cancellation point. |
339 | | // |
340 | | |
341 | | static void |
342 | | win32_testcancel(void) |
343 | | { |
344 | | cups_thread_t thread; // Current thread |
345 | | |
346 | | |
347 | | // Go to the thread's exit handler if we've been canceled... |
348 | | if ((thread = win32_self()) != NULL && thread->canceled) |
349 | | longjmp(thread->jumpbuf, 1); |
350 | | } |
351 | | |
352 | | |
353 | | // |
354 | | // 'win32_tls()' - Get the thread local storage key. |
355 | | // |
356 | | |
357 | | static DWORD // O - Key |
358 | | win32_tls(void) |
359 | | { |
360 | | static DWORD tls = 0; // Thread local storage key |
361 | | static CRITICAL_SECTION tls_mutex = { (void*)-1, -1, 0, 0, 0, 0 }; |
362 | | // Lock for thread local storage access |
363 | | |
364 | | |
365 | | EnterCriticalSection(&tls_mutex); |
366 | | if (!tls) |
367 | | { |
368 | | if ((tls = TlsAlloc()) == TLS_OUT_OF_INDEXES) |
369 | | abort(); |
370 | | } |
371 | | LeaveCriticalSection(&tls_mutex); |
372 | | |
373 | | return (tls); |
374 | | } |
375 | | |
376 | | |
377 | | // |
378 | | // 'win32_wrapper()' - Wrapper function for a POSIX thread. |
379 | | // |
380 | | |
381 | | static int // O - Exit status |
382 | | win32_wrapper(cups_thread_t thread) // I - Thread |
383 | | { |
384 | | TlsSetValue(win32_tls(), thread); |
385 | | |
386 | | if (!setjmp(thread->jumpbuf)) |
387 | | { |
388 | | // Call function in thread... |
389 | | thread->retval = (thread->func)(thread->arg); |
390 | | } |
391 | | |
392 | | // Clean up... |
393 | | while (thread->h == (HANDLE)-1) |
394 | | { |
395 | | // win32_create hasn't finished initializing the handle... |
396 | | YieldProcessor(); |
397 | | _ReadWriteBarrier(); |
398 | | } |
399 | | |
400 | | // Free if detached... |
401 | | if (!thread->h) |
402 | | free(thread); |
403 | | |
404 | | return (0); |
405 | | } |
406 | | |
407 | | |
408 | | #else |
409 | | // |
410 | | // POSIX threading... |
411 | | // |
412 | | |
413 | | // |
414 | | // 'cupsCondBroadcast()' - Wake up waiting threads. |
415 | | // |
416 | | |
417 | | void |
418 | | cupsCondBroadcast(cups_cond_t *cond) // I - Condition |
419 | 0 | { |
420 | 0 | pthread_cond_broadcast(cond); |
421 | 0 | } |
422 | | |
423 | | |
424 | | // |
425 | | // 'cupsCondDestroy()' - Destroy a condition variable. |
426 | | // |
427 | | |
428 | | void |
429 | | cupsCondDestroy(cups_cond_t *cond) // I - Condition |
430 | 0 | { |
431 | 0 | pthread_cond_destroy(cond); |
432 | 0 | } |
433 | | |
434 | | |
435 | | // |
436 | | // 'cupsCondInit()' - Initialize a condition variable. |
437 | | // |
438 | | |
439 | | void |
440 | | cupsCondInit(cups_cond_t *cond) // I - Condition |
441 | 0 | { |
442 | 0 | pthread_cond_init(cond, NULL); |
443 | 0 | } |
444 | | |
445 | | |
446 | | // |
447 | | // 'cupsCondWait()' - Wait for a condition with optional timeout. |
448 | | // |
449 | | |
450 | | void |
451 | | cupsCondWait(cups_cond_t *cond, // I - Condition |
452 | | cups_mutex_t *mutex, // I - Mutex |
453 | | double timeout) // I - Timeout in seconds (`0` or negative for none) |
454 | 0 | { |
455 | 0 | if (timeout > 0.0) |
456 | 0 | { |
457 | 0 | struct timespec abstime; // Timeout |
458 | |
|
459 | 0 | clock_gettime(CLOCK_REALTIME, &abstime); |
460 | |
|
461 | 0 | abstime.tv_sec += (long)timeout; |
462 | 0 | abstime.tv_nsec += (long)(1000000000 * (timeout - (long)timeout)); |
463 | |
|
464 | 0 | while (abstime.tv_nsec >= 1000000000) |
465 | 0 | { |
466 | 0 | abstime.tv_nsec -= 1000000000; |
467 | 0 | abstime.tv_sec ++; |
468 | 0 | }; |
469 | |
|
470 | 0 | (void)pthread_cond_timedwait(cond, mutex, &abstime); |
471 | 0 | } |
472 | 0 | else |
473 | 0 | (void)pthread_cond_wait(cond, mutex); |
474 | 0 | } |
475 | | |
476 | | |
477 | | // |
478 | | // 'cupsMutexDestroy()' - Destroy a mutex. |
479 | | // |
480 | | |
481 | | void |
482 | | cupsMutexDestroy(cups_mutex_t *mutex) // I - Mutex |
483 | 0 | { |
484 | 0 | pthread_mutex_destroy(mutex); |
485 | 0 | } |
486 | | |
487 | | |
488 | | // |
489 | | // 'cupsMutexInit()' - Initialize a mutex. |
490 | | // |
491 | | |
492 | | void |
493 | | cupsMutexInit(cups_mutex_t *mutex) // I - Mutex |
494 | 0 | { |
495 | 0 | pthread_mutex_init(mutex, NULL); |
496 | 0 | } |
497 | | |
498 | | |
499 | | // |
500 | | // 'cupsMutexLock()' - Lock a mutex. |
501 | | // |
502 | | |
503 | | void |
504 | | cupsMutexLock(cups_mutex_t *mutex) // I - Mutex |
505 | 37.2M | { |
506 | 37.2M | pthread_mutex_lock(mutex); |
507 | 37.2M | } |
508 | | |
509 | | |
510 | | // |
511 | | // 'cupsMutexUnlock()' - Unlock a mutex. |
512 | | // |
513 | | |
514 | | void |
515 | | cupsMutexUnlock(cups_mutex_t *mutex) // I - Mutex |
516 | 37.2M | { |
517 | 37.2M | pthread_mutex_unlock(mutex); |
518 | 37.2M | } |
519 | | |
520 | | |
521 | | // |
522 | | // 'cupsRWDestroy()' - Destroy a reader/writer lock. |
523 | | // |
524 | | |
525 | | void |
526 | | cupsRWDestroy(cups_rwlock_t *rwlock) // I - Reader/writer lock |
527 | 0 | { |
528 | 0 | pthread_rwlock_destroy(rwlock); |
529 | 0 | } |
530 | | |
531 | | |
532 | | // |
533 | | // 'cupsRWInit()' - Initialize a reader/writer lock. |
534 | | // |
535 | | |
536 | | void |
537 | | cupsRWInit(cups_rwlock_t *rwlock) // I - Reader/writer lock |
538 | 1 | { |
539 | 1 | pthread_rwlock_init(rwlock, NULL); |
540 | 1 | } |
541 | | |
542 | | |
543 | | // |
544 | | // 'cupsRWLockRead()' - Acquire a reader/writer lock for reading. |
545 | | // |
546 | | |
547 | | void |
548 | | cupsRWLockRead(cups_rwlock_t *rwlock) // I - Reader/writer lock |
549 | 548 | { |
550 | 548 | pthread_rwlock_rdlock(rwlock); |
551 | 548 | } |
552 | | |
553 | | |
554 | | // |
555 | | // 'cupsRWLockWrite()' - Acquire a reader/writer lock for writing. |
556 | | // |
557 | | |
558 | | void |
559 | | cupsRWLockWrite(cups_rwlock_t *rwlock)// I - Reader/writer lock |
560 | 1 | { |
561 | 1 | pthread_rwlock_wrlock(rwlock); |
562 | 1 | } |
563 | | |
564 | | |
565 | | // |
566 | | // 'cupsRWUnlock()' - Release a reader/writer lock. |
567 | | // |
568 | | |
569 | | void |
570 | | cupsRWUnlock(cups_rwlock_t *rwlock) // I - Reader/writer lock |
571 | 549 | { |
572 | 549 | pthread_rwlock_unlock(rwlock); |
573 | 549 | } |
574 | | |
575 | | |
576 | | // |
577 | | // 'cupsThreadCancel()' - Cancel (kill) a thread. |
578 | | // |
579 | | |
580 | | void |
581 | | cupsThreadCancel(cups_thread_t thread)// I - Thread ID |
582 | 0 | { |
583 | 0 | pthread_cancel(thread); |
584 | 0 | } |
585 | | |
586 | | |
587 | | // |
588 | | // 'cupsThreadCreate()' - Create a thread. |
589 | | // |
590 | | |
591 | | cups_thread_t // O - Thread ID or `CUPS_THREAD_INVALID` on failure |
592 | | cupsThreadCreate( |
593 | | cups_thread_func_t func, // I - Entry point |
594 | | void *arg) // I - Entry point context |
595 | 0 | { |
596 | 0 | pthread_t thread; // Thread |
597 | | |
598 | |
|
599 | 0 | if (pthread_create(&thread, NULL, (void *(*)(void *))func, arg)) |
600 | 0 | return (CUPS_THREAD_INVALID); |
601 | 0 | else |
602 | 0 | return (thread); |
603 | 0 | } |
604 | | |
605 | | |
606 | | // |
607 | | // 'cupsThreadDetach()' - Tell the OS that the thread is running independently. |
608 | | // |
609 | | |
610 | | void |
611 | | cupsThreadDetach(cups_thread_t thread)// I - Thread ID |
612 | 0 | { |
613 | 0 | pthread_detach(thread); |
614 | 0 | } |
615 | | |
616 | | |
617 | | // |
618 | | // 'cupsThreadWait()' - Wait for a thread to exit. |
619 | | // |
620 | | |
621 | | void * // O - Return value |
622 | | cupsThreadWait(cups_thread_t thread) // I - Thread ID |
623 | 0 | { |
624 | 0 | void *ret; // Return value |
625 | | |
626 | |
|
627 | 0 | if (pthread_join(thread, &ret)) |
628 | 0 | return (NULL); |
629 | 0 | else |
630 | 0 | return (ret); |
631 | 0 | } |
632 | | #endif // _WIN32 |