Coverage Report

Created: 2025-07-12 06:21

/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