1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Netscape Portable Runtime (NSPR).
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1999-2000
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nspr.h"
39 :
40 : /*
41 : * Thread pools
42 : * Thread pools create and manage threads to provide support for
43 : * scheduling jobs onto one or more threads.
44 : *
45 : */
46 : #ifdef OPT_WINNT
47 : #include <windows.h>
48 : #endif
49 :
50 : /*
51 : * worker thread
52 : */
53 : typedef struct wthread {
54 : PRCList links;
55 : PRThread *thread;
56 : } wthread;
57 :
58 : /*
59 : * queue of timer jobs
60 : */
61 : typedef struct timer_jobq {
62 : PRCList list;
63 : PRLock *lock;
64 : PRCondVar *cv;
65 : PRInt32 cnt;
66 : PRCList wthreads;
67 : } timer_jobq;
68 :
69 : /*
70 : * queue of jobs
71 : */
72 : typedef struct tp_jobq {
73 : PRCList list;
74 : PRInt32 cnt;
75 : PRLock *lock;
76 : PRCondVar *cv;
77 : PRCList wthreads;
78 : #ifdef OPT_WINNT
79 : HANDLE nt_completion_port;
80 : #endif
81 : } tp_jobq;
82 :
83 : /*
84 : * queue of IO jobs
85 : */
86 : typedef struct io_jobq {
87 : PRCList list;
88 : PRPollDesc *pollfds;
89 : PRInt32 npollfds;
90 : PRJob **polljobs;
91 : PRLock *lock;
92 : PRInt32 cnt;
93 : PRFileDesc *notify_fd;
94 : PRCList wthreads;
95 : } io_jobq;
96 :
97 : /*
98 : * Threadpool
99 : */
100 : struct PRThreadPool {
101 : PRInt32 init_threads;
102 : PRInt32 max_threads;
103 : PRInt32 current_threads;
104 : PRInt32 idle_threads;
105 : PRUint32 stacksize;
106 : tp_jobq jobq;
107 : io_jobq ioq;
108 : timer_jobq timerq;
109 : PRLock *join_lock; /* used with jobp->join_cv */
110 : PRCondVar *shutdown_cv;
111 : PRBool shutdown;
112 : };
113 :
114 : typedef enum io_op_type
115 : { JOB_IO_READ, JOB_IO_WRITE, JOB_IO_CONNECT, JOB_IO_ACCEPT } io_op_type;
116 :
117 : #ifdef OPT_WINNT
118 : typedef struct NT_notifier {
119 : OVERLAPPED overlapped; /* must be first */
120 : PRJob *jobp;
121 : } NT_notifier;
122 : #endif
123 :
124 : struct PRJob {
125 : PRCList links; /* for linking jobs */
126 : PRBool on_ioq; /* job on ioq */
127 : PRBool on_timerq; /* job on timerq */
128 : PRJobFn job_func;
129 : void *job_arg;
130 : PRCondVar *join_cv;
131 : PRBool join_wait; /* == PR_TRUE, when waiting to join */
132 : PRCondVar *cancel_cv; /* for cancelling IO jobs */
133 : PRBool cancel_io; /* for cancelling IO jobs */
134 : PRThreadPool *tpool; /* back pointer to thread pool */
135 : PRJobIoDesc *iod;
136 : io_op_type io_op;
137 : PRInt16 io_poll_flags;
138 : PRNetAddr *netaddr;
139 : PRIntervalTime timeout; /* relative value */
140 : PRIntervalTime absolute;
141 : #ifdef OPT_WINNT
142 : NT_notifier nt_notifier;
143 : #endif
144 : };
145 :
146 : #define JOB_LINKS_PTR(_qp) \
147 : ((PRJob *) ((char *) (_qp) - offsetof(PRJob, links)))
148 :
149 : #define WTHREAD_LINKS_PTR(_qp) \
150 : ((wthread *) ((char *) (_qp) - offsetof(wthread, links)))
151 :
152 : #define JOINABLE_JOB(_jobp) (NULL != (_jobp)->join_cv)
153 :
154 : #define JOIN_NOTIFY(_jobp) \
155 : PR_BEGIN_MACRO \
156 : PR_Lock(_jobp->tpool->join_lock); \
157 : _jobp->join_wait = PR_FALSE; \
158 : PR_NotifyCondVar(_jobp->join_cv); \
159 : PR_Unlock(_jobp->tpool->join_lock); \
160 : PR_END_MACRO
161 :
162 : #define CANCEL_IO_JOB(jobp) \
163 : PR_BEGIN_MACRO \
164 : jobp->cancel_io = PR_FALSE; \
165 : jobp->on_ioq = PR_FALSE; \
166 : PR_REMOVE_AND_INIT_LINK(&jobp->links); \
167 : tp->ioq.cnt--; \
168 : PR_NotifyCondVar(jobp->cancel_cv); \
169 : PR_END_MACRO
170 :
171 : static void delete_job(PRJob *jobp);
172 : static PRThreadPool * alloc_threadpool(void);
173 : static PRJob * alloc_job(PRBool joinable, PRThreadPool *tp);
174 : static void notify_ioq(PRThreadPool *tp);
175 : static void notify_timerq(PRThreadPool *tp);
176 :
177 : /*
178 : * locks are acquired in the following order
179 : *
180 : * tp->ioq.lock,tp->timerq.lock
181 : * |
182 : * V
183 : * tp->jobq->lock
184 : */
185 :
186 : /*
187 : * worker thread function
188 : */
189 0 : static void wstart(void *arg)
190 : {
191 0 : PRThreadPool *tp = (PRThreadPool *) arg;
192 : PRCList *head;
193 :
194 : /*
195 : * execute jobs until shutdown
196 : */
197 0 : while (!tp->shutdown) {
198 : PRJob *jobp;
199 : #ifdef OPT_WINNT
200 : BOOL rv;
201 : DWORD unused, shutdown;
202 : LPOVERLAPPED olp;
203 :
204 : PR_Lock(tp->jobq.lock);
205 : tp->idle_threads++;
206 : PR_Unlock(tp->jobq.lock);
207 : rv = GetQueuedCompletionStatus(tp->jobq.nt_completion_port,
208 : &unused, &shutdown, &olp, INFINITE);
209 :
210 : PR_ASSERT(rv);
211 : if (shutdown)
212 : break;
213 : jobp = ((NT_notifier *) olp)->jobp;
214 : PR_Lock(tp->jobq.lock);
215 : tp->idle_threads--;
216 : tp->jobq.cnt--;
217 : PR_Unlock(tp->jobq.lock);
218 : #else
219 :
220 0 : PR_Lock(tp->jobq.lock);
221 0 : while (PR_CLIST_IS_EMPTY(&tp->jobq.list) && (!tp->shutdown)) {
222 0 : tp->idle_threads++;
223 0 : PR_WaitCondVar(tp->jobq.cv, PR_INTERVAL_NO_TIMEOUT);
224 0 : tp->idle_threads--;
225 : }
226 0 : if (tp->shutdown) {
227 0 : PR_Unlock(tp->jobq.lock);
228 0 : break;
229 : }
230 0 : head = PR_LIST_HEAD(&tp->jobq.list);
231 : /*
232 : * remove job from queue
233 : */
234 0 : PR_REMOVE_AND_INIT_LINK(head);
235 0 : tp->jobq.cnt--;
236 0 : jobp = JOB_LINKS_PTR(head);
237 0 : PR_Unlock(tp->jobq.lock);
238 : #endif
239 :
240 0 : jobp->job_func(jobp->job_arg);
241 0 : if (!JOINABLE_JOB(jobp)) {
242 0 : delete_job(jobp);
243 : } else {
244 0 : JOIN_NOTIFY(jobp);
245 : }
246 : }
247 0 : PR_Lock(tp->jobq.lock);
248 0 : tp->current_threads--;
249 0 : PR_Unlock(tp->jobq.lock);
250 0 : }
251 :
252 : /*
253 : * add a job to the work queue
254 : */
255 : static void
256 0 : add_to_jobq(PRThreadPool *tp, PRJob *jobp)
257 : {
258 : /*
259 : * add to jobq
260 : */
261 : #ifdef OPT_WINNT
262 : PR_Lock(tp->jobq.lock);
263 : tp->jobq.cnt++;
264 : PR_Unlock(tp->jobq.lock);
265 : /*
266 : * notify worker thread(s)
267 : */
268 : PostQueuedCompletionStatus(tp->jobq.nt_completion_port, 0,
269 : FALSE, &jobp->nt_notifier.overlapped);
270 : #else
271 0 : PR_Lock(tp->jobq.lock);
272 0 : PR_APPEND_LINK(&jobp->links,&tp->jobq.list);
273 0 : tp->jobq.cnt++;
274 0 : if ((tp->idle_threads < tp->jobq.cnt) &&
275 0 : (tp->current_threads < tp->max_threads)) {
276 : wthread *wthrp;
277 : /*
278 : * increment thread count and unlock the jobq lock
279 : */
280 0 : tp->current_threads++;
281 0 : PR_Unlock(tp->jobq.lock);
282 : /* create new worker thread */
283 0 : wthrp = PR_NEWZAP(wthread);
284 0 : if (wthrp) {
285 0 : wthrp->thread = PR_CreateThread(PR_USER_THREAD, wstart,
286 : tp, PR_PRIORITY_NORMAL,
287 : PR_GLOBAL_THREAD,PR_JOINABLE_THREAD,tp->stacksize);
288 0 : if (NULL == wthrp->thread) {
289 0 : PR_DELETE(wthrp); /* this sets wthrp to NULL */
290 : }
291 : }
292 0 : PR_Lock(tp->jobq.lock);
293 0 : if (NULL == wthrp) {
294 0 : tp->current_threads--;
295 : } else {
296 0 : PR_APPEND_LINK(&wthrp->links, &tp->jobq.wthreads);
297 : }
298 : }
299 : /*
300 : * wakeup a worker thread
301 : */
302 0 : PR_NotifyCondVar(tp->jobq.cv);
303 0 : PR_Unlock(tp->jobq.lock);
304 : #endif
305 0 : }
306 :
307 : /*
308 : * io worker thread function
309 : */
310 0 : static void io_wstart(void *arg)
311 : {
312 0 : PRThreadPool *tp = (PRThreadPool *) arg;
313 : int pollfd_cnt, pollfds_used;
314 : int rv;
315 : PRCList *qp, *nextqp;
316 : PRPollDesc *pollfds;
317 : PRJob **polljobs;
318 : int poll_timeout;
319 : PRIntervalTime now;
320 :
321 : /*
322 : * scan io_jobq
323 : * construct poll list
324 : * call PR_Poll
325 : * for all fds, for which poll returns true, move the job to
326 : * jobq and wakeup worker thread.
327 : */
328 0 : while (!tp->shutdown) {
329 : PRJob *jobp;
330 :
331 0 : pollfd_cnt = tp->ioq.cnt + 10;
332 0 : if (pollfd_cnt > tp->ioq.npollfds) {
333 :
334 : /*
335 : * re-allocate pollfd array if the current one is not large
336 : * enough
337 : */
338 0 : if (NULL != tp->ioq.pollfds)
339 0 : PR_Free(tp->ioq.pollfds);
340 0 : tp->ioq.pollfds = (PRPollDesc *) PR_Malloc(pollfd_cnt *
341 : (sizeof(PRPollDesc) + sizeof(PRJob *)));
342 0 : PR_ASSERT(NULL != tp->ioq.pollfds);
343 : /*
344 : * array of pollfds
345 : */
346 0 : pollfds = tp->ioq.pollfds;
347 0 : tp->ioq.polljobs = (PRJob **) (&tp->ioq.pollfds[pollfd_cnt]);
348 : /*
349 : * parallel array of jobs
350 : */
351 0 : polljobs = tp->ioq.polljobs;
352 0 : tp->ioq.npollfds = pollfd_cnt;
353 : }
354 :
355 0 : pollfds_used = 0;
356 : /*
357 : * add the notify fd; used for unblocking io thread(s)
358 : */
359 0 : pollfds[pollfds_used].fd = tp->ioq.notify_fd;
360 0 : pollfds[pollfds_used].in_flags = PR_POLL_READ;
361 0 : pollfds[pollfds_used].out_flags = 0;
362 0 : polljobs[pollfds_used] = NULL;
363 0 : pollfds_used++;
364 : /*
365 : * fill in the pollfd array
366 : */
367 0 : PR_Lock(tp->ioq.lock);
368 0 : for (qp = tp->ioq.list.next; qp != &tp->ioq.list; qp = nextqp) {
369 0 : nextqp = qp->next;
370 0 : jobp = JOB_LINKS_PTR(qp);
371 0 : if (jobp->cancel_io) {
372 0 : CANCEL_IO_JOB(jobp);
373 0 : continue;
374 : }
375 0 : if (pollfds_used == (pollfd_cnt))
376 0 : break;
377 0 : pollfds[pollfds_used].fd = jobp->iod->socket;
378 0 : pollfds[pollfds_used].in_flags = jobp->io_poll_flags;
379 0 : pollfds[pollfds_used].out_flags = 0;
380 0 : polljobs[pollfds_used] = jobp;
381 :
382 0 : pollfds_used++;
383 : }
384 0 : if (!PR_CLIST_IS_EMPTY(&tp->ioq.list)) {
385 0 : qp = tp->ioq.list.next;
386 0 : jobp = JOB_LINKS_PTR(qp);
387 0 : if (PR_INTERVAL_NO_TIMEOUT == jobp->timeout)
388 0 : poll_timeout = PR_INTERVAL_NO_TIMEOUT;
389 0 : else if (PR_INTERVAL_NO_WAIT == jobp->timeout)
390 0 : poll_timeout = PR_INTERVAL_NO_WAIT;
391 : else {
392 0 : poll_timeout = jobp->absolute - PR_IntervalNow();
393 0 : if (poll_timeout <= 0) /* already timed out */
394 0 : poll_timeout = PR_INTERVAL_NO_WAIT;
395 : }
396 : } else {
397 0 : poll_timeout = PR_INTERVAL_NO_TIMEOUT;
398 : }
399 0 : PR_Unlock(tp->ioq.lock);
400 :
401 : /*
402 : * XXXX
403 : * should retry if more jobs have been added to the queue?
404 : *
405 : */
406 0 : PR_ASSERT(pollfds_used <= pollfd_cnt);
407 0 : rv = PR_Poll(tp->ioq.pollfds, pollfds_used, poll_timeout);
408 :
409 0 : if (tp->shutdown) {
410 0 : break;
411 : }
412 :
413 0 : if (rv > 0) {
414 : /*
415 : * at least one io event is set
416 : */
417 : PRStatus rval_status;
418 : PRInt32 index;
419 :
420 0 : PR_ASSERT(pollfds[0].fd == tp->ioq.notify_fd);
421 : /*
422 : * reset the pollable event, if notified
423 : */
424 0 : if (pollfds[0].out_flags & PR_POLL_READ) {
425 0 : rval_status = PR_WaitForPollableEvent(tp->ioq.notify_fd);
426 0 : PR_ASSERT(PR_SUCCESS == rval_status);
427 : }
428 :
429 0 : for(index = 1; index < (pollfds_used); index++) {
430 0 : PRInt16 events = pollfds[index].in_flags;
431 0 : PRInt16 revents = pollfds[index].out_flags;
432 0 : jobp = polljobs[index];
433 :
434 0 : if ((revents & PR_POLL_NVAL) || /* busted in all cases */
435 0 : (revents & PR_POLL_ERR) ||
436 0 : ((events & PR_POLL_WRITE) &&
437 0 : (revents & PR_POLL_HUP))) { /* write op & hup */
438 0 : PR_Lock(tp->ioq.lock);
439 0 : if (jobp->cancel_io) {
440 0 : CANCEL_IO_JOB(jobp);
441 0 : PR_Unlock(tp->ioq.lock);
442 0 : continue;
443 : }
444 0 : PR_REMOVE_AND_INIT_LINK(&jobp->links);
445 0 : tp->ioq.cnt--;
446 0 : jobp->on_ioq = PR_FALSE;
447 0 : PR_Unlock(tp->ioq.lock);
448 :
449 : /* set error */
450 0 : if (PR_POLL_NVAL & revents)
451 0 : jobp->iod->error = PR_BAD_DESCRIPTOR_ERROR;
452 0 : else if (PR_POLL_HUP & revents)
453 0 : jobp->iod->error = PR_CONNECT_RESET_ERROR;
454 : else
455 0 : jobp->iod->error = PR_IO_ERROR;
456 :
457 : /*
458 : * add to jobq
459 : */
460 0 : add_to_jobq(tp, jobp);
461 0 : } else if (revents) {
462 : /*
463 : * add to jobq
464 : */
465 0 : PR_Lock(tp->ioq.lock);
466 0 : if (jobp->cancel_io) {
467 0 : CANCEL_IO_JOB(jobp);
468 0 : PR_Unlock(tp->ioq.lock);
469 0 : continue;
470 : }
471 0 : PR_REMOVE_AND_INIT_LINK(&jobp->links);
472 0 : tp->ioq.cnt--;
473 0 : jobp->on_ioq = PR_FALSE;
474 0 : PR_Unlock(tp->ioq.lock);
475 :
476 0 : if (jobp->io_op == JOB_IO_CONNECT) {
477 0 : if (PR_GetConnectStatus(&pollfds[index]) == PR_SUCCESS)
478 0 : jobp->iod->error = 0;
479 : else
480 0 : jobp->iod->error = PR_GetError();
481 : } else
482 0 : jobp->iod->error = 0;
483 :
484 0 : add_to_jobq(tp, jobp);
485 : }
486 : }
487 : }
488 : /*
489 : * timeout processing
490 : */
491 0 : now = PR_IntervalNow();
492 0 : PR_Lock(tp->ioq.lock);
493 0 : for (qp = tp->ioq.list.next; qp != &tp->ioq.list; qp = nextqp) {
494 0 : nextqp = qp->next;
495 0 : jobp = JOB_LINKS_PTR(qp);
496 0 : if (jobp->cancel_io) {
497 0 : CANCEL_IO_JOB(jobp);
498 0 : continue;
499 : }
500 0 : if (PR_INTERVAL_NO_TIMEOUT == jobp->timeout)
501 0 : break;
502 0 : if ((PR_INTERVAL_NO_WAIT != jobp->timeout) &&
503 0 : ((PRInt32)(jobp->absolute - now) > 0))
504 0 : break;
505 0 : PR_REMOVE_AND_INIT_LINK(&jobp->links);
506 0 : tp->ioq.cnt--;
507 0 : jobp->on_ioq = PR_FALSE;
508 0 : jobp->iod->error = PR_IO_TIMEOUT_ERROR;
509 0 : add_to_jobq(tp, jobp);
510 : }
511 0 : PR_Unlock(tp->ioq.lock);
512 : }
513 0 : }
514 :
515 : /*
516 : * timer worker thread function
517 : */
518 0 : static void timer_wstart(void *arg)
519 : {
520 0 : PRThreadPool *tp = (PRThreadPool *) arg;
521 : PRCList *qp;
522 : PRIntervalTime timeout;
523 : PRIntervalTime now;
524 :
525 : /*
526 : * call PR_WaitCondVar with minimum value of all timeouts
527 : */
528 0 : while (!tp->shutdown) {
529 : PRJob *jobp;
530 :
531 0 : PR_Lock(tp->timerq.lock);
532 0 : if (PR_CLIST_IS_EMPTY(&tp->timerq.list)) {
533 0 : timeout = PR_INTERVAL_NO_TIMEOUT;
534 : } else {
535 : PRCList *qp;
536 :
537 0 : qp = tp->timerq.list.next;
538 0 : jobp = JOB_LINKS_PTR(qp);
539 :
540 0 : timeout = jobp->absolute - PR_IntervalNow();
541 0 : if (timeout <= 0)
542 0 : timeout = PR_INTERVAL_NO_WAIT; /* already timed out */
543 : }
544 0 : if (PR_INTERVAL_NO_WAIT != timeout)
545 0 : PR_WaitCondVar(tp->timerq.cv, timeout);
546 0 : if (tp->shutdown) {
547 0 : PR_Unlock(tp->timerq.lock);
548 0 : break;
549 : }
550 : /*
551 : * move expired-timer jobs to jobq
552 : */
553 0 : now = PR_IntervalNow();
554 0 : while (!PR_CLIST_IS_EMPTY(&tp->timerq.list)) {
555 0 : qp = tp->timerq.list.next;
556 0 : jobp = JOB_LINKS_PTR(qp);
557 :
558 0 : if ((PRInt32)(jobp->absolute - now) > 0) {
559 0 : break;
560 : }
561 : /*
562 : * job timed out
563 : */
564 0 : PR_REMOVE_AND_INIT_LINK(&jobp->links);
565 0 : tp->timerq.cnt--;
566 0 : jobp->on_timerq = PR_FALSE;
567 0 : add_to_jobq(tp, jobp);
568 : }
569 0 : PR_Unlock(tp->timerq.lock);
570 : }
571 0 : }
572 :
573 : static void
574 0 : delete_threadpool(PRThreadPool *tp)
575 : {
576 0 : if (NULL != tp) {
577 0 : if (NULL != tp->shutdown_cv)
578 0 : PR_DestroyCondVar(tp->shutdown_cv);
579 0 : if (NULL != tp->jobq.cv)
580 0 : PR_DestroyCondVar(tp->jobq.cv);
581 0 : if (NULL != tp->jobq.lock)
582 0 : PR_DestroyLock(tp->jobq.lock);
583 0 : if (NULL != tp->join_lock)
584 0 : PR_DestroyLock(tp->join_lock);
585 : #ifdef OPT_WINNT
586 : if (NULL != tp->jobq.nt_completion_port)
587 : CloseHandle(tp->jobq.nt_completion_port);
588 : #endif
589 : /* Timer queue */
590 0 : if (NULL != tp->timerq.cv)
591 0 : PR_DestroyCondVar(tp->timerq.cv);
592 0 : if (NULL != tp->timerq.lock)
593 0 : PR_DestroyLock(tp->timerq.lock);
594 :
595 0 : if (NULL != tp->ioq.lock)
596 0 : PR_DestroyLock(tp->ioq.lock);
597 0 : if (NULL != tp->ioq.pollfds)
598 0 : PR_Free(tp->ioq.pollfds);
599 0 : if (NULL != tp->ioq.notify_fd)
600 0 : PR_DestroyPollableEvent(tp->ioq.notify_fd);
601 0 : PR_Free(tp);
602 : }
603 : return;
604 : }
605 :
606 : static PRThreadPool *
607 0 : alloc_threadpool(void)
608 : {
609 : PRThreadPool *tp;
610 :
611 0 : tp = (PRThreadPool *) PR_CALLOC(sizeof(*tp));
612 0 : if (NULL == tp)
613 0 : goto failed;
614 0 : tp->jobq.lock = PR_NewLock();
615 0 : if (NULL == tp->jobq.lock)
616 0 : goto failed;
617 0 : tp->jobq.cv = PR_NewCondVar(tp->jobq.lock);
618 0 : if (NULL == tp->jobq.cv)
619 0 : goto failed;
620 0 : tp->join_lock = PR_NewLock();
621 0 : if (NULL == tp->join_lock)
622 0 : goto failed;
623 : #ifdef OPT_WINNT
624 : tp->jobq.nt_completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
625 : NULL, 0, 0);
626 : if (NULL == tp->jobq.nt_completion_port)
627 : goto failed;
628 : #endif
629 :
630 0 : tp->ioq.lock = PR_NewLock();
631 0 : if (NULL == tp->ioq.lock)
632 0 : goto failed;
633 :
634 : /* Timer queue */
635 :
636 0 : tp->timerq.lock = PR_NewLock();
637 0 : if (NULL == tp->timerq.lock)
638 0 : goto failed;
639 0 : tp->timerq.cv = PR_NewCondVar(tp->timerq.lock);
640 0 : if (NULL == tp->timerq.cv)
641 0 : goto failed;
642 :
643 0 : tp->shutdown_cv = PR_NewCondVar(tp->jobq.lock);
644 0 : if (NULL == tp->shutdown_cv)
645 0 : goto failed;
646 0 : tp->ioq.notify_fd = PR_NewPollableEvent();
647 0 : if (NULL == tp->ioq.notify_fd)
648 0 : goto failed;
649 0 : return tp;
650 : failed:
651 0 : delete_threadpool(tp);
652 0 : PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
653 0 : return NULL;
654 : }
655 :
656 : /* Create thread pool */
657 : PR_IMPLEMENT(PRThreadPool *)
658 0 : PR_CreateThreadPool(PRInt32 initial_threads, PRInt32 max_threads,
659 : PRUint32 stacksize)
660 : {
661 : PRThreadPool *tp;
662 : PRThread *thr;
663 : int i;
664 : wthread *wthrp;
665 :
666 0 : tp = alloc_threadpool();
667 0 : if (NULL == tp)
668 0 : return NULL;
669 :
670 0 : tp->init_threads = initial_threads;
671 0 : tp->max_threads = max_threads;
672 0 : tp->stacksize = stacksize;
673 0 : PR_INIT_CLIST(&tp->jobq.list);
674 0 : PR_INIT_CLIST(&tp->ioq.list);
675 0 : PR_INIT_CLIST(&tp->timerq.list);
676 0 : PR_INIT_CLIST(&tp->jobq.wthreads);
677 0 : PR_INIT_CLIST(&tp->ioq.wthreads);
678 0 : PR_INIT_CLIST(&tp->timerq.wthreads);
679 0 : tp->shutdown = PR_FALSE;
680 :
681 0 : PR_Lock(tp->jobq.lock);
682 0 : for(i=0; i < initial_threads; ++i) {
683 :
684 0 : thr = PR_CreateThread(PR_USER_THREAD, wstart,
685 : tp, PR_PRIORITY_NORMAL,
686 : PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,stacksize);
687 0 : PR_ASSERT(thr);
688 0 : wthrp = PR_NEWZAP(wthread);
689 0 : PR_ASSERT(wthrp);
690 0 : wthrp->thread = thr;
691 0 : PR_APPEND_LINK(&wthrp->links, &tp->jobq.wthreads);
692 : }
693 0 : tp->current_threads = initial_threads;
694 :
695 0 : thr = PR_CreateThread(PR_USER_THREAD, io_wstart,
696 : tp, PR_PRIORITY_NORMAL,
697 : PR_GLOBAL_THREAD,PR_JOINABLE_THREAD,stacksize);
698 0 : PR_ASSERT(thr);
699 0 : wthrp = PR_NEWZAP(wthread);
700 0 : PR_ASSERT(wthrp);
701 0 : wthrp->thread = thr;
702 0 : PR_APPEND_LINK(&wthrp->links, &tp->ioq.wthreads);
703 :
704 0 : thr = PR_CreateThread(PR_USER_THREAD, timer_wstart,
705 : tp, PR_PRIORITY_NORMAL,
706 : PR_GLOBAL_THREAD,PR_JOINABLE_THREAD,stacksize);
707 0 : PR_ASSERT(thr);
708 0 : wthrp = PR_NEWZAP(wthread);
709 0 : PR_ASSERT(wthrp);
710 0 : wthrp->thread = thr;
711 0 : PR_APPEND_LINK(&wthrp->links, &tp->timerq.wthreads);
712 :
713 0 : PR_Unlock(tp->jobq.lock);
714 0 : return tp;
715 : }
716 :
717 : static void
718 0 : delete_job(PRJob *jobp)
719 : {
720 0 : if (NULL != jobp) {
721 0 : if (NULL != jobp->join_cv) {
722 0 : PR_DestroyCondVar(jobp->join_cv);
723 0 : jobp->join_cv = NULL;
724 : }
725 0 : if (NULL != jobp->cancel_cv) {
726 0 : PR_DestroyCondVar(jobp->cancel_cv);
727 0 : jobp->cancel_cv = NULL;
728 : }
729 0 : PR_DELETE(jobp);
730 : }
731 0 : }
732 :
733 : static PRJob *
734 0 : alloc_job(PRBool joinable, PRThreadPool *tp)
735 : {
736 : PRJob *jobp;
737 :
738 0 : jobp = PR_NEWZAP(PRJob);
739 0 : if (NULL == jobp)
740 0 : goto failed;
741 0 : if (joinable) {
742 0 : jobp->join_cv = PR_NewCondVar(tp->join_lock);
743 0 : jobp->join_wait = PR_TRUE;
744 0 : if (NULL == jobp->join_cv)
745 0 : goto failed;
746 : } else {
747 0 : jobp->join_cv = NULL;
748 : }
749 : #ifdef OPT_WINNT
750 : jobp->nt_notifier.jobp = jobp;
751 : #endif
752 0 : return jobp;
753 : failed:
754 0 : delete_job(jobp);
755 0 : PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
756 0 : return NULL;
757 : }
758 :
759 : /* queue a job */
760 : PR_IMPLEMENT(PRJob *)
761 0 : PR_QueueJob(PRThreadPool *tpool, PRJobFn fn, void *arg, PRBool joinable)
762 : {
763 : PRJob *jobp;
764 :
765 0 : jobp = alloc_job(joinable, tpool);
766 0 : if (NULL == jobp)
767 0 : return NULL;
768 :
769 0 : jobp->job_func = fn;
770 0 : jobp->job_arg = arg;
771 0 : jobp->tpool = tpool;
772 :
773 0 : add_to_jobq(tpool, jobp);
774 0 : return jobp;
775 : }
776 :
777 : /* queue a job, when a socket is readable or writeable */
778 : static PRJob *
779 0 : queue_io_job(PRThreadPool *tpool, PRJobIoDesc *iod, PRJobFn fn, void * arg,
780 : PRBool joinable, io_op_type op)
781 : {
782 : PRJob *jobp;
783 : PRIntervalTime now;
784 :
785 0 : jobp = alloc_job(joinable, tpool);
786 0 : if (NULL == jobp) {
787 0 : return NULL;
788 : }
789 :
790 : /*
791 : * Add a new job to io_jobq
792 : * wakeup io worker thread
793 : */
794 :
795 0 : jobp->job_func = fn;
796 0 : jobp->job_arg = arg;
797 0 : jobp->tpool = tpool;
798 0 : jobp->iod = iod;
799 0 : if (JOB_IO_READ == op) {
800 0 : jobp->io_op = JOB_IO_READ;
801 0 : jobp->io_poll_flags = PR_POLL_READ;
802 0 : } else if (JOB_IO_WRITE == op) {
803 0 : jobp->io_op = JOB_IO_WRITE;
804 0 : jobp->io_poll_flags = PR_POLL_WRITE;
805 0 : } else if (JOB_IO_ACCEPT == op) {
806 0 : jobp->io_op = JOB_IO_ACCEPT;
807 0 : jobp->io_poll_flags = PR_POLL_READ;
808 0 : } else if (JOB_IO_CONNECT == op) {
809 0 : jobp->io_op = JOB_IO_CONNECT;
810 0 : jobp->io_poll_flags = PR_POLL_WRITE|PR_POLL_EXCEPT;
811 : } else {
812 0 : delete_job(jobp);
813 0 : PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
814 0 : return NULL;
815 : }
816 :
817 0 : jobp->timeout = iod->timeout;
818 0 : if ((PR_INTERVAL_NO_TIMEOUT == iod->timeout) ||
819 0 : (PR_INTERVAL_NO_WAIT == iod->timeout)) {
820 0 : jobp->absolute = iod->timeout;
821 : } else {
822 0 : now = PR_IntervalNow();
823 0 : jobp->absolute = now + iod->timeout;
824 : }
825 :
826 :
827 0 : PR_Lock(tpool->ioq.lock);
828 :
829 0 : if (PR_CLIST_IS_EMPTY(&tpool->ioq.list) ||
830 0 : (PR_INTERVAL_NO_TIMEOUT == iod->timeout)) {
831 0 : PR_APPEND_LINK(&jobp->links,&tpool->ioq.list);
832 0 : } else if (PR_INTERVAL_NO_WAIT == iod->timeout) {
833 0 : PR_INSERT_LINK(&jobp->links,&tpool->ioq.list);
834 : } else {
835 : PRCList *qp;
836 : PRJob *tmp_jobp;
837 : /*
838 : * insert into the timeout-sorted ioq
839 : */
840 0 : for (qp = tpool->ioq.list.prev; qp != &tpool->ioq.list;
841 0 : qp = qp->prev) {
842 0 : tmp_jobp = JOB_LINKS_PTR(qp);
843 0 : if ((PRInt32)(jobp->absolute - tmp_jobp->absolute) >= 0) {
844 0 : break;
845 : }
846 : }
847 0 : PR_INSERT_AFTER(&jobp->links,qp);
848 : }
849 :
850 0 : jobp->on_ioq = PR_TRUE;
851 0 : tpool->ioq.cnt++;
852 : /*
853 : * notify io worker thread(s)
854 : */
855 0 : PR_Unlock(tpool->ioq.lock);
856 0 : notify_ioq(tpool);
857 0 : return jobp;
858 : }
859 :
860 : /* queue a job, when a socket is readable */
861 : PR_IMPLEMENT(PRJob *)
862 0 : PR_QueueJob_Read(PRThreadPool *tpool, PRJobIoDesc *iod, PRJobFn fn, void * arg,
863 : PRBool joinable)
864 : {
865 0 : return (queue_io_job(tpool, iod, fn, arg, joinable, JOB_IO_READ));
866 : }
867 :
868 : /* queue a job, when a socket is writeable */
869 : PR_IMPLEMENT(PRJob *)
870 0 : PR_QueueJob_Write(PRThreadPool *tpool, PRJobIoDesc *iod, PRJobFn fn,void * arg,
871 : PRBool joinable)
872 : {
873 0 : return (queue_io_job(tpool, iod, fn, arg, joinable, JOB_IO_WRITE));
874 : }
875 :
876 :
877 : /* queue a job, when a socket has a pending connection */
878 : PR_IMPLEMENT(PRJob *)
879 0 : PR_QueueJob_Accept(PRThreadPool *tpool, PRJobIoDesc *iod, PRJobFn fn,
880 : void * arg, PRBool joinable)
881 : {
882 0 : return (queue_io_job(tpool, iod, fn, arg, joinable, JOB_IO_ACCEPT));
883 : }
884 :
885 : /* queue a job, when a socket can be connected */
886 : PR_IMPLEMENT(PRJob *)
887 0 : PR_QueueJob_Connect(PRThreadPool *tpool, PRJobIoDesc *iod,
888 : const PRNetAddr *addr, PRJobFn fn, void * arg, PRBool joinable)
889 : {
890 : PRStatus rv;
891 : PRErrorCode err;
892 :
893 0 : rv = PR_Connect(iod->socket, addr, PR_INTERVAL_NO_WAIT);
894 0 : if ((rv == PR_FAILURE) && ((err = PR_GetError()) == PR_IN_PROGRESS_ERROR)){
895 : /* connection pending */
896 0 : return(queue_io_job(tpool, iod, fn, arg, joinable, JOB_IO_CONNECT));
897 : } else {
898 : /*
899 : * connection succeeded or failed; add to jobq right away
900 : */
901 0 : if (rv == PR_FAILURE)
902 0 : iod->error = err;
903 : else
904 0 : iod->error = 0;
905 0 : return(PR_QueueJob(tpool, fn, arg, joinable));
906 : }
907 : }
908 :
909 : /* queue a job, when a timer expires */
910 : PR_IMPLEMENT(PRJob *)
911 0 : PR_QueueJob_Timer(PRThreadPool *tpool, PRIntervalTime timeout,
912 : PRJobFn fn, void * arg, PRBool joinable)
913 : {
914 : PRIntervalTime now;
915 : PRJob *jobp;
916 :
917 0 : if (PR_INTERVAL_NO_TIMEOUT == timeout) {
918 0 : PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
919 0 : return NULL;
920 : }
921 0 : if (PR_INTERVAL_NO_WAIT == timeout) {
922 : /*
923 : * no waiting; add to jobq right away
924 : */
925 0 : return(PR_QueueJob(tpool, fn, arg, joinable));
926 : }
927 0 : jobp = alloc_job(joinable, tpool);
928 0 : if (NULL == jobp) {
929 0 : return NULL;
930 : }
931 :
932 : /*
933 : * Add a new job to timer_jobq
934 : * wakeup timer worker thread
935 : */
936 :
937 0 : jobp->job_func = fn;
938 0 : jobp->job_arg = arg;
939 0 : jobp->tpool = tpool;
940 0 : jobp->timeout = timeout;
941 :
942 0 : now = PR_IntervalNow();
943 0 : jobp->absolute = now + timeout;
944 :
945 :
946 0 : PR_Lock(tpool->timerq.lock);
947 0 : jobp->on_timerq = PR_TRUE;
948 0 : if (PR_CLIST_IS_EMPTY(&tpool->timerq.list))
949 0 : PR_APPEND_LINK(&jobp->links,&tpool->timerq.list);
950 : else {
951 : PRCList *qp;
952 : PRJob *tmp_jobp;
953 : /*
954 : * insert into the sorted timer jobq
955 : */
956 0 : for (qp = tpool->timerq.list.prev; qp != &tpool->timerq.list;
957 0 : qp = qp->prev) {
958 0 : tmp_jobp = JOB_LINKS_PTR(qp);
959 0 : if ((PRInt32)(jobp->absolute - tmp_jobp->absolute) >= 0) {
960 0 : break;
961 : }
962 : }
963 0 : PR_INSERT_AFTER(&jobp->links,qp);
964 : }
965 0 : tpool->timerq.cnt++;
966 : /*
967 : * notify timer worker thread(s)
968 : */
969 0 : notify_timerq(tpool);
970 0 : PR_Unlock(tpool->timerq.lock);
971 0 : return jobp;
972 : }
973 :
974 : static void
975 0 : notify_timerq(PRThreadPool *tp)
976 : {
977 : /*
978 : * wakeup the timer thread(s)
979 : */
980 0 : PR_NotifyCondVar(tp->timerq.cv);
981 0 : }
982 :
983 : static void
984 0 : notify_ioq(PRThreadPool *tp)
985 : {
986 : PRStatus rval_status;
987 :
988 : /*
989 : * wakeup the io thread(s)
990 : */
991 0 : rval_status = PR_SetPollableEvent(tp->ioq.notify_fd);
992 0 : PR_ASSERT(PR_SUCCESS == rval_status);
993 0 : }
994 :
995 : /*
996 : * cancel a job
997 : *
998 : * XXXX: is this needed? likely to be removed
999 : */
1000 : PR_IMPLEMENT(PRStatus)
1001 0 : PR_CancelJob(PRJob *jobp) {
1002 :
1003 0 : PRStatus rval = PR_FAILURE;
1004 : PRThreadPool *tp;
1005 :
1006 0 : if (jobp->on_timerq) {
1007 : /*
1008 : * now, check again while holding the timerq lock
1009 : */
1010 0 : tp = jobp->tpool;
1011 0 : PR_Lock(tp->timerq.lock);
1012 0 : if (jobp->on_timerq) {
1013 0 : jobp->on_timerq = PR_FALSE;
1014 0 : PR_REMOVE_AND_INIT_LINK(&jobp->links);
1015 0 : tp->timerq.cnt--;
1016 0 : PR_Unlock(tp->timerq.lock);
1017 0 : if (!JOINABLE_JOB(jobp)) {
1018 0 : delete_job(jobp);
1019 : } else {
1020 0 : JOIN_NOTIFY(jobp);
1021 : }
1022 0 : rval = PR_SUCCESS;
1023 : } else
1024 0 : PR_Unlock(tp->timerq.lock);
1025 0 : } else if (jobp->on_ioq) {
1026 : /*
1027 : * now, check again while holding the ioq lock
1028 : */
1029 0 : tp = jobp->tpool;
1030 0 : PR_Lock(tp->ioq.lock);
1031 0 : if (jobp->on_ioq) {
1032 0 : jobp->cancel_cv = PR_NewCondVar(tp->ioq.lock);
1033 0 : if (NULL == jobp->cancel_cv) {
1034 0 : PR_Unlock(tp->ioq.lock);
1035 0 : PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
1036 0 : return PR_FAILURE;
1037 : }
1038 : /*
1039 : * mark job 'cancelled' and notify io thread(s)
1040 : * XXXX:
1041 : * this assumes there is only one io thread; when there
1042 : * are multiple threads, the io thread processing this job
1043 : * must be notified.
1044 : */
1045 0 : jobp->cancel_io = PR_TRUE;
1046 0 : PR_Unlock(tp->ioq.lock); /* release, reacquire ioq lock */
1047 0 : notify_ioq(tp);
1048 0 : PR_Lock(tp->ioq.lock);
1049 0 : while (jobp->cancel_io)
1050 0 : PR_WaitCondVar(jobp->cancel_cv, PR_INTERVAL_NO_TIMEOUT);
1051 0 : PR_Unlock(tp->ioq.lock);
1052 0 : PR_ASSERT(!jobp->on_ioq);
1053 0 : if (!JOINABLE_JOB(jobp)) {
1054 0 : delete_job(jobp);
1055 : } else {
1056 0 : JOIN_NOTIFY(jobp);
1057 : }
1058 0 : rval = PR_SUCCESS;
1059 : } else
1060 0 : PR_Unlock(tp->ioq.lock);
1061 : }
1062 0 : if (PR_FAILURE == rval)
1063 0 : PR_SetError(PR_INVALID_STATE_ERROR, 0);
1064 0 : return rval;
1065 : }
1066 :
1067 : /* join a job, wait until completion */
1068 : PR_IMPLEMENT(PRStatus)
1069 0 : PR_JoinJob(PRJob *jobp)
1070 : {
1071 0 : if (!JOINABLE_JOB(jobp)) {
1072 0 : PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
1073 0 : return PR_FAILURE;
1074 : }
1075 0 : PR_Lock(jobp->tpool->join_lock);
1076 0 : while(jobp->join_wait)
1077 0 : PR_WaitCondVar(jobp->join_cv, PR_INTERVAL_NO_TIMEOUT);
1078 0 : PR_Unlock(jobp->tpool->join_lock);
1079 0 : delete_job(jobp);
1080 0 : return PR_SUCCESS;
1081 : }
1082 :
1083 : /* shutdown threadpool */
1084 : PR_IMPLEMENT(PRStatus)
1085 0 : PR_ShutdownThreadPool(PRThreadPool *tpool)
1086 : {
1087 0 : PRStatus rval = PR_SUCCESS;
1088 :
1089 0 : PR_Lock(tpool->jobq.lock);
1090 0 : tpool->shutdown = PR_TRUE;
1091 0 : PR_NotifyAllCondVar(tpool->shutdown_cv);
1092 0 : PR_Unlock(tpool->jobq.lock);
1093 :
1094 0 : return rval;
1095 : }
1096 :
1097 : /*
1098 : * join thread pool
1099 : * wait for termination of worker threads
1100 : * reclaim threadpool resources
1101 : */
1102 : PR_IMPLEMENT(PRStatus)
1103 0 : PR_JoinThreadPool(PRThreadPool *tpool)
1104 : {
1105 0 : PRStatus rval = PR_SUCCESS;
1106 : PRCList *head;
1107 : PRStatus rval_status;
1108 :
1109 0 : PR_Lock(tpool->jobq.lock);
1110 0 : while (!tpool->shutdown)
1111 0 : PR_WaitCondVar(tpool->shutdown_cv, PR_INTERVAL_NO_TIMEOUT);
1112 :
1113 : /*
1114 : * wakeup worker threads
1115 : */
1116 : #ifdef OPT_WINNT
1117 : /*
1118 : * post shutdown notification for all threads
1119 : */
1120 : {
1121 : int i;
1122 : for(i=0; i < tpool->current_threads; i++) {
1123 : PostQueuedCompletionStatus(tpool->jobq.nt_completion_port, 0,
1124 : TRUE, NULL);
1125 : }
1126 : }
1127 : #else
1128 0 : PR_NotifyAllCondVar(tpool->jobq.cv);
1129 : #endif
1130 :
1131 : /*
1132 : * wakeup io thread(s)
1133 : */
1134 0 : notify_ioq(tpool);
1135 :
1136 : /*
1137 : * wakeup timer thread(s)
1138 : */
1139 0 : PR_Lock(tpool->timerq.lock);
1140 0 : notify_timerq(tpool);
1141 0 : PR_Unlock(tpool->timerq.lock);
1142 :
1143 0 : while (!PR_CLIST_IS_EMPTY(&tpool->jobq.wthreads)) {
1144 : wthread *wthrp;
1145 :
1146 0 : head = PR_LIST_HEAD(&tpool->jobq.wthreads);
1147 0 : PR_REMOVE_AND_INIT_LINK(head);
1148 0 : PR_Unlock(tpool->jobq.lock);
1149 0 : wthrp = WTHREAD_LINKS_PTR(head);
1150 0 : rval_status = PR_JoinThread(wthrp->thread);
1151 0 : PR_ASSERT(PR_SUCCESS == rval_status);
1152 0 : PR_DELETE(wthrp);
1153 0 : PR_Lock(tpool->jobq.lock);
1154 : }
1155 0 : PR_Unlock(tpool->jobq.lock);
1156 0 : while (!PR_CLIST_IS_EMPTY(&tpool->ioq.wthreads)) {
1157 : wthread *wthrp;
1158 :
1159 0 : head = PR_LIST_HEAD(&tpool->ioq.wthreads);
1160 0 : PR_REMOVE_AND_INIT_LINK(head);
1161 0 : wthrp = WTHREAD_LINKS_PTR(head);
1162 0 : rval_status = PR_JoinThread(wthrp->thread);
1163 0 : PR_ASSERT(PR_SUCCESS == rval_status);
1164 0 : PR_DELETE(wthrp);
1165 : }
1166 :
1167 0 : while (!PR_CLIST_IS_EMPTY(&tpool->timerq.wthreads)) {
1168 : wthread *wthrp;
1169 :
1170 0 : head = PR_LIST_HEAD(&tpool->timerq.wthreads);
1171 0 : PR_REMOVE_AND_INIT_LINK(head);
1172 0 : wthrp = WTHREAD_LINKS_PTR(head);
1173 0 : rval_status = PR_JoinThread(wthrp->thread);
1174 0 : PR_ASSERT(PR_SUCCESS == rval_status);
1175 0 : PR_DELETE(wthrp);
1176 : }
1177 :
1178 : /*
1179 : * Delete queued jobs
1180 : */
1181 0 : while (!PR_CLIST_IS_EMPTY(&tpool->jobq.list)) {
1182 : PRJob *jobp;
1183 :
1184 0 : head = PR_LIST_HEAD(&tpool->jobq.list);
1185 0 : PR_REMOVE_AND_INIT_LINK(head);
1186 0 : jobp = JOB_LINKS_PTR(head);
1187 0 : tpool->jobq.cnt--;
1188 0 : delete_job(jobp);
1189 : }
1190 :
1191 : /* delete io jobs */
1192 0 : while (!PR_CLIST_IS_EMPTY(&tpool->ioq.list)) {
1193 : PRJob *jobp;
1194 :
1195 0 : head = PR_LIST_HEAD(&tpool->ioq.list);
1196 0 : PR_REMOVE_AND_INIT_LINK(head);
1197 0 : tpool->ioq.cnt--;
1198 0 : jobp = JOB_LINKS_PTR(head);
1199 0 : delete_job(jobp);
1200 : }
1201 :
1202 : /* delete timer jobs */
1203 0 : while (!PR_CLIST_IS_EMPTY(&tpool->timerq.list)) {
1204 : PRJob *jobp;
1205 :
1206 0 : head = PR_LIST_HEAD(&tpool->timerq.list);
1207 0 : PR_REMOVE_AND_INIT_LINK(head);
1208 0 : tpool->timerq.cnt--;
1209 0 : jobp = JOB_LINKS_PTR(head);
1210 0 : delete_job(jobp);
1211 : }
1212 :
1213 0 : PR_ASSERT(0 == tpool->jobq.cnt);
1214 0 : PR_ASSERT(0 == tpool->ioq.cnt);
1215 0 : PR_ASSERT(0 == tpool->timerq.cnt);
1216 :
1217 0 : delete_threadpool(tpool);
1218 0 : return rval;
1219 : }
|