Coverage Report

Created: 2026-08-13 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pupnp/upnp/src/threadutil/ThreadPool.c
Line
Count
Source
1
/*******************************************************************************
2
 *
3
 * Copyright (c) 2000-2003 Intel Corporation
4
 * All rights reserved.
5
 * Copyright (c) 2012 France Telecom All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions are met:
9
 *
10
 * - Redistributions of source code must retain the above copyright notice,
11
 * this list of conditions and the following disclaimer.
12
 * - Redistributions in binary form must reproduce the above copyright notice,
13
 * this list of conditions and the following disclaimer in the documentation
14
 * and/or other materials provided with the distribution.
15
 * - Neither name of Intel Corporation nor the names of its contributors
16
 * may be used to endorse or promote products derived from this software
17
 * without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
23
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
 *
31
 ******************************************************************************/
32
33
/*!
34
 * \file
35
 */
36
37
#include "config.h" // IWYU pragma: keep - must be first; defines UPNP_USE_RWLOCK before ithread.h
38
39
#include "LinkedList.h"
40
#include "ithread.h"
41
#include <errno.h>
42
#include <pthread.h>
43
#include <sched.h>
44
#include <time.h>
45
46
#include "ThreadPool.h"
47
48
#include "FreeList.h"
49
50
#include <assert.h>
51
#include <stdio.h>
52
#include <stdlib.h>
53
#include <string.h> /* for memset()*/
54
55
#ifdef HAVE_BACKTRACE
56
  #include <execinfo.h>
57
  #include <unistd.h>
58
#endif
59
60
/*!
61
 * \brief Returns the difference in milliseconds between two timeval structures.
62
 *
63
 * \internal
64
 *
65
 * \return The difference in milliseconds, time1-time2.
66
 */
67
static long DiffMillis(
68
  /*! . */
69
  struct timeval *time1,
70
  /*! . */
71
  struct timeval *time2)
72
0
{
73
0
  double temp = 0.0;
74
75
0
  temp = (double)time1->tv_sec - (double)time2->tv_sec;
76
  /* convert to milliseconds */
77
0
  temp *= 1000.0;
78
79
  /* convert microseconds to milliseconds and add to temp */
80
  /* implicit flooring of unsigned long data type */
81
0
  temp += ((double)time1->tv_usec - (double)time2->tv_usec) / 1000.0;
82
83
0
  return (long)temp;
84
0
}
85
86
#ifdef STATS
87
/*!
88
 * \brief Initializes the statistics structure.
89
 *
90
 * \internal
91
 */
92
static void StatsInit(
93
  /*! Must be valid non null stats structure. */
94
  ThreadPoolStats *stats)
95
0
{
96
0
  stats->totalIdleTime = 0.0;
97
0
  stats->totalJobsHQ = 0;
98
0
  stats->totalJobsLQ = 0;
99
0
  stats->totalJobsMQ = 0;
100
0
  stats->totalTimeHQ = 0.0;
101
0
  stats->totalTimeMQ = 0.0;
102
0
  stats->totalTimeLQ = 0.0;
103
0
  stats->totalWorkTime = 0.0;
104
0
  stats->totalIdleTime = 0.0;
105
0
  stats->avgWaitHQ = 0.0;
106
0
  stats->avgWaitMQ = 0.0;
107
0
  stats->avgWaitLQ = 0.0;
108
0
  stats->workerThreads = 0;
109
0
  stats->idleThreads = 0;
110
0
  stats->persistentThreads = 0;
111
0
  stats->maxThreads = 0;
112
0
  stats->totalThreads = 0;
113
0
  stats->droppedJobs = 0;
114
0
}
115
116
/*!
117
 * \brief
118
 *
119
 * \internal
120
 */
121
static void StatsAccountLQ(
122
  /*! . */
123
  ThreadPool *tp,
124
  /*! . */
125
  long diffTime)
126
0
{
127
0
  tp->stats.totalJobsLQ++;
128
0
  tp->stats.totalTimeLQ += (double)diffTime;
129
0
}
130
131
/*!
132
 * \brief
133
 *
134
 * \internal
135
 */
136
static void StatsAccountMQ(
137
  /*! . */
138
  ThreadPool *tp,
139
  /*! . */
140
  long diffTime)
141
0
{
142
0
  tp->stats.totalJobsMQ++;
143
0
  tp->stats.totalTimeMQ += (double)diffTime;
144
0
}
145
146
/*!
147
 * \brief
148
 *
149
 * \internal
150
 */
151
static void StatsAccountHQ(
152
  /*! . */
153
  ThreadPool *tp,
154
  /*! . */
155
  long diffTime)
156
0
{
157
0
  tp->stats.totalJobsHQ++;
158
0
  tp->stats.totalTimeHQ += (double)diffTime;
159
0
}
160
161
/*!
162
 * \brief Calculates the time the job has been waiting at the specified
163
 * priority.
164
 *
165
 * Adds to the totalTime and totalJobs kept in the thread pool statistics
166
 * structure.
167
 *
168
 * \internal
169
 */
170
static void CalcWaitTime(
171
  /*! . */
172
  ThreadPool *tp,
173
  /*! . */
174
  ThreadPriority p,
175
  /*! . */
176
  ThreadPoolJob *job)
177
0
{
178
0
  struct timeval now;
179
0
  long diff;
180
181
0
  assert(tp != NULL);
182
0
  assert(job != NULL);
183
184
0
  gettimeofday(&now, NULL);
185
0
  diff = DiffMillis(&now, &job->requestTime);
186
0
  switch (p) {
187
0
  case LOW_PRIORITY:
188
0
    StatsAccountLQ(tp, diff);
189
0
    break;
190
0
  case MED_PRIORITY:
191
0
    StatsAccountMQ(tp, diff);
192
0
    break;
193
0
  case HIGH_PRIORITY:
194
0
    StatsAccountHQ(tp, diff);
195
0
    break;
196
0
  default:
197
0
    assert(0);
198
0
  }
199
0
}
200
201
/*!
202
 * \brief
203
 *
204
 * \internal
205
 */
206
static time_t StatsTime(
207
  /*! . */
208
  time_t *t)
209
0
{
210
0
  struct timeval tv;
211
212
0
  gettimeofday(&tv, NULL);
213
0
  if (t)
214
0
    *t = tv.tv_sec;
215
216
0
  return tv.tv_sec;
217
0
}
218
#else  /* STATS */
219
static UPNP_INLINE void StatsInit(ThreadPoolStats *stats) {}
220
static UPNP_INLINE void StatsAccountLQ(ThreadPool *tp, long diffTime) {}
221
static UPNP_INLINE void StatsAccountMQ(ThreadPool *tp, long diffTime) {}
222
static UPNP_INLINE void StatsAccountHQ(ThreadPool *tp, long diffTime) {}
223
static UPNP_INLINE void CalcWaitTime(
224
  ThreadPool *tp, ThreadPriority p, ThreadPoolJob *job)
225
{
226
}
227
static UPNP_INLINE time_t StatsTime(time_t *t) { return 0; }
228
#endif /* STATS */
229
230
/*!
231
 * \brief Compares thread pool jobs.
232
 *
233
 * \internal
234
 */
235
static int CmpThreadPoolJob(void *jobA, void *jobB)
236
0
{
237
0
  ThreadPoolJob *a = (ThreadPoolJob *)jobA;
238
0
  ThreadPoolJob *b = (ThreadPoolJob *)jobB;
239
240
0
  return a->jobId == b->jobId;
241
0
}
242
243
/*!
244
 * \brief Deallocates a dynamically allocated ThreadPoolJob.
245
 *
246
 * \internal
247
 */
248
static void FreeThreadPoolJob(
249
  /*! . */
250
  ThreadPool *tp,
251
  /*! Must be allocated with CreateThreadPoolJob. */
252
  ThreadPoolJob *tpj)
253
0
{
254
0
  FreeListFree(&tp->jobFreeList, tpj);
255
0
}
256
257
/*!
258
 * \brief Sets the scheduling policy of the current process.
259
 *
260
 * \internal
261
 *
262
 * \return
263
 *  \li \c 0 on success.
264
 *      \li \c result of GetLastError() on failure.
265
 *
266
 */
267
static int SetPolicyType(
268
  /*! . */
269
  PolicyType in)
270
0
{
271
0
  int retVal = 0;
272
#ifdef __CYGWIN__
273
  /* TODO not currently working... */
274
  (void)in;
275
  retVal = 0;
276
#elif defined(__APPLE__) || defined(__NetBSD__)
277
  (void)in;
278
  setpriority(PRIO_PROCESS, 0, 0);
279
  retVal = 0;
280
#elif defined(__PTW32_DLLPORT)
281
  retVal = sched_setscheduler(0, in);
282
#elif defined(_POSIX_PRIORITY_SCHEDULING) && _POSIX_PRIORITY_SCHEDULING > 0
283
  struct sched_param current;
284
0
  int sched_result;
285
286
0
  memset(&current, 0, sizeof(current));
287
0
  sched_getparam(0, &current);
288
0
  current.sched_priority = sched_get_priority_min(DEFAULT_POLICY);
289
0
  sched_result = sched_setscheduler(0, in, &current);
290
0
  retVal = (sched_result != -1 || errno == EPERM) ? 0 : errno;
291
#else
292
  retVal = 0;
293
#endif
294
0
  return retVal;
295
0
}
296
297
/*!
298
 * \brief Sets the priority of the currently running thread.
299
 *
300
 * \internal
301
 *
302
 * \return
303
 *  \li \c 0 on success.
304
 *      \li \c EINVAL invalid priority or the result of GerLastError.
305
 */
306
static int SetPriority(
307
  /*! . */
308
  ThreadPriority priority)
309
0
{
310
0
#if defined(_POSIX_PRIORITY_SCHEDULING) && _POSIX_PRIORITY_SCHEDULING > 0
311
0
  int retVal = 0;
312
0
  int currentPolicy;
313
0
  int minPriority = 0;
314
0
  int maxPriority = 0;
315
0
  int actPriority = 0;
316
0
  int midPriority = 0;
317
0
  struct sched_param newPriority;
318
0
  int sched_result;
319
320
0
  pthread_getschedparam(ithread_self(), &currentPolicy, &newPriority);
321
0
  minPriority = sched_get_priority_min(currentPolicy);
322
0
  maxPriority = sched_get_priority_max(currentPolicy);
323
0
  midPriority = (maxPriority - minPriority) / 2;
324
0
  switch (priority) {
325
0
  case LOW_PRIORITY:
326
0
    actPriority = minPriority;
327
0
    break;
328
0
  case MED_PRIORITY:
329
0
    actPriority = midPriority;
330
0
    break;
331
0
  case HIGH_PRIORITY:
332
0
    actPriority = maxPriority;
333
0
    break;
334
0
  default:
335
0
    retVal = EINVAL;
336
0
    goto exit_function;
337
0
  }
338
339
0
  newPriority.sched_priority = actPriority;
340
341
0
  sched_result = pthread_setschedparam(
342
0
    ithread_self(), currentPolicy, &newPriority);
343
0
  retVal = (sched_result == 0 || errno == EPERM) ? 0 : sched_result;
344
0
exit_function:
345
0
  return retVal;
346
#else
347
  (void)priority;
348
  return 0;
349
#endif
350
0
}
351
352
/*!
353
 * \brief Determines whether any jobs need to be bumped to a higher priority Q
354
 * and bumps them.
355
 *
356
 * tp->mutex must be locked.
357
 *
358
 * \internal
359
 *
360
 * \return
361
 */
362
static void BumpPriority(
363
  /*! . */
364
  ThreadPool *tp)
365
0
{
366
0
  int done = 0;
367
0
  struct timeval now;
368
0
  long diffTime = 0;
369
0
  ThreadPoolJob *tempJob = NULL;
370
371
0
  gettimeofday(&now, NULL);
372
0
  while (!done) {
373
0
    if (tp->medJobQ.size) {
374
0
      tempJob = (ThreadPoolJob *)tp->medJobQ.head.next->item;
375
0
      diffTime = DiffMillis(&now, &tempJob->requestTime);
376
0
      if (diffTime >= tp->attr.starvationTime) {
377
        /* If job has waited longer than the starvation
378
         * time
379
         * bump priority (add to higher priority Q) */
380
0
        StatsAccountMQ(tp, diffTime);
381
0
        ListDelNode(
382
0
          &tp->medJobQ, tp->medJobQ.head.next, 0);
383
0
        ListAddTail(&tp->highJobQ, tempJob);
384
0
        continue;
385
0
      }
386
0
    }
387
0
    if (tp->lowJobQ.size) {
388
0
      tempJob = (ThreadPoolJob *)tp->lowJobQ.head.next->item;
389
0
      diffTime = DiffMillis(&now, &tempJob->requestTime);
390
0
      if (diffTime >= tp->attr.maxIdleTime) {
391
        /* If job has waited longer than the starvation
392
         * time
393
         * bump priority (add to higher priority Q) */
394
0
        StatsAccountLQ(tp, diffTime);
395
0
        ListDelNode(
396
0
          &tp->lowJobQ, tp->lowJobQ.head.next, 0);
397
0
        ListAddTail(&tp->medJobQ, tempJob);
398
0
        continue;
399
0
      }
400
0
    }
401
0
    done = 1;
402
0
  }
403
0
}
404
405
/*!
406
 * \brief Sets the fields of the passed in timespec to be relMillis
407
 * milliseconds in the future.
408
 *
409
 * \internal
410
 */
411
static void SetRelTimeout(
412
  /*! . */
413
  struct timespec *time,
414
  /*! milliseconds in the future. */
415
  int relMillis)
416
0
{
417
0
  struct timeval now;
418
0
  int sec = relMillis / 1000;
419
0
  int milliSeconds = relMillis % 1000;
420
421
0
  gettimeofday(&now, NULL);
422
0
  time->tv_sec = now.tv_sec + sec;
423
0
  time->tv_nsec = (now.tv_usec / 1000 + milliSeconds) * 1000000;
424
0
}
425
426
/*!
427
 * \brief Sets seed for random number generator. Each thread sets the seed
428
 * random number generator.
429
 *
430
 * \internal
431
 */
432
static void SetSeed(void)
433
0
{
434
0
  struct timeval t;
435
436
0
  gettimeofday(&t, NULL);
437
#if defined(__PTW32_DLLPORT)
438
  srand((unsigned int)t.tv_usec +
439
    PtrToUint(ithread_get_current_thread_id().p));
440
#elif defined(BSD) || defined(__APPLE__) || defined(__FreeBSD_kernel__)
441
  srand((unsigned int)t.tv_usec +
442
    (unsigned int)(unsigned long)ithread_get_current_thread_id());
443
#elif defined(__linux__) || defined(__sun) || defined(__CYGWIN__) || \
444
  defined(__GLIBC__)
445
  srand((unsigned int)t.tv_usec +
446
0
    (unsigned int)ithread_get_current_thread_id());
447
#else
448
  {
449
    volatile union
450
    {
451
      volatile pthread_t tid;
452
      volatile unsigned i;
453
    } idu;
454
455
    idu.tid = ithread_get_current_thread_id();
456
    srand((unsigned int)t.tv_usec + idu.i);
457
  }
458
#endif
459
0
}
460
461
/*!
462
 * \brief Implements a thread pool worker. Worker waits for a job to become
463
 * available. Worker picks up persistent jobs first, high priority,
464
 * med priority, then low priority.
465
 *
466
 * If worker remains idle for more than specified max, the worker is released.
467
 *
468
 * \internal
469
 */
470
static void *WorkerThread(
471
  /*! arg -> is cast to (ThreadPool *). */
472
  void *arg)
473
0
{
474
0
  time_t start = 0;
475
476
0
  ThreadPoolJob *job = NULL;
477
0
  ListNode *head = NULL;
478
479
0
  struct timespec timeout;
480
0
  int retCode = 0;
481
0
  int persistent = -1;
482
0
  ThreadPool *tp = (ThreadPool *)arg;
483
484
0
  ithread_initialize_thread();
485
486
  /* Increment total thread count */
487
0
  ithread_mutex_lock(&tp->mutex);
488
0
  tp->totalThreads++;
489
0
  tp->pendingWorkerThreadStart = 0;
490
0
  ithread_cond_broadcast(&tp->start_and_shutdown);
491
0
  ithread_mutex_unlock(&tp->mutex);
492
493
0
  SetSeed();
494
0
  StatsTime(&start);
495
0
  while (1) {
496
0
    ithread_mutex_lock(&tp->mutex);
497
0
    if (job) {
498
0
      tp->busyThreads--;
499
0
      FreeThreadPoolJob(tp, job);
500
0
      job = NULL;
501
0
    }
502
0
    retCode = 0;
503
0
    tp->stats.idleThreads++;
504
0
    tp->stats.totalWorkTime +=
505
0
      (double)(StatsTime(NULL)) - (double)start;
506
0
    StatsTime(&start);
507
0
    if (persistent == 0) {
508
0
      tp->stats.workerThreads--;
509
0
    } else if (persistent == 1) {
510
      /* Persistent thread becomes a regular thread */
511
0
      tp->persistentThreads--;
512
0
    }
513
514
    /* Check for a job or shutdown */
515
0
    while (tp->lowJobQ.size == 0 && tp->medJobQ.size == 0 &&
516
0
      tp->highJobQ.size == 0 && !tp->persistentJob &&
517
0
      !tp->shutdown) {
518
      /* If wait timed out and we currently have more than the
519
       * min threads, or if we have more than the max threads
520
       * (only possible if the attributes have been reset)
521
       * let this thread die. */
522
0
      if ((retCode == ETIMEDOUT &&
523
0
            tp->totalThreads > tp->attr.minThreads) ||
524
0
        (tp->attr.maxThreads != -1 &&
525
0
          tp->totalThreads >
526
0
            tp->attr.maxThreads)) {
527
0
        tp->stats.idleThreads--;
528
0
        goto exit_function;
529
0
      }
530
0
      SetRelTimeout(&timeout, tp->attr.maxIdleTime);
531
532
      /* wait for a job up to the specified max time */
533
0
      retCode = ithread_cond_timedwait(
534
0
        &tp->condition, &tp->mutex, &timeout);
535
0
    }
536
0
    tp->stats.idleThreads--;
537
    /* idle time */
538
0
    tp->stats.totalIdleTime +=
539
0
      (double)(StatsTime(NULL)) - (double)start;
540
    /* work time */
541
0
    StatsTime(&start);
542
    /* bump priority of starved jobs */
543
0
    BumpPriority(tp);
544
    /* if shutdown then stop */
545
0
    if (tp->shutdown) {
546
0
      goto exit_function;
547
0
    } else {
548
      /* Pick up persistent job if available */
549
0
      if (tp->persistentJob) {
550
0
        job = tp->persistentJob;
551
0
        tp->persistentJob = NULL;
552
0
        tp->persistentThreads++;
553
0
        persistent = 1;
554
0
        ithread_cond_broadcast(&tp->start_and_shutdown);
555
0
      } else {
556
0
        tp->stats.workerThreads++;
557
0
        persistent = 0;
558
        /* Pick the highest priority job */
559
0
        if (tp->highJobQ.size > 0) {
560
0
          head = ListHead(&tp->highJobQ);
561
0
          if (head == NULL) {
562
0
            tp->stats.workerThreads--;
563
0
            goto exit_function;
564
0
          }
565
0
          job = (ThreadPoolJob *)head->item;
566
0
          CalcWaitTime(tp, HIGH_PRIORITY, job);
567
0
          ListDelNode(&tp->highJobQ, head, 0);
568
0
        } else if (tp->medJobQ.size > 0) {
569
0
          head = ListHead(&tp->medJobQ);
570
0
          if (head == NULL) {
571
0
            tp->stats.workerThreads--;
572
0
            goto exit_function;
573
0
          }
574
0
          job = (ThreadPoolJob *)head->item;
575
0
          CalcWaitTime(tp, MED_PRIORITY, job);
576
0
          ListDelNode(&tp->medJobQ, head, 0);
577
0
        } else if (tp->lowJobQ.size > 0) {
578
0
          head = ListHead(&tp->lowJobQ);
579
0
          if (head == NULL) {
580
0
            tp->stats.workerThreads--;
581
0
            goto exit_function;
582
0
          }
583
0
          job = (ThreadPoolJob *)head->item;
584
0
          CalcWaitTime(tp, LOW_PRIORITY, job);
585
0
          ListDelNode(&tp->lowJobQ, head, 0);
586
0
        } else {
587
          /* Should never get here */
588
0
          tp->stats.workerThreads--;
589
0
          goto exit_function;
590
0
        }
591
0
      }
592
0
    }
593
594
0
    tp->busyThreads++;
595
0
    ithread_mutex_unlock(&tp->mutex);
596
597
    /* In the future can log info */
598
0
    if (SetPriority(job->priority) != 0) {
599
0
    } else {
600
0
    }
601
    /* run the job */
602
0
    job->func(job->arg);
603
    /* return to Normal */
604
0
    SetPriority(DEFAULT_PRIORITY);
605
0
  }
606
607
0
exit_function:
608
0
  tp->totalThreads--;
609
0
  ithread_cond_broadcast(&tp->start_and_shutdown);
610
0
  ithread_mutex_unlock(&tp->mutex);
611
0
  ithread_cleanup_thread();
612
613
0
  return NULL;
614
0
}
615
616
/*!
617
 * \brief Creates a Thread Pool Job. (Dynamically allocated)
618
 *
619
 * \internal
620
 *
621
 * \return ThreadPoolJob *on success, NULL on failure.
622
 */
623
static ThreadPoolJob *CreateThreadPoolJob(
624
  /*! job is copied. */
625
  ThreadPoolJob *job,
626
  /*! id of job. */
627
  int id,
628
  /*! . */
629
  ThreadPool *tp)
630
0
{
631
0
  ThreadPoolJob *newJob = NULL;
632
633
0
  newJob = (ThreadPoolJob *)FreeListAlloc(&tp->jobFreeList);
634
0
  if (newJob) {
635
0
    *newJob = *job;
636
0
    newJob->jobId = id;
637
0
    gettimeofday(&newJob->requestTime, NULL);
638
0
  }
639
640
0
  return newJob;
641
0
}
642
643
/*!
644
 * \brief Creates a worker thread, if the thread pool does not already have
645
 * max threads.
646
 *
647
 * \remark The ThreadPool object mutex must be locked prior to calling this
648
 * function.
649
 *
650
 * \internal
651
 *
652
 * \return
653
 *  \li \c 0 on success, < 0 on failure.
654
 *  \li \c EMAXTHREADS if already max threads reached.
655
 *  \li \c EAGAIN if system can not create thread.
656
 */
657
static int CreateWorker(
658
  /*! A pointer to the ThreadPool object. */
659
  ThreadPool *tp)
660
0
{
661
0
  ithread_t temp;
662
0
  int rc = 0;
663
0
  ithread_attr_t attr;
664
665
  /* if a new worker is the process of starting, wait until it fully
666
   * starts */
667
0
  while (tp->pendingWorkerThreadStart) {
668
0
    ithread_cond_wait(&tp->start_and_shutdown, &tp->mutex);
669
0
  }
670
671
0
  if (tp->attr.maxThreads != INFINITE_THREADS &&
672
0
    tp->totalThreads + 1 > tp->attr.maxThreads) {
673
0
    return EMAXTHREADS;
674
0
  }
675
0
  ithread_attr_init(&attr);
676
0
  ithread_attr_setstacksize(&attr, tp->attr.stackSize);
677
0
  ithread_attr_setdetachstate(&attr, ITHREAD_CREATE_DETACHED);
678
0
  rc = ithread_create(&temp, &attr, WorkerThread, tp);
679
0
  ithread_attr_destroy(&attr);
680
0
  if (rc == 0) {
681
0
    tp->pendingWorkerThreadStart = 1;
682
    /* wait until the new worker thread starts */
683
0
    while (tp->pendingWorkerThreadStart) {
684
0
      ithread_cond_wait(&tp->start_and_shutdown, &tp->mutex);
685
0
    }
686
0
  }
687
0
  if (tp->stats.maxThreads < tp->totalThreads) {
688
0
    tp->stats.maxThreads = tp->totalThreads;
689
0
  }
690
691
0
  return rc;
692
0
}
693
694
/*!
695
 * \brief Determines whether or not a thread should be added based on the
696
 * jobsPerThread ratio. Adds a thread if appropriate.
697
 *
698
 * \remark The ThreadPool object mutex must be locked prior to calling this
699
 * function.
700
 *
701
 * \internal
702
 */
703
static void AddWorker(
704
  /*! A pointer to the ThreadPool object. */
705
  ThreadPool *tp)
706
0
{
707
0
  long jobs = 0;
708
0
  int threads = 0;
709
710
0
  jobs = tp->highJobQ.size + tp->lowJobQ.size + tp->medJobQ.size;
711
0
  threads = tp->totalThreads - tp->persistentThreads;
712
0
  while (threads == 0 || (jobs / threads) >= tp->attr.jobsPerThread ||
713
0
    (tp->totalThreads == tp->busyThreads)) {
714
0
    if (CreateWorker(tp) != 0) {
715
0
      return;
716
0
    }
717
0
    threads++;
718
0
  }
719
0
}
720
721
int ThreadPoolInit(ThreadPool *tp, ThreadPoolAttr *attr)
722
0
{
723
0
  int retCode = 0;
724
0
  int i = 0;
725
726
0
  if (!tp) {
727
0
    return EINVAL;
728
0
  }
729
730
0
  retCode += ithread_mutex_init(&tp->mutex, NULL);
731
0
  retCode += ithread_mutex_lock(&tp->mutex);
732
733
0
  retCode += ithread_cond_init(&tp->condition, NULL);
734
0
  retCode += ithread_cond_init(&tp->start_and_shutdown, NULL);
735
0
  if (retCode) {
736
0
    ithread_mutex_unlock(&tp->mutex);
737
0
    ithread_mutex_destroy(&tp->mutex);
738
0
    ithread_cond_destroy(&tp->condition);
739
0
    ithread_cond_destroy(&tp->start_and_shutdown);
740
0
    return EAGAIN;
741
0
  }
742
0
  if (attr) {
743
0
    tp->attr = *attr;
744
0
  } else {
745
0
    TPAttrInit(&tp->attr);
746
0
  }
747
0
  if (SetPolicyType(tp->attr.schedPolicy) != 0) {
748
0
    ithread_mutex_unlock(&tp->mutex);
749
0
    ithread_mutex_destroy(&tp->mutex);
750
0
    ithread_cond_destroy(&tp->condition);
751
0
    ithread_cond_destroy(&tp->start_and_shutdown);
752
753
0
    return INVALID_POLICY;
754
0
  }
755
0
  retCode += FreeListInit(
756
0
    &tp->jobFreeList, sizeof(ThreadPoolJob), JOBFREELISTSIZE);
757
0
  StatsInit(&tp->stats);
758
0
  tp->stats.droppedJobs = 0;
759
0
  retCode += ListInit(&tp->highJobQ, CmpThreadPoolJob, NULL);
760
0
  retCode += ListInit(&tp->medJobQ, CmpThreadPoolJob, NULL);
761
0
  retCode += ListInit(&tp->lowJobQ, CmpThreadPoolJob, NULL);
762
0
  if (retCode) {
763
0
    retCode = EAGAIN;
764
0
  } else {
765
0
    tp->persistentJob = NULL;
766
0
    tp->lastJobId = 0;
767
0
    tp->shutdown = 0;
768
0
    tp->totalThreads = 0;
769
0
    tp->busyThreads = 0;
770
0
    tp->persistentThreads = 0;
771
0
    tp->pendingWorkerThreadStart = 0;
772
0
    for (i = 0; i < tp->attr.minThreads; ++i) {
773
0
      retCode = CreateWorker(tp);
774
0
      if (retCode) {
775
0
        break;
776
0
      }
777
0
    }
778
0
  }
779
780
0
  ithread_mutex_unlock(&tp->mutex);
781
782
0
  if (retCode) {
783
    /* clean up if the min threads could not be created */
784
0
    ThreadPoolShutdown(tp);
785
0
  }
786
787
0
  return retCode;
788
0
}
789
790
int ThreadPoolAddPersistent(ThreadPool *tp, ThreadPoolJob *job, int *jobId)
791
0
{
792
0
  int ret = 0;
793
0
  int tempId = -1;
794
0
  ThreadPoolJob *temp = NULL;
795
796
0
  if (!tp || !job) {
797
0
    return EINVAL;
798
0
  }
799
0
  if (!jobId) {
800
0
    jobId = &tempId;
801
0
  }
802
0
  *jobId = INVALID_JOB_ID;
803
804
0
  ithread_mutex_lock(&tp->mutex);
805
806
  /* Create A worker if less than max threads running */
807
0
  if (tp->totalThreads < tp->attr.maxThreads) {
808
0
    CreateWorker(tp);
809
0
  } else {
810
    /* if there is more than one worker thread
811
     * available then schedule job, otherwise fail */
812
0
    if (tp->totalThreads - tp->persistentThreads - 1 == 0) {
813
0
      ret = EMAXTHREADS;
814
0
      goto exit_function;
815
0
    }
816
0
  }
817
0
  temp = CreateThreadPoolJob(job, tp->lastJobId, tp);
818
0
  if (!temp) {
819
0
    ret = EOUTOFMEM;
820
0
    goto exit_function;
821
0
  }
822
0
  tp->persistentJob = temp;
823
824
  /* Notify a waiting thread */
825
0
  ithread_cond_signal(&tp->condition);
826
827
  /* wait until long job has been picked up */
828
0
  while (tp->persistentJob)
829
0
    ithread_cond_wait(&tp->start_and_shutdown, &tp->mutex);
830
0
  *jobId = tp->lastJobId++;
831
832
0
exit_function:
833
0
  ithread_mutex_unlock(&tp->mutex);
834
835
0
  return ret;
836
0
}
837
838
int ThreadPoolAdd(ThreadPool *tp, ThreadPoolJob *job, int *jobId)
839
0
{
840
0
  int rc = EOUTOFMEM;
841
0
  int tempId = -1;
842
0
  long totalJobs;
843
0
  ThreadPoolJob *temp = NULL;
844
845
0
  if (!tp || !job)
846
0
    return EINVAL;
847
848
0
  ithread_mutex_lock(&tp->mutex);
849
850
0
  totalJobs = tp->highJobQ.size + tp->lowJobQ.size + tp->medJobQ.size;
851
0
  if (totalJobs >= tp->attr.maxJobsTotal) {
852
0
    if (tp->stats.droppedJobs == 0) {
853
0
      fprintf(stderr,
854
0
        "libupnp ThreadPoolAdd too many jobs: %ld"
855
0
        " (dropping; further messages suppressed)\n",
856
0
        totalJobs);
857
0
#ifdef HAVE_BACKTRACE
858
0
      {
859
0
        void *frames[20];
860
0
        int n = backtrace(frames, 20);
861
0
        backtrace_symbols_fd(frames, n, STDERR_FILENO);
862
0
      }
863
0
#endif
864
0
    }
865
0
    tp->stats.droppedJobs++;
866
0
    goto exit_function;
867
0
  }
868
0
  if (tp->stats.droppedJobs > 0) {
869
0
    fprintf(stderr,
870
0
      "libupnp ThreadPoolAdd: queue available again,"
871
0
      " %ld job(s) dropped\n",
872
0
      tp->stats.droppedJobs);
873
0
    tp->stats.droppedJobs = 0;
874
0
  }
875
0
  if (!jobId)
876
0
    jobId = &tempId;
877
0
  *jobId = INVALID_JOB_ID;
878
0
  temp = CreateThreadPoolJob(job, tp->lastJobId, tp);
879
0
  if (!temp)
880
0
    goto exit_function;
881
0
  switch (job->priority) {
882
0
  case HIGH_PRIORITY:
883
0
    if (ListAddTail(&tp->highJobQ, temp))
884
0
      rc = 0;
885
0
    break;
886
0
  case MED_PRIORITY:
887
0
    if (ListAddTail(&tp->medJobQ, temp))
888
0
      rc = 0;
889
0
    break;
890
0
  default:
891
0
    if (ListAddTail(&tp->lowJobQ, temp))
892
0
      rc = 0;
893
0
  }
894
  /* AddWorker if appropriate */
895
0
  AddWorker(tp);
896
  /* Notify a waiting thread */
897
0
  if (rc == 0)
898
0
    ithread_cond_signal(&tp->condition);
899
0
  else
900
0
    FreeThreadPoolJob(tp, temp);
901
0
  *jobId = tp->lastJobId++;
902
903
0
exit_function:
904
0
  ithread_mutex_unlock(&tp->mutex);
905
906
0
  return rc;
907
0
}
908
909
int ThreadPoolRemove(ThreadPool *tp, int jobId, ThreadPoolJob *out)
910
0
{
911
0
  int ret = INVALID_JOB_ID;
912
0
  ThreadPoolJob *temp = NULL;
913
0
  ListNode *tempNode = NULL;
914
0
  ThreadPoolJob dummy;
915
916
0
  if (!tp)
917
0
    return EINVAL;
918
0
  if (!out)
919
0
    out = &dummy;
920
0
  dummy.jobId = jobId;
921
922
0
  ithread_mutex_lock(&tp->mutex);
923
924
0
  tempNode = ListFind(&tp->highJobQ, NULL, &dummy);
925
0
  if (tempNode) {
926
0
    temp = (ThreadPoolJob *)tempNode->item;
927
0
    *out = *temp;
928
0
    ListDelNode(&tp->highJobQ, tempNode, 0);
929
0
    FreeThreadPoolJob(tp, temp);
930
0
    ret = 0;
931
0
    goto exit_function;
932
0
  }
933
934
0
  tempNode = ListFind(&tp->medJobQ, NULL, &dummy);
935
0
  if (tempNode) {
936
0
    temp = (ThreadPoolJob *)tempNode->item;
937
0
    *out = *temp;
938
0
    ListDelNode(&tp->medJobQ, tempNode, 0);
939
0
    FreeThreadPoolJob(tp, temp);
940
0
    ret = 0;
941
0
    goto exit_function;
942
0
  }
943
0
  tempNode = ListFind(&tp->lowJobQ, NULL, &dummy);
944
0
  if (tempNode) {
945
0
    temp = (ThreadPoolJob *)tempNode->item;
946
0
    *out = *temp;
947
0
    ListDelNode(&tp->lowJobQ, tempNode, 0);
948
0
    FreeThreadPoolJob(tp, temp);
949
0
    ret = 0;
950
0
    goto exit_function;
951
0
  }
952
0
  if (tp->persistentJob && tp->persistentJob->jobId == jobId) {
953
0
    *out = *tp->persistentJob;
954
0
    FreeThreadPoolJob(tp, tp->persistentJob);
955
0
    tp->persistentJob = NULL;
956
0
    ret = 0;
957
0
    goto exit_function;
958
0
  }
959
960
0
exit_function:
961
0
  ithread_mutex_unlock(&tp->mutex);
962
963
0
  return ret;
964
0
}
965
966
int ThreadPoolGetAttr(ThreadPool *tp, ThreadPoolAttr *out)
967
0
{
968
0
  if (!tp || !out)
969
0
    return EINVAL;
970
0
  if (!tp->shutdown)
971
0
    ithread_mutex_lock(&tp->mutex);
972
0
  *out = tp->attr;
973
0
  if (!tp->shutdown)
974
0
    ithread_mutex_unlock(&tp->mutex);
975
976
0
  return 0;
977
0
}
978
979
int ThreadPoolSetAttr(ThreadPool *tp, ThreadPoolAttr *attr)
980
0
{
981
0
  int retCode = 0;
982
0
  ThreadPoolAttr temp;
983
0
  int i = 0;
984
985
0
  if (!tp)
986
0
    return EINVAL;
987
988
0
  ithread_mutex_lock(&tp->mutex);
989
990
0
  if (attr)
991
0
    temp = *attr;
992
0
  else
993
0
    TPAttrInit(&temp);
994
0
  if (SetPolicyType(temp.schedPolicy) != 0) {
995
0
    ithread_mutex_unlock(&tp->mutex);
996
0
    return INVALID_POLICY;
997
0
  }
998
0
  tp->attr = temp;
999
  /* add threads */
1000
0
  if (tp->totalThreads < tp->attr.minThreads) {
1001
0
    for (i = tp->totalThreads; i < tp->attr.minThreads; i++) {
1002
0
      retCode = CreateWorker(tp);
1003
0
      if (retCode != 0) {
1004
0
        break;
1005
0
      }
1006
0
    }
1007
0
  }
1008
  /* signal changes */
1009
0
  ithread_cond_signal(&tp->condition);
1010
1011
0
  ithread_mutex_unlock(&tp->mutex);
1012
1013
0
  if (retCode != 0)
1014
    /* clean up if the min threads could not be created */
1015
0
    ThreadPoolShutdown(tp);
1016
1017
0
  return retCode;
1018
0
}
1019
1020
int ThreadPoolShutdown(ThreadPool *tp)
1021
0
{
1022
0
  ListNode *head = NULL;
1023
0
  ThreadPoolJob *temp = NULL;
1024
1025
0
  if (!tp)
1026
0
    return EINVAL;
1027
0
  ithread_mutex_lock(&tp->mutex);
1028
  /* clean up high priority jobs */
1029
0
  while (tp->highJobQ.size) {
1030
0
    head = ListHead(&tp->highJobQ);
1031
0
    if (head == NULL) {
1032
0
      ithread_mutex_unlock(&tp->mutex);
1033
0
      return EINVAL;
1034
0
    }
1035
0
    temp = (ThreadPoolJob *)head->item;
1036
0
    if (temp->free_func)
1037
0
      temp->free_func(temp->arg);
1038
0
    FreeThreadPoolJob(tp, temp);
1039
0
    ListDelNode(&tp->highJobQ, head, 0);
1040
0
  }
1041
0
  ListDestroy(&tp->highJobQ, 0);
1042
  /* clean up med priority jobs */
1043
0
  while (tp->medJobQ.size) {
1044
0
    head = ListHead(&tp->medJobQ);
1045
0
    if (head == NULL) {
1046
0
      ithread_mutex_unlock(&tp->mutex);
1047
0
      return EINVAL;
1048
0
    }
1049
0
    temp = (ThreadPoolJob *)head->item;
1050
0
    if (temp->free_func)
1051
0
      temp->free_func(temp->arg);
1052
0
    FreeThreadPoolJob(tp, temp);
1053
0
    ListDelNode(&tp->medJobQ, head, 0);
1054
0
  }
1055
0
  ListDestroy(&tp->medJobQ, 0);
1056
  /* clean up low priority jobs */
1057
0
  while (tp->lowJobQ.size) {
1058
0
    head = ListHead(&tp->lowJobQ);
1059
0
    if (head == NULL) {
1060
0
      ithread_mutex_unlock(&tp->mutex);
1061
0
      return EINVAL;
1062
0
    }
1063
0
    temp = (ThreadPoolJob *)head->item;
1064
0
    if (temp->free_func)
1065
0
      temp->free_func(temp->arg);
1066
0
    FreeThreadPoolJob(tp, temp);
1067
0
    ListDelNode(&tp->lowJobQ, head, 0);
1068
0
  }
1069
0
  ListDestroy(&tp->lowJobQ, 0);
1070
  /* clean up long term job */
1071
0
  if (tp->persistentJob) {
1072
0
    temp = tp->persistentJob;
1073
0
    if (temp->free_func)
1074
0
      temp->free_func(temp->arg);
1075
0
    FreeThreadPoolJob(tp, temp);
1076
0
    tp->persistentJob = NULL;
1077
0
  }
1078
  /* signal shutdown */
1079
0
  tp->shutdown = 1;
1080
0
  ithread_cond_broadcast(&tp->condition);
1081
  /* wait for all threads to finish */
1082
0
  while (tp->totalThreads > 0)
1083
0
    ithread_cond_wait(&tp->start_and_shutdown, &tp->mutex);
1084
  /* destroy condition */
1085
0
  while (ithread_cond_destroy(&tp->condition) != 0) {
1086
0
  }
1087
0
  while (ithread_cond_destroy(&tp->start_and_shutdown) != 0) {
1088
0
  }
1089
0
  FreeListDestroy(&tp->jobFreeList);
1090
1091
0
  ithread_mutex_unlock(&tp->mutex);
1092
1093
  /* destroy mutex */
1094
0
  while (ithread_mutex_destroy(&tp->mutex) != 0) {
1095
0
  }
1096
1097
0
  return 0;
1098
0
}
1099
1100
int maxJobsTotal = DEFAULT_MAX_JOBS_TOTAL;
1101
1102
0
void TPSetMaxJobsTotal(int mjt) { maxJobsTotal = mjt; }
1103
1104
int TPAttrInit(ThreadPoolAttr *attr)
1105
0
{
1106
0
  if (!attr)
1107
0
    return EINVAL;
1108
0
  attr->jobsPerThread = DEFAULT_JOBS_PER_THREAD;
1109
0
  attr->maxIdleTime = DEFAULT_IDLE_TIME;
1110
0
  attr->maxThreads = DEFAULT_MAX_THREADS;
1111
0
  attr->minThreads = DEFAULT_MIN_THREADS;
1112
0
  attr->stackSize = DEFAULT_STACK_SIZE;
1113
0
  attr->schedPolicy = DEFAULT_POLICY;
1114
0
  attr->starvationTime = DEFAULT_STARVATION_TIME;
1115
0
  attr->maxJobsTotal = maxJobsTotal;
1116
1117
0
  return 0;
1118
0
}
1119
1120
int TPJobInit(ThreadPoolJob *job, start_routine func, void *arg)
1121
0
{
1122
0
  if (!job || !func)
1123
0
    return EINVAL;
1124
0
  job->func = func;
1125
0
  job->arg = arg;
1126
0
  job->priority = DEFAULT_PRIORITY;
1127
0
  job->free_func = DEFAULT_FREE_ROUTINE;
1128
1129
0
  return 0;
1130
0
}
1131
1132
int TPJobSetPriority(ThreadPoolJob *job, ThreadPriority priority)
1133
0
{
1134
0
  if (!job)
1135
0
    return EINVAL;
1136
0
  switch (priority) {
1137
0
  case LOW_PRIORITY:
1138
0
  case MED_PRIORITY:
1139
0
  case HIGH_PRIORITY:
1140
0
    job->priority = priority;
1141
0
    return 0;
1142
0
  default:
1143
0
    return EINVAL;
1144
0
  }
1145
0
}
1146
1147
int TPJobSetFreeFunction(ThreadPoolJob *job, free_routine func)
1148
0
{
1149
0
  if (!job)
1150
0
    return EINVAL;
1151
0
  job->free_func = func;
1152
1153
0
  return 0;
1154
0
}
1155
1156
int TPAttrSetMaxThreads(ThreadPoolAttr *attr, int maxThreads)
1157
0
{
1158
0
  if (!attr)
1159
0
    return EINVAL;
1160
0
  attr->maxThreads = maxThreads;
1161
1162
0
  return 0;
1163
0
}
1164
1165
int TPAttrSetMinThreads(ThreadPoolAttr *attr, int minThreads)
1166
0
{
1167
0
  if (!attr)
1168
0
    return EINVAL;
1169
0
  attr->minThreads = minThreads;
1170
1171
0
  return 0;
1172
0
}
1173
1174
int TPAttrSetStackSize(ThreadPoolAttr *attr, size_t stackSize)
1175
0
{
1176
0
  if (!attr)
1177
0
    return EINVAL;
1178
0
  attr->stackSize = stackSize;
1179
1180
0
  return 0;
1181
0
}
1182
1183
int TPAttrSetIdleTime(ThreadPoolAttr *attr, int idleTime)
1184
0
{
1185
0
  if (!attr)
1186
0
    return EINVAL;
1187
0
  attr->maxIdleTime = idleTime;
1188
1189
0
  return 0;
1190
0
}
1191
1192
int TPAttrSetJobsPerThread(ThreadPoolAttr *attr, int jobsPerThread)
1193
0
{
1194
0
  if (!attr)
1195
0
    return EINVAL;
1196
0
  attr->jobsPerThread = jobsPerThread;
1197
1198
0
  return 0;
1199
0
}
1200
1201
int TPAttrSetStarvationTime(ThreadPoolAttr *attr, int starvationTime)
1202
0
{
1203
0
  if (!attr)
1204
0
    return EINVAL;
1205
0
  attr->starvationTime = starvationTime;
1206
1207
0
  return 0;
1208
0
}
1209
1210
int TPAttrSetSchedPolicy(ThreadPoolAttr *attr, PolicyType schedPolicy)
1211
0
{
1212
0
  if (!attr)
1213
0
    return EINVAL;
1214
0
  attr->schedPolicy = schedPolicy;
1215
1216
0
  return 0;
1217
0
}
1218
1219
int TPAttrSetMaxJobsTotal(ThreadPoolAttr *attr, int maxJobsTotal)
1220
0
{
1221
0
  if (!attr)
1222
0
    return EINVAL;
1223
0
  attr->maxJobsTotal = maxJobsTotal;
1224
1225
0
  return 0;
1226
0
}
1227
1228
#ifdef STATS
1229
void ThreadPoolPrintStats(ThreadPoolStats *stats)
1230
0
{
1231
0
  if (!stats)
1232
0
    return;
1233
  /* some OSses time_t length may depending on platform, promote it to
1234
   * long for safety */
1235
0
  fprintf(stderr,
1236
0
    "ThreadPoolStats at Time: %ld\n",
1237
0
    (long)StatsTime(NULL));
1238
0
  fprintf(stderr, "High Jobs pending: %d\n", stats->currentJobsHQ);
1239
0
  fprintf(stderr, "Med Jobs Pending: %d\n", stats->currentJobsMQ);
1240
0
  fprintf(stderr, "Low Jobs Pending: %d\n", stats->currentJobsLQ);
1241
0
  fprintf(stderr,
1242
0
    "Average Wait in High Priority Q in milliseconds: %f\n",
1243
0
    stats->avgWaitHQ);
1244
0
  fprintf(stderr,
1245
0
    "Average Wait in Med Priority Q in milliseconds: %f\n",
1246
0
    stats->avgWaitMQ);
1247
0
  fprintf(stderr,
1248
0
    "Averate Wait in Low Priority Q in milliseconds: %f\n",
1249
0
    stats->avgWaitLQ);
1250
0
  fprintf(stderr, "Max Threads Active: %d\n", stats->maxThreads);
1251
0
  fprintf(stderr, "Current Worker Threads: %d\n", stats->workerThreads);
1252
0
  fprintf(stderr,
1253
0
    "Current Persistent Threads: %d\n",
1254
0
    stats->persistentThreads);
1255
0
  fprintf(stderr, "Current Idle Threads: %d\n", stats->idleThreads);
1256
0
  fprintf(stderr, "Total Threads : %d\n", stats->totalThreads);
1257
0
  fprintf(stderr,
1258
0
    "Total Time spent Working in seconds: %f\n",
1259
0
    stats->totalWorkTime);
1260
0
  fprintf(stderr,
1261
0
    "Total Time spent Idle in seconds : %f\n",
1262
0
    stats->totalIdleTime);
1263
0
  fprintf(stderr, "Jobs dropped (queue full): %ld\n", stats->droppedJobs);
1264
0
}
1265
1266
int ThreadPoolGetStats(ThreadPool *tp, ThreadPoolStats *stats)
1267
0
{
1268
0
  if (tp == NULL || stats == NULL)
1269
0
    return EINVAL;
1270
  /* if not shutdown then acquire mutex */
1271
0
  if (!tp->shutdown)
1272
0
    ithread_mutex_lock(&tp->mutex);
1273
1274
0
  *stats = tp->stats;
1275
0
  if (stats->totalJobsHQ > 0)
1276
0
    stats->avgWaitHQ =
1277
0
      stats->totalTimeHQ / (double)stats->totalJobsHQ;
1278
0
  else
1279
0
    stats->avgWaitHQ = 0.0;
1280
0
  if (stats->totalJobsMQ > 0)
1281
0
    stats->avgWaitMQ =
1282
0
      stats->totalTimeMQ / (double)stats->totalJobsMQ;
1283
0
  else
1284
0
    stats->avgWaitMQ = 0.0;
1285
0
  if (stats->totalJobsLQ > 0)
1286
0
    stats->avgWaitLQ =
1287
0
      stats->totalTimeLQ / (double)stats->totalJobsLQ;
1288
0
  else
1289
0
    stats->avgWaitLQ = 0.0;
1290
0
  stats->totalThreads = tp->totalThreads;
1291
0
  stats->persistentThreads = tp->persistentThreads;
1292
0
  stats->currentJobsHQ = (int)ListSize(&tp->highJobQ);
1293
0
  stats->currentJobsLQ = (int)ListSize(&tp->lowJobQ);
1294
0
  stats->currentJobsMQ = (int)ListSize(&tp->medJobQ);
1295
1296
  /* if not shutdown then release mutex */
1297
0
  if (!tp->shutdown)
1298
0
    ithread_mutex_unlock(&tp->mutex);
1299
1300
0
  return 0;
1301
0
}
1302
#endif /* STATS */
1303
1304
#ifdef _WIN32
1305
  #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
1306
    #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
1307
  #else
1308
    #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
1309
  #endif
1310
1311
int gettimeofday(struct timeval *tv, struct timezone *tz)
1312
{
1313
  FILETIME ft;
1314
  unsigned __int64 tmpres = 0;
1315
  static int tzflag;
1316
1317
  if (tv) {
1318
    GetSystemTimeAsFileTime(&ft);
1319
1320
    tmpres |= ft.dwHighDateTime;
1321
    tmpres <<= 32;
1322
    tmpres |= ft.dwLowDateTime;
1323
1324
    /*converting file time to unix epoch*/
1325
    tmpres /= 10; /*convert into microseconds*/
1326
    tmpres -= DELTA_EPOCH_IN_MICROSECS;
1327
    tv->tv_sec = (long)(tmpres / 1000000UL);
1328
    tv->tv_usec = (long)(tmpres % 1000000UL);
1329
  }
1330
  if (tz) {
1331
    if (!tzflag) {
1332
      _tzset();
1333
      tzflag++;
1334
    }
1335
  #ifdef _UCRT
1336
    long itz = 0;
1337
    _get_timezone(&itz);
1338
    tz->tz_minuteswest = (int)(itz / 60);
1339
    _get_daylight(&tz->tz_dsttime);
1340
  #else
1341
    tz->tz_minuteswest = _timezone / 60;
1342
    tz->tz_dsttime = _daylight;
1343
  #endif
1344
  }
1345
1346
  return 0;
1347
}
1348
#endif /* _WIN32 */