LCOV - app.info - nsprpub/pr/src/misc/prtpool.c

LCOV - code coverage report
| Current view: | directory - nsprpub/pr/src/misc - prtpool.c (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 518 | 0 | 0.0 %
| Date: | 2012-04-07 | Functions: | 22 | 0 | 0.0 %

           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                 : }

Generated by: LCOV version 1.7