]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/thread.c
Merge pull request #1312 from weblate/weblate-cups-cups
[thirdparty/cups.git] / cups / thread.c
1 //
2 // Threading primitives for CUPS.
3 //
4 // Copyright © 2020-2024 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 {
420 pthread_cond_broadcast(cond);
421 }
422
423
424 //
425 // 'cupsCondDestroy()' - Destroy a condition variable.
426 //
427
428 void
429 cupsCondDestroy(cups_cond_t *cond) // I - Condition
430 {
431 pthread_cond_destroy(cond);
432 }
433
434
435 //
436 // 'cupsCondInit()' - Initialize a condition variable.
437 //
438
439 void
440 cupsCondInit(cups_cond_t *cond) // I - Condition
441 {
442 pthread_cond_init(cond, NULL);
443 }
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 {
455 if (timeout > 0.0)
456 {
457 struct timespec abstime; // Timeout
458
459 clock_gettime(CLOCK_REALTIME, &abstime);
460
461 abstime.tv_sec += (long)timeout;
462 abstime.tv_nsec += (long)(1000000000 * (timeout - (long)timeout));
463
464 while (abstime.tv_nsec >= 1000000000)
465 {
466 abstime.tv_nsec -= 1000000000;
467 abstime.tv_sec ++;
468 };
469
470 (void)pthread_cond_timedwait(cond, mutex, &abstime);
471 }
472 else
473 (void)pthread_cond_wait(cond, mutex);
474 }
475
476
477 //
478 // 'cupsMutexDestroy()' - Destroy a mutex.
479 //
480
481 void
482 cupsMutexDestroy(cups_mutex_t *mutex) // I - Mutex
483 {
484 pthread_mutex_destroy(mutex);
485 }
486
487
488 //
489 // 'cupsMutexInit()' - Initialize a mutex.
490 //
491
492 void
493 cupsMutexInit(cups_mutex_t *mutex) // I - Mutex
494 {
495 pthread_mutex_init(mutex, NULL);
496 }
497
498
499 //
500 // 'cupsMutexLock()' - Lock a mutex.
501 //
502
503 void
504 cupsMutexLock(cups_mutex_t *mutex) // I - Mutex
505 {
506 pthread_mutex_lock(mutex);
507 }
508
509
510 //
511 // 'cupsMutexUnlock()' - Unlock a mutex.
512 //
513
514 void
515 cupsMutexUnlock(cups_mutex_t *mutex) // I - Mutex
516 {
517 pthread_mutex_unlock(mutex);
518 }
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 {
528 pthread_rwlock_destroy(rwlock);
529 }
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 {
539 pthread_rwlock_init(rwlock, NULL);
540 }
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 {
550 pthread_rwlock_rdlock(rwlock);
551 }
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 {
561 pthread_rwlock_wrlock(rwlock);
562 }
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 {
572 pthread_rwlock_unlock(rwlock);
573 }
574
575
576 //
577 // 'cupsThreadCancel()' - Cancel (kill) a thread.
578 //
579
580 void
581 cupsThreadCancel(cups_thread_t thread)// I - Thread ID
582 {
583 pthread_cancel(thread);
584 }
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 {
596 pthread_t thread; // Thread
597
598
599 if (pthread_create(&thread, NULL, (void *(*)(void *))func, arg))
600 return (CUPS_THREAD_INVALID);
601 else
602 return (thread);
603 }
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 {
613 pthread_detach(thread);
614 }
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 {
624 void *ret; // Return value
625
626
627 if (pthread_join(thread, &ret))
628 return (NULL);
629 else
630 return (ret);
631 }
632 #endif // _WIN32