Coverage Report

Created: 2026-08-13 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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