]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Win32: Avoid busy wait which consumes 100% of the CPU when all the worker
authorBill Stoddard <stoddard@apache.org>
Thu, 13 Mar 2003 19:27:39 +0000 (19:27 +0000)
committerBill Stoddard <stoddard@apache.org>
Thu, 13 Mar 2003 19:27:39 +0000 (19:27 +0000)
threads are busy.

Submitted by: Igor Nazarenko
Reviewed by: Bill Stoddard

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/APACHE_2_0_BRANCH@98995 13f79535-47bb-0310-9956-ffa450edef68

server/mpm/winnt/child.c

index f76ebbddea6119f6f14ba397cadd1bedaa79256d..5ca5f7ca4f046a6f80c119533e880fa14085f566 100644 (file)
@@ -108,6 +108,7 @@ static PCOMP_CONTEXT qhead = NULL;
 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 @@ AP_DECLARE(void) mpm_recycle_completion_context(PCOMP_CONTEXT context)
         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 @@ AP_DECLARE(PCOMP_CONTEXT) mpm_get_completion_context(void)
     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 @@ void child_main(apr_pool_t *pconf)
                                                     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 @@ void child_main(apr_pool_t *pconf)
 
     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);