You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by st...@apache.org on 2003/03/13 20:27:40 UTC

cvs commit: httpd-2.0/server/mpm/winnt child.c

stoddard    2003/03/13 11:27:40

  Modified:    server/mpm/winnt Tag: APACHE_2_0_BRANCH child.c
  Log:
  Win32: Avoid busy wait which consumes 100% of the CPU when all the worker
  threads are busy.
  
  Submitted by: Igor Nazarenko
  Reviewed by: Bill Stoddard
  
  Revision  Changes    Path
  No                   revision
  
  
  No                   revision
  
  
  1.9.2.3   +83 -51    httpd-2.0/server/mpm/winnt/child.c
  
  Index: child.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/server/mpm/winnt/child.c,v
  retrieving revision 1.9.2.2
  retrieving revision 1.9.2.3
  diff -u -r1.9.2.2 -r1.9.2.3
  --- child.c	3 Feb 2003 17:32:06 -0000	1.9.2.2
  +++ child.c	13 Mar 2003 19:27:39 -0000	1.9.2.3
  @@ -108,6 +108,7 @@
   static PCOMP_CONTEXT qtail = NULL;
   static int num_completion_contexts = 0;
   static HANDLE ThreadDispatchIOCP = NULL;
  +static HANDLE qwait_event = NULL;
   
   
   AP_DECLARE(void) mpm_recycle_completion_context(PCOMP_CONTEXT context)
  @@ -124,10 +125,12 @@
           context->next = NULL;
           ResetEvent(context->Overlapped.hEvent);
           apr_thread_mutex_lock(qlock);
  -        if (qtail)
  +        if (qtail) {
               qtail->next = context;
  -        else
  +        } else {
               qhead = context;
  +            SetEvent(qwait_event);
  +        }
           qtail = context;
           apr_thread_mutex_unlock(qlock);
       }
  @@ -138,57 +141,78 @@
       apr_status_t rv;
       PCOMP_CONTEXT context = NULL;
   
  -    /* Grab a context off the queue */
  -    apr_thread_mutex_lock(qlock);
  -    if (qhead) {
  -        context = qhead;
  -        qhead = qhead->next;
  -        if (!qhead)
  -            qtail = NULL;
  -    }
  -    apr_thread_mutex_unlock(qlock);
  -
  -    /* If we failed to grab a context off the queue, alloc one out of 
  -     * the child pool. There may be up to ap_threads_per_child contexts
  -     * in the system at once.
  -     */
  -    if (!context) {
  -        if (num_completion_contexts >= ap_threads_per_child) {
  -            static int reported = 0;
  -            if (!reported) {
  -                ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf,
  -                             "Server ran out of threads to serve requests. Consider "
  -                             "raising the ThreadsPerChild setting");
  -                reported = 1;
  -            }
  -            return NULL;
  -        }
  -        /* Note:
  -         * Multiple failures in the next two steps will cause the pchild pool
  -         * to 'leak' storage. I don't think this is worth fixing...
  -         */
  -        context = (PCOMP_CONTEXT) apr_pcalloc(pchild, sizeof(COMP_CONTEXT));
  -
  -        context->Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  -        if (context->Overlapped.hEvent == NULL) {
  -            /* Hopefully this is a temporary condition ... */
  -            ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_os_error(), ap_server_conf,
  -                         "mpm_get_completion_context: CreateEvent failed.");
  -            return NULL;
  +    while (1) {
  +        /* Grab a context off the queue */
  +        apr_thread_mutex_lock(qlock);
  +        if (qhead) {
  +            context = qhead;
  +            qhead = qhead->next;
  +            if (!qhead)
  +                qtail = NULL;
  +        } else {
  +            ResetEvent(qwait_event);
           }
  +        apr_thread_mutex_unlock(qlock);
  +  
  +        if (!context) {
  +            /* We failed to grab a context off the queue, consider allocating a
  +             * new one out of the child pool. There may be up to ap_threads_per_child
  +             * contexts in the system at once.
  +             */
  +            if (num_completion_contexts >= ap_threads_per_child) {
  +                /* All workers are busy, need to wait for one */
  +                static int reported = 0;
  +                if (!reported) {
  +                    ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf,
  +                                 "Server ran out of threads to serve requests. Consider "
  +                                 "raising the ThreadsPerChild setting");
  +                    reported = 1;
  +                }
   
  -        /* Create the tranaction pool */
  -        if ((rv = apr_pool_create(&context->ptrans, pchild)) != APR_SUCCESS) {
  -            ap_log_error(APLOG_MARK,APLOG_WARNING, rv, ap_server_conf,
  -                         "mpm_get_completion_context: Failed to create the transaction pool.");
  -            CloseHandle(context->Overlapped.hEvent);
  -            return NULL;
  +                /* Wait for a worker to free a context. Once per second, give
  +                 * the caller a chance to check for shutdown. If the wait
  +                 * succeeds, get the context off the queue. It must be available,
  +                 * since there's only one consumer.
  +                 */
  +                rv = WaitForSingleObject(qwait_event, 1000);
  +                if (rv == WAIT_OBJECT_0)
  +                    continue;
  +                else /* Hopefully, WAIT_TIMEOUT */
  +                    return NULL;
  +            } else {
  +                /* Allocate another context.
  +                 * Note:
  +                 * Multiple failures in the next two steps will cause the pchild pool
  +                 * to 'leak' storage. I don't think this is worth fixing...
  +                 */
  +                context = (PCOMP_CONTEXT) apr_pcalloc(pchild, sizeof(COMP_CONTEXT));
  +  
  +                context->Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  +                if (context->Overlapped.hEvent == NULL) {
  +                    /* Hopefully this is a temporary condition ... */
  +                    ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_os_error(), ap_server_conf,
  +                                 "mpm_get_completion_context: CreateEvent failed.");
  +                    return NULL;
  +                }
  + 
  +                /* Create the tranaction pool */
  +                if ((rv = apr_pool_create(&context->ptrans, pchild)) != APR_SUCCESS) {
  +                    ap_log_error(APLOG_MARK,APLOG_WARNING, rv, ap_server_conf,
  +                                 "mpm_get_completion_context: Failed to create the transaction pool.");
  +                    CloseHandle(context->Overlapped.hEvent);
  +                    return NULL;
  +                }
  +                apr_pool_tag(context->ptrans, "ptrans");
  + 
  +                context->accept_socket = INVALID_SOCKET;
  +                context->ba = apr_bucket_alloc_create(pchild);
  +                apr_atomic_inc(&num_completion_contexts); 
  +                break;
  +            }
  +        } else {
  +            /* Got a context from the queue */
  +            break;
           }
  -        apr_pool_tag(context->ptrans, "ptrans");
  -
  -        context->accept_socket = INVALID_SOCKET;
  -        context->ba = apr_bucket_alloc_create(pchild);
  -        apr_atomic_inc(&num_completion_contexts);
       }
   
       return context;
  @@ -823,6 +847,12 @@
                                                       0,
                                                       0); /* CONCURRENT ACTIVE THREADS */
           apr_thread_mutex_create(&qlock, APR_THREAD_MUTEX_DEFAULT, pchild);
  +        qwait_event = CreateEvent(NULL, TRUE, FALSE, NULL);
  +        if (!qwait_event) {
  +            ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
  +                         "Child %d: Failed to create a qwait event.", my_pid);
  +            exit(APEXIT_CHILDINIT);
  +        }
       }
   
       /* 
  @@ -1035,8 +1065,10 @@
   
       CloseHandle(allowed_globals.jobsemaphore);
       apr_thread_mutex_destroy(allowed_globals.jobmutex);
  -    if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS)
  +    if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
       	apr_thread_mutex_destroy(qlock);
  +        CloseHandle(qwait_event);
  +    }
   
       apr_pool_destroy(pchild);
       CloseHandle(exit_event);