* Definitions of WINNT MPM specific config globals
*/
-static char *mpm_pid_fname=NULL;
+char *ap_pid_fname=NULL;
static int ap_threads_per_child = 0;
static int workers_may_exit = 0;
static int max_requests_per_child = 0;
static char ap_coredump_dir[MAX_STRING_LEN];
static server_rec *server_conf;
-HANDLE AcceptExCompPort = NULL;
+static HANDLE AcceptExCompPort = NULL;
static int one_process = 0;
CloseHandle(event_id);
}
+/* To share the semaphores with other processes, we need a NULL ACL
+ * Code from MS KB Q106387
+ */
+static PSECURITY_ATTRIBUTES GetNullACL()
+{
+ PSECURITY_DESCRIPTOR pSD;
+ PSECURITY_ATTRIBUTES sa;
+
+ sa = (PSECURITY_ATTRIBUTES) LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES));
+ pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
+ SECURITY_DESCRIPTOR_MIN_LENGTH);
+ if (pSD == NULL || sa == NULL) {
+ return NULL;
+ }
+ if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)
+ || GetLastError()) {
+ LocalFree( pSD );
+ LocalFree( sa );
+ return NULL;
+ }
+ if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE)
+ || GetLastError()) {
+ LocalFree( pSD );
+ LocalFree( sa );
+ return NULL;
+ }
+ sa->nLength = sizeof(sa);
+ sa->lpSecurityDescriptor = pSD;
+ sa->bInheritHandle = TRUE;
+ return sa;
+}
+
+static void CleanNullACL( void *sa ) {
+ if( sa ) {
+ LocalFree( ((PSECURITY_ATTRIBUTES)sa)->lpSecurityDescriptor);
+ LocalFree( sa );
+ }
+}
+
/*
* The Win32 call WaitForMultipleObjects will only allow you to wait for
* a maximum of MAXIMUM_WAIT_OBJECTS (current 64). Since the threading
* routine that will increase this size.
*/
static DWORD wait_for_many_objects(DWORD nCount, CONST HANDLE *lpHandles,
- DWORD dwSeconds)
+ DWORD dwSeconds)
{
time_t tStopTime;
DWORD dwRet = WAIT_TIMEOUT;
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);
+ dwRet = WaitForMultipleObjects(
+ min(MAXIMUM_WAIT_OBJECTS, nCount - (dwIndex * MAXIMUM_WAIT_OBJECTS)),
+ lpHandles + (dwIndex * MAXIMUM_WAIT_OBJECTS),
+ 0, 0);
if (dwRet != WAIT_TIMEOUT) {
break;
* On entry, type gives the event to signal. 0 means shutdown, 1 means
* graceful restart.
*/
-
static void signal_parent(int type)
{
HANDLE e;
case 1: signal_name = signal_restart_name; break;
default: return;
}
-
e = OpenEvent(EVENT_ALL_ACCESS, FALSE, signal_name);
if (!e) {
/* Um, problem, can't signal the parent, which means we can't
* signal ourselves to die. Ignore for now...
*/
ap_log_error(APLOG_MARK, APLOG_EMERG, GetLastError(), server_conf,
- "OpenEvent on %s event", signal_name);
+ "OpenEvent on %s event", signal_name);
return;
}
if (SetEvent(e) == 0) {
/* Same problem as above */
ap_log_error(APLOG_MARK, APLOG_EMERG, GetLastError(), server_conf,
- "SetEvent on %s event", signal_name);
+ "SetEvent on %s event", signal_name);
CloseHandle(e);
return;
}
CloseHandle(e);
}
-void ap_start_shutdown(void)
-{
- signal_parent(0);
-}
-void ap_start_restart(int graceful)
-{
- signal_parent(1);
-}
-static int volatile is_graceful = 0;
-API_EXPORT(int) ap_graceful_stop_signalled(void)
+/*
+ * Initialise the signal names, in the global variables signal_name_prefix,
+ * signal_restart_name and signal_shutdown_name.
+ */
+
+#define MAX_SIGNAL_NAME 30 /* Long enough for apPID_shutdown, where PID is an int */
+char signal_name_prefix[MAX_SIGNAL_NAME];
+char signal_restart_name[MAX_SIGNAL_NAME];
+char signal_shutdown_name[MAX_SIGNAL_NAME];
+static void setup_signal_names(char *prefix)
{
- return is_graceful;
+ ap_snprintf(signal_name_prefix, sizeof(signal_name_prefix), prefix);
+ ap_snprintf(signal_shutdown_name, sizeof(signal_shutdown_name),
+ "%s_shutdown", signal_name_prefix);
+ ap_snprintf(signal_restart_name, sizeof(signal_restart_name),
+ "%s_restart", signal_name_prefix);
}
/*
exit(1);
}
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, APR_SUCCESS, server_conf,
- "BytesRead = %d WSAProtocolInfo = %x20", BytesRead, WSAProtocolInfo);
+ "Child %d: setup_inherited_listener() read = %d bytes of WSAProtocolInfo.", my_pid);
nsd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
&WSAProtocolInfo, 0, 0);
if (nsd == INVALID_SOCKET) {
ap_log_error(APLOG_MARK, APLOG_CRIT, WSAGetLastError(), server_conf,
- "setup_inherited_listeners: WSASocket failed to open the inherited socket.");
+ "Child %d: setup_inherited_listeners(), WSASocket failed to open the inherited socket.", my_pid);
signal_parent(0); /* tell parent to die */
exit(1);
}
ap_put_os_sock(&lr->sd, &nsd, pconf);
lr->count = 0;
}
+ /* Now, read the AcceptExCompPort from the parent */
+ ReadFile(pipe, &AcceptExCompPort, sizeof(AcceptExCompPort), &BytesRead, (LPOVERLAPPED) NULL);
CloseHandle(pipe);
-
for (lr = ap_listeners; lr; lr = lr->next) {
num_listeners++;
}
head_listener = ap_listeners;
-
return num_listeners;
}
* the connection has finished the thread is free to take another
* job from the job list.
*
- * In the code, the "main" thread is running within the worker_main()
+ * In the code, the "main" thread is running within the child_main()
* function. The first thing this function does is create the
* worker threads, which operate in the child_sub_main() function. The
- * main thread then goes into a loop within worker_main() where they
+ * main thread then goes into a loop within child_main() where they
* do a select() on the listening sockets. The select times out once
* per second so that the thread can check for an "exit" signal
* from the parent process (see below). If this signal is set, the
* process is active at once.
**********************************************************************/
-int service_init()
-{
-/*
- common_init();
-
- ap_cpystrn(ap_server_root, HTTPD_ROOT, sizeof(ap_server_root));
- if (ap_registry_get_service_conf(pconf, ap_server_confname, sizeof(ap_server_confname),
- ap_server_argv0))
- return FALSE;
-
- ap_setup_prelinked_modules();
- server_conf = ap_read_config(pconf, ptrans, ap_server_confname);
- ap_log_pid(pconf, ap_pid_fname);
- post_parse_init();
-*/
- return TRUE;
-}
/*
* Definition of jobs, shared by main and worker threads.
/*
* Windows 2000/NT specific code...
- * create_and_queue_acceptex_context()
- * requeue_acceptex_context()
+ * create_acceptex_context()
+ * reset_acceptex_context()
+ * drain_acceptex_complport()
* winnt_get_connection()
*
* TODO: Insert a discussion of 'completion contexts' and what these function do here...
*/
-static int create_and_queue_acceptex_context(ap_context_t *_pconf, ap_listen_rec *lr)
+static void drain_acceptex_complport(HANDLE hComplPort, BOOLEAN bCleanUp)
+{
+ LPOVERLAPPED pol;
+ PCOMP_CONTEXT context;
+ int rc;
+ DWORD BytesRead;
+ DWORD CompKey;
+ int lastError;
+
+ while (1) {
+ context = NULL;
+ rc = GetQueuedCompletionStatus(hComplPort, &BytesRead, &CompKey,
+ &pol, 1000);
+ if (!rc) {
+ lastError = GetLastError();
+ if (lastError == ERROR_OPERATION_ABORTED) {
+ ap_log_error(APLOG_MARK,APLOG_INFO,lastError, server_conf,
+ "Child: %d - Draining a packet off the completion port.", my_pid);
+ continue;
+ }
+ break;
+ }
+ ap_log_error(APLOG_MARK,APLOG_INFO,APR_SUCCESS, server_conf,
+ "Child: %d - Nuking an active connection. context = %x", my_pid, context);
+ context = (PCOMP_CONTEXT) pol;
+ if (context && bCleanUp) {
+ /* It is only valid to clean-up in the process that initiated the I/O */
+ closesocket(context->accept_socket);
+ CloseHandle(context->Overlapped.hEvent);
+ }
+ }
+}
+static int create_acceptex_context(ap_context_t *_pconf, ap_listen_rec *lr)
{
PCOMP_CONTEXT context;
DWORD BytesRead;
context = ap_pcalloc(_pconf, sizeof(COMP_CONTEXT));
if (!context) {
ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), server_conf,
- "create_and_queue_acceptex_context: ap_pcalloc() failed. Process will exit.");
+ "create_acceptex_context: ap_pcalloc() failed. Process will exit.");
return -1;
}
context->Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (context->Overlapped.hEvent == NULL) {
ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), server_conf,
- "create_and_queue_acceptex_context: CreateEvent() failed. Process will exit.");
+ "create_acceptex_context: CreateEvent() failed. Process will exit.");
return -1;
}
context->accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (context->accept_socket == INVALID_SOCKET) {
ap_log_error(APLOG_MARK,APLOG_ERR, WSAGetLastError(), server_conf,
- "create_and_queue_acceptex_context: socket() failed. Process will exit.");
+ "create_acceptex_context: socket() failed. Process will exit.");
return -1;
}
ap_create_context(&context->ptrans, _pconf);
lasterror = WSAGetLastError();
if (lasterror != ERROR_IO_PENDING) {
ap_log_error(APLOG_MARK,APLOG_ERR, WSAGetLastError(), server_conf,
- "create_and_queue_acceptex_context: AcceptEx failed. Process will exit.");
+ "create_acceptex_context: AcceptEx failed. Process will exit.");
return -1;
}
}
return 0;
}
-static ap_inline int requeue_acceptex_context(PCOMP_CONTEXT context)
+static ap_inline int reset_acceptex_context(PCOMP_CONTEXT context)
{
DWORD BytesRead;
SOCKET nsd;
int lasterror;
+
context->lr->count++;
if (context->accept_socket == -1)
if (context->accept_socket == INVALID_SOCKET) {
ap_log_error(APLOG_MARK,APLOG_ERR, WSAGetLastError(), server_conf,
- "requeue_acceptex_context: socket() failed. Process will exit.");
+ "reset_acceptex_context: socket() failed. Process will exit.");
return -1;
}
context->recv_buf_size = context->conn_io->bufsiz - 2*PADDED_ADDR_SIZE;
ap_get_os_sock(&nsd, context->lr->sd);
- if (!AcceptEx(nsd,
- context->accept_socket,
+ if (!AcceptEx(nsd, context->accept_socket,
context->recv_buf, context->recv_buf_size,
PADDED_ADDR_SIZE, PADDED_ADDR_SIZE,
&BytesRead, (LPOVERLAPPED) context)) {
lasterror = WSAGetLastError();
if (lasterror != ERROR_IO_PENDING) {
ap_log_error(APLOG_MARK,APLOG_ERR, WSAGetLastError(), server_conf,
- "requeue_acceptex_context: AcceptEx failed. Leaving the process running.");
+ "reset_acceptex_context: AcceptEx failed. Leaving the process running.");
return -1;
}
}
+
return 0;
}
static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
{
int requests_this_child = 0;
int rc;
-
LPOVERLAPPED pol;
DWORD CompKey;
DWORD BytesRead;
+ int lastError;
if (context != NULL) {
- context->accept_socket = -1; /* Don't reuse the socket */
- if (requeue_acceptex_context(context) == -1) {
- if (context->accept_socket != -1)
- closesocket(context->accept_socket);
+ /* If child shutdown has been signaled, clean-up the completion context */
+ if (workers_may_exit) {
CloseHandle(context->Overlapped.hEvent);
- return NULL;
+ /* destroy pool */
+ }
+ else {
+ context->accept_socket = -1; /* Don't reuse the accept_socket */
+ if (reset_acceptex_context(context) == -1) {
+ if (context->accept_socket != -1)
+ closesocket(context->accept_socket);
+ CloseHandle(context->Overlapped.hEvent);
+ return NULL;
+ }
}
}
- rc = GetQueuedCompletionStatus(AcceptExCompPort,
- &BytesRead,
- &CompKey,
- &pol,
- INFINITE);
+ while (1) {
+ rc = GetQueuedCompletionStatus(AcceptExCompPort,
+ &BytesRead,
+ &CompKey,
+ &pol,
+ INFINITE);
+ if (!rc) {
+ ap_log_error(APLOG_MARK,APLOG_ERR, lastError, server_conf,
+ "Child: %d - GetQueuedCompletionStatus() failed", my_pid);
+ continue;
+ }
+ break;
+ }
context = (PCOMP_CONTEXT) pol;
if (CompKey == 999) {
- if (context) {
- closesocket(context->accept_socket);
- CloseHandle(context->Overlapped.hEvent);
- return NULL;
- }
+ return NULL;
}
+ /* Each listener needs at least 1 context available to receive connections on.
+ * Create additional listener contexts if needed.
+ */
ap_lock(allowed_globals.jobmutex);
-
context->lr->count--;
- if (context->lr->count < 2) {
- if (create_and_queue_acceptex_context(pconf, context->lr) == -1) {
+ if ((context->lr->count < 2) && !workers_may_exit) {
+ if (create_acceptex_context(pconf, context->lr) == -1) {
ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), server_conf,
"Unable to create an AcceptEx completion context -- process will exit");
- closesocket(context->accept_socket);
- CloseHandle(context->Overlapped.hEvent);
+ signal_parent(0);
return NULL;
}
}
ap_unlock(allowed_globals.jobmutex);
+ /* Received a connection */
context->conn_io->incnt = BytesRead;
-
GetAcceptExSockaddrs(context->recv_buf,
context->recv_buf_size,
PADDED_ADDR_SIZE,
&context->sa_client_len);
return context;
-/*
- CloseHandle(context->Overlapped.hEvent);
- SetEvent(exit_event);
- return NULL;
-*/
+
}
/*
- * child_main() - this is the main loop for the worker threads
+ * worker_main() - this is the main loop for the worker threads
*
+ * Windows 95/98
* Each thread runs within this function. They wait within remove_job()
* for a job to become available, then handle all the requests on that
* connection until it is closed, then return to remove_job().
* - block in remove_job, and when unblocked we have an already
* accepted socket, instead of blocking on a mutex or select().
*/
-static void child_main(int child_num)
+
+static void worker_main(int child_num)
{
+ static BOOLEAN bListenersStarted = FALSE;
PCOMP_CONTEXT context = NULL;
+ if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
+ /* Windows NT/2000: Create AcceptEx completion contexts for each of the
+ * listeners
+ */
+ ap_lock(allowed_globals.jobmutex);
+ if (!bListenersStarted) {
+ ap_listen_rec *lr;
+ int i;
+ bListenersStarted = TRUE;
+ for (lr = ap_listeners; lr != NULL; lr = lr->next) {
+ for(i=0; i<2; i++) {
+ if (lr->count < 2)
+ if (create_acceptex_context(pconf, lr) == -1) {
+ ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), server_conf,
+ "Unable to create an AcceptEx completion context -- process will exit");
+ signal_parent(0); /* tell parent to die */
+ }
+ }
+ }
+ }
+ ap_unlock(allowed_globals.jobmutex);
+ }
+
while (1) {
conn_rec *current_conn;
ap_iol *iol;
iol = win32_attach_socket(context->ptrans, context->accept_socket);
if (iol == NULL) {
ap_log_error(APLOG_MARK, APLOG_ERR, APR_ENOMEM, server_conf,
- "child_main: attach_socket() failed. Continuing...");
+ "worker_main: attach_socket() failed. Continuing...");
closesocket(context->accept_socket);
continue;
}
ap_process_connection(current_conn);
}
+#if 0
+ ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf,
+ "child_main: Setting exit_event");
SetEvent(exit_event);
+#endif
/* TODO: Add code to clean-up completion contexts here */
}
/*
- * Initialise the signal names, in the global variables signal_name_prefix,
- * signal_restart_name and signal_shutdown_name.
- */
-
-#define MAX_SIGNAL_NAME 30 /* Long enough for apPID_shutdown, where PID is an int */
-char signal_name_prefix[MAX_SIGNAL_NAME];
-char signal_restart_name[MAX_SIGNAL_NAME];
-char signal_shutdown_name[MAX_SIGNAL_NAME];
-static void setup_signal_names(char *prefix)
-{
- ap_snprintf(signal_name_prefix, sizeof(signal_name_prefix), prefix);
- ap_snprintf(signal_shutdown_name, sizeof(signal_shutdown_name),
- "%s_shutdown", signal_name_prefix);
- ap_snprintf(signal_restart_name, sizeof(signal_restart_name),
- "%s_restart", signal_name_prefix);
-}
-
-/*
- * worker_main() is main loop for the child process. The loop in
+ * child_main() is main loop for the child process. The loop in
* this function becomes the controlling thread for the actually working
* threads (which run in a loop in child_sub_main()).
* Globals Used:
* exit_event, start_mutex, ap_threads_per_child, server_conf,
* h_errno defined to WSAGetLastError in winsock2.h,
*/
-static void worker_main()
+static void child_main()
{
int nthreads = ap_threads_per_child;
ap_create_context(&pchild, pconf);
// ap_restart_time = time(NULL);
-
/*
* Wait until we have permission to start accepting connections.
* start_mutex is used to ensure that only one child ever
status = ap_lock(start_mutex);
if (status != APR_SUCCESS) {
ap_log_error(APLOG_MARK,APLOG_ERR, status, server_conf,
- "Waiting for start_mutex or exit_event -- process will exit");
-
+ "Child %d: Failed to acquire the start_mutex. Process will exit.", my_pid);
+ signal_parent(0); /* tell parent to die */
ap_destroy_context(pchild);
exit(0);
}
+ ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, server_conf, "Child %d: Acquired the start mutex", my_pid);
/* Setup the listening sockets */
if (one_process) {
setup_listeners(server_conf);
} else {
- /* Get listeners from the parent process */
setup_inherited_listeners(server_conf);
}
if (listenmaxfd == INVALID_SOCKET) {
- /* Help, no sockets were made, better log something and exit */
- ap_log_error(APLOG_MARK, APLOG_CRIT, h_errno, NULL,
- "No sockets were created for listening");
- signal_parent(0); /* tell parent to die */
+ /* No sockets were made, better log something and exit */
+ ap_log_error(APLOG_MARK, APLOG_CRIT, h_errno, NULL,
+ "No sockets were created for listening");
+ signal_parent(0); /* tell parent to die */
ap_destroy_context(pchild);
- exit(0);
+ exit(0);
}
allowed_globals.jobsemaphore = create_semaphore(0);
ap_create_lock(&allowed_globals.jobmutex, APR_MUTEX, APR_INTRAPROCESS, NULL, pchild);
+ /* Create the worker thread pool */
+ ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, server_conf, "Child %d: Creating %d worker threads",my_pid, nthreads);
+ child_handles = (thread *) alloca(nthreads * sizeof(int));
+ for (i = 0; i < nthreads; i++) {
+ child_handles[i] = create_thread((void (*)(void *)) worker_main, (void *) i);
+ }
+
if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
- /* Win9X (Windows 95/98)
- * Create the worker thread pool... */
- child_handles = (thread *) alloca(nthreads * sizeof(int));
- for (i = 0; i < nthreads; i++) {
- child_handles[i] = create_thread((void (*)(void *)) child_main, (void *) i);
- }
- /* Create the accept thread */
+ /* Win95/98: Create the accept thread */
create_thread((void (*)(void *)) accept_and_queue_connections, (void *) NULL);
- } /* Windows 95/98 */
- else {
- /* Windows NT/2000
- * Windows NT/2000 have nifty network I/O routines not available in
- * Windows 95/98 like AcceptEx, TransmitFile and CompletionPorts. If we want to use
- * them, we gotta do things differently. */
- ap_listen_rec *lr;
- SOCKET nsd;
-
- /* Create the AcceptEx completion port
- * All listeners are associated with the AcceptEx completion port. When a connection
- * is accepted, the AcceptEx completion port is signaled and one of the worker threads
- * blocked on it will be awakened (in LIFO order) to handle the connection.
- * Note: Experiment with CONCURRENT_ACTIVE_THREADS. A setting of 0 is best for performance
- * (only one thread will be 'active', i.e., not blocked on I/O, at any time). This is bad
- * if your 'active' threads get caught in computationally intensive tasks... */
- AcceptExCompPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
- NULL,
- 0,
- 0); /* CONCURRENT ACTIVE THREADS */
- if (AcceptExCompPort == NULL) {
- ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), server_conf,
- "Unable to create the AcceptExCompletionPort -- process will exit");
- ap_destroy_context(pchild);
- exit(0);
- }
-
- /* Associate each listener with the AcceptEx completion port */
- for (lr = ap_listeners; lr != NULL; lr = lr->next) {
- ap_get_os_sock(&nsd, lr->sd);
- if (!CreateIoCompletionPort((HANDLE) nsd, AcceptExCompPort, 0, 0)) {
- ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), server_conf,
- "Unable to associate listener with the AcceptExCompletionPort -- process will exit");
- ap_destroy_context(pchild);
- exit(0);
- }
- }
-
- /* Create the worker thread pool */
- child_handles = (thread *) alloca(nthreads * sizeof(int));
- for (i = 0; i < nthreads; i++) {
- child_handles[i] = create_thread((void (*)(void *)) child_main, (void *) i);
- }
-
- /* Create 3 AcceptEx contexts for each listener then queue them to the
- * AcceptEx completion port. */
- for (lr = ap_listeners; lr != NULL; lr = lr->next) {
- for(i=0; i<2; i++) {
- if (create_and_queue_acceptex_context(pconf, lr) == -1) {
- ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), server_conf,
- "Unable to create an AcceptEx completion context -- process will exit");
- ap_destroy_context(pchild);
- exit(0);
- }
- }
- }
- } /* Windows 2000/NT */
+ }
+ /* Wait for the exit event to be signaled by the parent process */
rv = WaitForSingleObject(exit_event, INFINITE);
- printf("exit event signalled \n");
+
ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, server_conf,
- "Exit event signaled. Child process is ending.");
+ "Child %d: Exit event signaled. Child process is ending.", my_pid);
workers_may_exit = 1;
- /* Get ready to shutdown and exit */
- ap_unlock(start_mutex);
-
- /* Tell the workers to stop */
+ /* Shutdown the worker threads */
if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
- /* Windows 95/98
- * Tell the workers to stop */
for (i = 0; i < nthreads; i++) {
add_job(-1);
}
}
- else {
- /* Windows NT/2000
- * Drain any completion contexts and threads waiting for them */
+ else { /* Windows NT/2000 */
+ /* Hack alert... Give the server a couple of seconds to receive
+ * connections and drain AcceptEx completion contexts. We will
+ * probably drop a few connections across a graceful restart, but
+ * hopefully not many. This needs work...*/
+ Sleep(2000);
+
+ /* Tell the worker threads to exit. Any connections accepted on
+ * the completion port from now will be dropped */
for (i=0; i < nthreads; i++) {
- PostQueuedCompletionStatus(AcceptExCompPort, 0, 999, NULL);
+ if (!PostQueuedCompletionStatus(AcceptExCompPort, 0, 999, NULL)) {
+ ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, server_conf,
+ "PostQueuedCompletionStatus failed");
+ }
}
}
- /* Wait for all your children */
+ /* Wait for the worker threads to die */
end_time = time(NULL) + 180;
while (nthreads) {
- rv = wait_for_many_objects(nthreads, child_handles,
- end_time - time(NULL));
+ rv = wait_for_many_objects(nthreads, child_handles, end_time - time(NULL));
if (rv != WAIT_TIMEOUT) {
rv = rv - WAIT_OBJECT_0;
ap_assert((rv >= 0) && (rv < nthreads));
cleanup_thread(child_handles, &nthreads, rv);
- continue;
- }
- break;
+ continue;
+ }
+ break;
}
-
for (i = 0; i < nthreads; i++) {
kill_thread(child_handles[i]);
free_thread(child_handles[i]);
}
+ ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, server_conf,
+ "Child %d: All worker threads have ended.", my_pid);
+ if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
+ /* All the worker threads should have exited by now, which will
+ * cause any outstanding I/O on the completion port to be aborted.
+ * Drain the completion port of this aborted I/O/
+ */
+ drain_acceptex_complport(AcceptExCompPort, TRUE);
+ }
+
+ ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, server_conf,
+ "Child %d: Releasing the start mutex", my_pid);
+ ap_unlock(start_mutex);
+
+ /* Still need to register cleanups for the sockets */
+ CloseHandle(AcceptExCompPort);
destroy_semaphore(allowed_globals.jobsemaphore);
ap_destroy_lock(allowed_globals.jobmutex);
ap_destroy_context(pchild);
-
-}
-static HANDLE create_exit_event(const char* event_name)
-{
- return CreateEvent(NULL, TRUE, FALSE, event_name);
}
+
/*
* Spawn a child Apache process. The child process has the command line arguments from
* argc and argv[], plus a -Z argument giving the name of an event. The child should
static int create_process(ap_context_t *p, HANDLE *handles, HANDLE *events, int *processes)
{
+
int rv;
char buf[1024];
char *pCommand;
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
+ /* Create the IOCompletionPort */
+ if (AcceptExCompPort == NULL) {
+ AcceptExCompPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
+ NULL,
+ 0,
+ 0); /* CONCURRENT ACTIVE THREADS */
+ if (AcceptExCompPort == NULL) {
+ ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), server_conf,
+ "Unable to create the AcceptExCompletionPort -- process will exit");
+ return -1;
+ }
+ }
+
/* Build the command line. Should look something like this:
* C:/apache/bin/apache.exe -f ap_server_confname
* First, get the path to the executable...
return -1;
}
- // pCommand = ap_psprintf(p, "\"%s\" -f \"%s\"", buf, ap_server_confname);
+ //pCommand = ap_psprintf(p, "\"%s\" -f \"%s\"", buf, ap_server_confname);
pCommand = ap_psprintf(p, "\"%s\" -f \"%s\"", buf, SERVER_CONFIG_FILE);
/* Create a pipe to send socket info to the child */
SetEnvironmentVariable("AP_PARENT_PID",ap_psprintf(p,"%d",parent_pid));
- /* Give the read in of the pipe (hPipeRead) to the child as stdin. The
+ /* Give the read end of the pipe (hPipeRead) to the child as stdin. The
* parent will write the socket data to the child on this pipe.
*/
memset(&si, 0, sizeof(si));
else {
HANDLE kill_event;
LPWSAPROTOCOL_INFO lpWSAProtocolInfo;
+ HANDLE hDupedCompPort;
ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf,
"Parent: Created child process %d", pi.dwProcessId);
SetEnvironmentVariable("AP_PARENT_PID",NULL);
- /* Create the exit_event, apCHILD_PID */
- kill_event = create_exit_event(ap_psprintf(pconf,"apC%d", pi.dwProcessId));
-//CreateEvent(NULL, TRUE, TRUE, ap_psprintf(pconf,"apC%d", pi.dwProcessId)); // exit_event_name...
+ /* Create the exit_event, apCchild_pid */
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+ sa.lpSecurityDescriptor = NULL;
+ kill_event = CreateEvent(&sa, TRUE, FALSE, ap_psprintf(pconf,"apC%d", pi.dwProcessId));
if (!kill_event) {
ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf,
"Parent: Could not create exit event for child process");
for (lr = ap_listeners; lr; lr = lr->next) {
int nsd;
lpWSAProtocolInfo = ap_pcalloc(p, sizeof(WSAPROTOCOL_INFO));
- ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, APR_SUCCESS, server_conf,
- "Parent: Duplicating socket %d and sending it to child process %d", lr->sd, pi.dwProcessId);
ap_get_os_sock(&nsd,lr->sd);
- if (WSADuplicateSocket(nsd,
- pi.dwProcessId,
+ /* Associate the socket with the IOcompletion port */
+ CreateIoCompletionPort((HANDLE) nsd, AcceptExCompPort, 0, 0);
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, APR_SUCCESS, server_conf,
+ "Parent: Duplicating socket %d and sending it to child process %d", nsd, pi.dwProcessId);
+ if (WSADuplicateSocket(nsd, pi.dwProcessId,
lpWSAProtocolInfo) == SOCKET_ERROR) {
ap_log_error(APLOG_MARK, APLOG_CRIT, h_errno, server_conf,
"Parent: WSADuplicateSocket failed for socket %d.", lr->sd );
return -1;
}
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, APR_SUCCESS, server_conf,
- "BytesWritten = %d WSAProtocolInfo = %x20", BytesWritten, *lpWSAProtocolInfo);
+ "Parent: BytesWritten = %d WSAProtocolInfo = %x20", BytesWritten, *lpWSAProtocolInfo);
}
+ /* Now, send the AcceptEx completion port to the child */
+ DuplicateHandle(GetCurrentProcess(),
+ AcceptExCompPort,
+ pi.hProcess,
+ &hDupedCompPort,
+ 0,
+ TRUE, // handle can be inherited
+ DUPLICATE_SAME_ACCESS);
+ WriteFile(hPipeWrite, &hDupedCompPort, (DWORD) sizeof(hDupedCompPort), &BytesWritten, (LPOVERLAPPED) NULL);
+ CloseHandle(hDupedCompPort);
}
+
CloseHandle(hPipeRead);
CloseHandle(hPipeWrite);
return 0;
}
-/* To share the semaphores with other processes, we need a NULL ACL
- * Code from MS KB Q106387
- */
-static PSECURITY_ATTRIBUTES GetNullACL()
-{
- PSECURITY_DESCRIPTOR pSD;
- PSECURITY_ATTRIBUTES sa;
-
- sa = (PSECURITY_ATTRIBUTES) LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES));
- pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
- SECURITY_DESCRIPTOR_MIN_LENGTH);
- if (pSD == NULL || sa == NULL) {
- return NULL;
- }
- if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)
- || GetLastError()) {
- LocalFree( pSD );
- LocalFree( sa );
- return NULL;
- }
- if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE)
- || GetLastError()) {
- LocalFree( pSD );
- LocalFree( sa );
- return NULL;
- }
- sa->nLength = sizeof(sa);
- sa->lpSecurityDescriptor = pSD;
- sa->bInheritHandle = TRUE;
- return sa;
-}
-
-static void CleanNullACL( void *sa ) {
- if( sa ) {
- LocalFree( ((PSECURITY_ATTRIBUTES)sa)->lpSecurityDescriptor);
- LocalFree( sa );
- }
-}
-
static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_event)
{
int remaining_children_to_start = ap_daemons_to_start;
/* Wait for shutdown or restart events or for child death */
process_handles[current_live_processes] = shutdown_event;
process_handles[current_live_processes+1] = restart_event;
+ printf("process/shutdown/restart %d %d %d\n", process_handles[0], process_handles[1], process_handles[2]);
rv = WaitForMultipleObjects(current_live_processes+2, (HANDLE *)process_handles,
FALSE, INFINITE);
cld = rv - WAIT_OBJECT_0;
if (rv == WAIT_FAILED) {
/* Something serious is wrong */
ap_log_error(APLOG_MARK,APLOG_CRIT, GetLastError(), server_conf,
- "master_main: : WaitForMultipeObjects on process handles and apache-signal -- doing shutdown");
+ "master_main: WaitForMultipeObjects WAIT_FAILED -- doing server shutdown");
shutdown_pending = 1;
}
else if (rv == WAIT_TIMEOUT) {
else if (cld == current_live_processes) {
/* shutdown_event signalled */
shutdown_pending = 1;
+ printf("shutdown event signaled\n");
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, APR_SUCCESS, s,
- "master_main: Shutdown event signaled. Shutting the server down.");
+ "master_main: Shutdown event signaled -- doing server shutdown.");
if (ResetEvent(shutdown_event) == 0) {
ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), s,
"ResetEvent(shutdown_event)");
* child out of the process_handles ap_table_t and hope for the best...
*/
for (i = 0; i < children_to_kill; i++) {
- /* APD3("master_main: signalling child #%d handle %d to die", i, process_handles[i]); */
+ printf("SetEvent handle = %d\n", process_kill_events[i]);
if (SetEvent(process_kill_events[i]) == 0)
ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), s,
"master_main: SetEvent for child process in slot #%d failed", i);
}
}
else {
- /* A child process must have exited because of MaxRequestPerChild being hit
- * or a fatal error condition (seg fault, etc.). Remove the dead process
+ /* A child process must have exited because of a fatal error condition (seg fault, etc.).
+ * Remove the dead process
* from the process_handles and process_kill_events ap_table_t and create a new
* child process.
* TODO: Consider restarting the child immediately without looping through http_main
*/
restart_pending = 1;
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, APR_SUCCESS, server_conf,
- "master_main: Child processed exited (due to MaxRequestsPerChild?). Restarting the child process.");
+ "master_main: Child process failed. Restarting the child process.");
ap_assert(cld < current_live_processes);
cleanup_process(process_handles, process_kill_events, cld, ¤t_live_processes);
/* APD2("main_process: child in slot %d died", rv); */
/* restart_child(process_hancles, process_kill_events, cld, ¤t_live_processes); */
+
+ /* Drain the AcceptEx completion port of any outstanding I/O pending for the dead
+ * process. */
+ drain_acceptex_complport(AcceptExCompPort, FALSE);
}
die_now:
int tmstart = time(NULL);
/* Signal each child processes to die */
for (i = 0; i < current_live_processes; i++) {
+ printf("SetEvent handle = %d\n", process_kill_events[i]);
if (SetEvent(process_kill_events[i]) == 0)
ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), server_conf,
"master_main: SetEvent for child process in slot #%d failed", i);
static void winnt_pre_config(ap_context_t *pconf, ap_context_t *plog, ap_context_t *ptemp)
{
char *pid;
-#if 0
- one_process=1;
-#else
+
one_process = !!getenv("ONE_PROCESS");
-#endif
osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osver);
else {
/* This is the parent */
parent_pid = my_pid = getpid();
- ap_log_pid(pconf, mpm_pid_fname);
+ ap_log_pid(pconf, ap_pid_fname);
}
ap_listen_pre_config();
ap_daemons_to_start = DEFAULT_NUM_DAEMON;
ap_threads_per_child = DEFAULT_START_THREAD;
- mpm_pid_fname = DEFAULT_PIDLOG;
+ ap_pid_fname = DEFAULT_PIDLOG;
max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
ap_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
{
char* exit_event_name;
-
+ static int restart = 0; /* Default is to not restart */
// time_t tmstart;
- HANDLE shutdown_event; /* used to signal shutdown to parent */
- HANDLE restart_event; /* used to signal a restart to parent */
+ static HANDLE shutdown_event; /* used to signal shutdown to parent */
+ static HANDLE restart_event; /* used to signal a restart to parent */
pconf = _pconf;
server_conf = s;
if ((parent_pid != my_pid) || one_process) {
/* Child process */
+ ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf,
+ "Child %d: Child process is running", my_pid);
AMCSocketInitialize();
exit_event_name = ap_psprintf(pconf, "apC%d", my_pid);
setup_signal_names(ap_psprintf(pconf,"ap%d", parent_pid));
if (one_process) {
ap_create_lock(&start_mutex,APR_MUTEX, APR_CROSS_PROCESS,signal_name_prefix,pconf);
- exit_event = create_exit_event(exit_event_name);
+ exit_event = CreateEvent(NULL, TRUE, FALSE, exit_event_name);
}
else {
ap_child_init_lock(&start_mutex, signal_name_prefix, pconf);
ap_assert(start_mutex);
ap_assert(exit_event);
- worker_main();
+ child_main();
destroy_event(exit_event);
AMCSocketCleanup();
+ restart = 0;
}
else {
/* Parent process */
- static int restart = 0;
PSECURITY_ATTRIBUTES sa = GetNullACL(); /* returns NULL if invalid (Win95?) */
ap_clear_pool(plog);
ap_open_logs(server_conf, plog);
-
+ setup_signal_names(ap_psprintf(pconf,"ap%d", parent_pid));
if (!restart) {
/* service_set_status(SERVICE_START_PENDING);*/
AMCSocketInitialize();
- setup_signal_names(ap_psprintf(pconf,"ap%d", parent_pid));
+// setup_signal_names(ap_psprintf(pconf,"ap%d", parent_pid));
/* Create shutdown event, apPID_shutdown, where PID is the parent
* Apache process ID. Shutdown is signaled by 'apache -k shutdown'.
*/
- shutdown_event = CreateEvent(sa, TRUE, FALSE, signal_shutdown_name);
+ shutdown_event = CreateEvent(sa, FALSE, FALSE, signal_shutdown_name);
if (!shutdown_event) {
ap_log_error(APLOG_MARK, APLOG_EMERG, GetLastError(), s,
"master_main: Cannot create shutdown event %s", signal_shutdown_name);
/* Create restart event, apPID_restart, where PID is the parent
* Apache process ID. Restart is signaled by 'apache -k restart'.
*/
- restart_event = CreateEvent(sa, TRUE, FALSE, signal_restart_name);
+// restart_event = CreateEvent(sa, TRUE, FALSE, signal_restart_name);
+ restart_event = CreateEvent(sa, FALSE, FALSE, signal_restart_name);
if (!restart_event) {
CloseHandle(shutdown_event);
ap_log_error(APLOG_MARK, APLOG_EMERG, GetLastError(), s,
- "master_main: Cannot create restart event %s", signal_restart_name);
+ "ap_run_mpm: Cannot create restart event %s", signal_restart_name);
CleanNullACL((void *)sa);
exit(1);
}
* Ths start mutex is used during a restart to prevent more than one
* child process from entering the accept loop at once.
*/
- ap_create_lock(&start_mutex,APR_MUTEX, APR_CROSS_PROCESS,signal_name_prefix,pconf);
+// ap_create_lock(&start_mutex,APR_MUTEX, APR_CROSS_PROCESS,signal_name_prefix,pconf);
+ ap_create_lock(&start_mutex,APR_MUTEX, APR_CROSS_PROCESS,signal_name_prefix,s->process->pool);
/* TODO: Add some code to detect failure */
}
if (!restart) {
const char *pidfile = NULL;
/* Shutting down. Clean up... */
- pidfile = ap_server_root_relative (pconf, mpm_pid_fname);
+ pidfile = ap_server_root_relative (pconf, ap_pid_fname);
if ( pidfile != NULL && unlink(pidfile) == 0)
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO,APR_SUCCESS,
- server_conf,
- "removed PID file %s (pid=%ld)",
+ server_conf, "removed PID file %s (pid=%ld)",
pidfile, (long)getpid());
ap_destroy_lock(start_mutex);
AMCSocketCleanup();
/* service_set_status(SERVICE_STOPPED); */
}
- return !restart;
}
- return (0);
+ return !restart;
}
static void winnt_hooks(void)
if (cmd->server->is_virtual) {
return "PidFile directive not allowed in <VirtualHost>";
}
- mpm_pid_fname = arg;
+ ap_pid_fname = arg;
return NULL;
}
if (ap_threads_per_child > HARD_THREAD_LIMIT) {
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"WARNING: ThreadsPerChild of %d exceeds compile time"
- " limit of %d threads,", ap_threads_per_child,
+ " limit of %d threads,", ap_threads_per_child,
+ HARD_THREAD_LIMIT);
+ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ " lowering ThreadsPerChild to %d. To increase, please"
+ " see the HARD_THREAD_LIMIT define in src/include/httpd.h.",
HARD_THREAD_LIMIT);
- ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, " lowering ThreadsPerChild to %d. To increase, please"
- " see the", HARD_THREAD_LIMIT);
- ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
- " HARD_THREAD_LIMIT define in src/include/httpd.h.");
}
else if (ap_threads_per_child < 1) {
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,