summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES4
-rw-r--r--os/win32/os.h3
-rw-r--r--os/win32/util_win32.c39
-rw-r--r--server/mpm/winnt/child.c81
4 files changed, 70 insertions, 57 deletions
diff --git a/CHANGES b/CHANGES
index c29f628ca9..23cb5ed1d9 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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]
diff --git a/os/win32/os.h b/os/win32/os.h
index c250ebff40..56fc8b6f9d 100644
--- a/os/win32/os.h
+++ b/os/win32/os.h
@@ -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);
diff --git a/os/win32/util_win32.c b/os/win32/util_win32.c
index 2a5d0583ea..c6c3840478 100644
--- a/os/win32/util_win32.c
+++ b/os/win32/util_win32.c
@@ -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;
-}
diff --git a/server/mpm/winnt/child.c b/server/mpm/winnt/child.c
index c2e9dd0013..d575aadcdc 100644
--- a/server/mpm/winnt/child.c
+++ b/server/mpm/winnt/child.c
@@ -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;
}
- break;
+ 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 */