/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(¤t, 0, sizeof(current)); |
287 | 0 | sched_getparam(0, ¤t); |
288 | 0 | current.sched_priority = sched_get_priority_min(DEFAULT_POLICY); |
289 | 0 | sched_result = sched_setscheduler(0, in, ¤t); |
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(), ¤tPolicy, &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 */ |