Commit 8537ecfc authored by William A. Rowe Jr's avatar William A. Rowe Jr
Browse files

Refactor releasing the child processes by eliminating the

time lookup, eliminating the bogus/misimplemented function,
and introducing fairness to release any dead threads before
getting hung up on releasing longer lived threads.

Also pick up the time-to-wait from the server global timeout.

And finally, axe a completely bogus internal helper function.

Backports: r573103, r573105

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.0.x@607310 13f79535-47bb-0310-9956-ffa450edef68
parent 8bfbc4bc
Loading
Loading
Loading
Loading
+4 −0
Changes for CHANGES: 4 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -5,6 +5,10 @@ Changes with Apache 2.0.62
     mod_imagemap: Fix a cross-site scripting issue.  Reported by JPCERT.
     [Joe Orton]

  *) mpm_winnt: Eliminate wait_for_many_objects.  Allows the clean 
     shutdown of the server when the MaxClients is higher then 257,
     in a more responsive manner [Mladen Turk, William Rowe]

  *) http_protocol: Escape request method in 405 error reporting.
     This has no security impact since the browser cannot be tricked
     into sending arbitrary method strings.  [Jeff Trawick]
+0 −3
Changes for os/win32/os.h: 0 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -76,9 +76,6 @@ FARPROC ap_load_dll_func(ap_dlltoken_e fnLib, char* fnName, int ordinal);
PSECURITY_ATTRIBUTES GetNullACL();
void CleanNullACL(void *sa);

DWORD wait_for_many_objects(DWORD nCount, CONST HANDLE *lpHandles, 
                            DWORD dwSeconds);

int set_listeners_noninheritable(apr_pool_t *p);


+0 −39
Changes for os/win32/util_win32.c: 0 added lines, 39 removed lines.
Original line number Diff line number Diff line
@@ -145,42 +145,3 @@ void CleanNullACL(void *sa)
        LocalFree(sa);
    }
}


/*
 * The Win32 call WaitForMultipleObjects will only allow you to wait for 
 * a maximum of MAXIMUM_WAIT_OBJECTS (current 64).  Since the threading 
 * model in the multithreaded version of apache wants to use this call, 
 * we are restricted to a maximum of 64 threads.  This is a simplistic 
 * routine that will increase this size.
 */
DWORD wait_for_many_objects(DWORD nCount, CONST HANDLE *lpHandles, 
                            DWORD dwSeconds)
{
    time_t tStopTime;
    DWORD dwRet = WAIT_TIMEOUT;
    DWORD dwIndex=0;
    BOOL bFirst = TRUE;
  
    tStopTime = time(NULL) + dwSeconds;
  
    do {
        if (!bFirst)
            Sleep(1000);
        else
            bFirst = FALSE;
          
        for (dwIndex = 0; dwIndex * MAXIMUM_WAIT_OBJECTS < nCount; dwIndex++) {
            dwRet = WaitForMultipleObjects( 
                min(MAXIMUM_WAIT_OBJECTS, nCount - (dwIndex * MAXIMUM_WAIT_OBJECTS)),
                lpHandles + (dwIndex * MAXIMUM_WAIT_OBJECTS), 
                0, 0);
                                           
            if (dwRet != WAIT_TIMEOUT) {                                          
              break;
            }
        }
    } while((time(NULL) < tStopTime) && (dwRet == WAIT_TIMEOUT));
    
    return dwRet;
}
+65 −14
Changes for server/mpm/winnt/child.c: 65 added lines, 14 removed lines.
Original line number Diff line number Diff line
@@ -821,14 +821,15 @@ void child_main(apr_pool_t *pconf)
    apr_hash_t *ht;
    ap_listen_rec *lr;
    HANDLE child_events[2];
    int threads_created = 0;
    HANDLE *child_handles;
    int listener_started = 0;
    int threads_created = 0;
    int watch_thread;
    int time_remains;
    int cld;
    int tid;
    HANDLE *child_handles;
    int rv;
    time_t end_time;
    int i;
    int cld;

    apr_pool_create(&pchild, pconf);
    apr_pool_tag(pchild, "pchild");
@@ -1067,19 +1068,69 @@ void child_main(apr_pool_t *pconf)
        apr_thread_mutex_unlock(qlock);
    }

    /* Give busy worker threads a chance to service their connections */
    ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
                 "Child %d: Waiting for %d worker threads to exit.", my_pid, threads_created);
    end_time = time(NULL) + 180;
    while (threads_created) {
        rv = wait_for_many_objects(threads_created, child_handles, end_time - time(NULL));
        if (rv != WAIT_TIMEOUT) {
            rv = rv - WAIT_OBJECT_0;
            ap_assert((rv >= 0) && (rv < threads_created));
            cleanup_thread(child_handles, &threads_created, rv);
    /* Give busy threads a chance to service their connections,
     * (no more than the global server timeout period which 
     * we track in msec remaining).
     */
    watch_thread = 0;
    time_remains = (int)(ap_server_conf->timeout / APR_TIME_C(1000));

    while (threads_created)
    {
        int nFailsafe = MAXIMUM_WAIT_OBJECTS;
        DWORD dwRet;

        /* Every time we roll over to wait on the first group
         * of MAXIMUM_WAIT_OBJECTS threads, take a breather,
         * and infrequently update the error log.
         */
        if (watch_thread >= threads_created) {
            if ((time_remains -= 100) < 0)
                break;

            /* Every 30 seconds give an update */
            if ((time_remains % 30000) == 0) {
                ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, 
                             ap_server_conf,
                             "Child %d: Waiting %d more seconds "
                             "for %d worker threads to finish.", 
                             my_pid, time_remains / 1000, threads_created);
            }
            /* We'll poll from the top, 10 times per second */
            Sleep(100);
            watch_thread = 0;
        }

        /* Fairness, on each iteration we will pick up with the thread
         * after the one we just removed, even if it's a single thread.
         * We don't block here.
         */
        dwRet = WaitForMultipleObjects(min(threads_created - watch_thread,
                                           MAXIMUM_WAIT_OBJECTS),
                                       child_handles + watch_thread, 0, 0);

        if (dwRet == WAIT_FAILED) {
            break;
        }
        if (dwRet == WAIT_TIMEOUT) {
            /* none ready */
            watch_thread += MAXIMUM_WAIT_OBJECTS;
            continue;
        }
        else if (dwRet >= WAIT_ABANDONED_0) {
            /* We just got the ownership of the object, which
             * should happen at most MAXIMUM_WAIT_OBJECTS times.
             * It does NOT mean that the object is signaled.
             */
            if ((nFailsafe--) < 1)
                break;
        }
        else {
            watch_thread += (dwRet - WAIT_OBJECT_0);
            if (watch_thread >= threads_created)
                break;
            cleanup_thread(child_handles, &threads_created, watch_thread);
        }
    }

    /* Kill remaining threads off the hard way */