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-06-02 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