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