/* Licensed to the Apache Software Foundation (ASF) under one or more *contributorlicenseagreements.SeetheNOTICEfiledistributedwith *thisworkforadditionalinformationregardingcopyrightownership. *TheASFlicensesthisfiletoYouundertheApacheLicense,Version2.0 *(the"License");youmaynotusethisfileexceptincompliancewith *theLicense.YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense.
*/
*timeout = 0; 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 *anewoneoutofthechildpool.Theremaybeupto *(ap_threads_per_child+num_listeners)contextsinthesystem *atonce.
*/ if (num_completion_contexts >= max_num_completion_contexts) { /* All workers are busy, need to wait for one */ staticint reported = 0; if (!reported) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00326) "Server ran out of threads to serve " "requests. Consider raising the " "ThreadsPerChild setting");
reported = 1;
}
/* Wait for a worker to free a context. Once per second, give *thecallerachancetocheckforshutdown.Ifthewait *succeeds,getthecontextoffthequeue.Itmustbe *available,sincethere'sonlyoneconsumer.
*/
rv = WaitForSingleObject(qwait_event, 1000); if (rv == WAIT_OBJECT_0) continue; else { if (rv == WAIT_TIMEOUT) { /* somewhat-normal condition where threads are busy */
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00327) "mpm_get_completion_context: Failed to get a " "free context within 1 second");
*timeout = 1;
} else { /* should be the unexpected, generic WAIT_FAILED */
ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(),
ap_server_conf, APLOGNO(00328) "mpm_get_completion_context: " "WaitForSingleObject failed to get free context");
} return NULL;
}
} else { /* Allocate another context. *Note:Multiplefailuresinthenexttwostepswillcause *thepchildpoolto'leak'storage.Idon'tthinkthis *isworthfixing...
*/
apr_allocator_t *allocator;
/* Potential optimization; consider handing off to the worker */
/* Inherit the listen socket settings. Required for *shutdown()towork
*/ if (setsockopt(context->accept_socket, SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT, (char *)&nlsd, sizeof(nlsd))) {
ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(),
ap_server_conf, APLOGNO(00342) "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed."); /* Not a failure condition. Keep running. */
}
/* Get the local & remote address *TODO;errorcheck
*/
lpfnGetAcceptExSockaddrs(buf, 0, PADDED_ADDR_SIZE, PADDED_ADDR_SIZE,
&context->sa_server, &context->sa_server_len,
&context->sa_client, &context->sa_client_len);
} else/* accf == ACCEPT_FILTER_NONE */
{ /* There is no socket reuse without AcceptEx() */ if (context->accept_socket != INVALID_SOCKET)
closesocket(context->accept_socket);
/* This could be a persistent event per-listener rather than *per-accept.However,theeventneedstoberemovedfrom *thetargetsocketifnotremovedfromthelistensocket *priortoaccept(),ortheeventselectisinherited. *andmustberemovedfromtheacceptedsocket.
*/
do {
rv = WaitForMultipleObjectsEx(3, events, FALSE, INFINITE, TRUE);
} while (rv == WAIT_IO_COMPLETION);
if (rv != WAIT_OBJECT_0 + 2) { /* not FD_ACCEPT; *exit_eventtriggeredoreventhandlewasclosed
*/ break;
}
/* Grab a connection off the network */
context = winnt_get_connection(context);
if (!context) { /* Time for the thread to exit */ break;
}
/* Have we hit MaxConnectionsPerChild connections? */ if (ap_max_requests_per_child) {
requests_this_child++; if (requests_this_child > ap_max_requests_per_child) {
SetEvent(max_requests_per_child_event);
}
}
#if AP_HAS_THREAD_LOCAL if (!osthd) {
apr_pool_destroy(apr_thread_pool_get(thd));
} #endif
return0;
}
staticvoid cleanup_thread(HANDLE *handles, int *thread_cnt, int thread_to_clean)
{ int i;
CloseHandle(handles[thread_to_clean]); for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++)
handles[i] = handles[i + 1];
(*thread_cnt)--;
}
/* *child_main() *Entrypointforthemaincontrolthreadforthechildprocess. *Thisthreadcreatestheacceptthread,workerthreadsand *monitorsthechildprocessformaintenanceandshutdown *events.
*/ staticvoid create_listener_thread(void)
{ unsigned tid; int num_listeners = 0; /* Start an accept thread per listener *XXX:WhywouldwehaveaNULLsdinourlisteners?
*/
ap_listen_rec *lr;
/* Number of completion_contexts allowed in the system is *(ap_threads_per_child+num_listeners).Weneedtheadditional *completioncontextstopreventserverhangswhenThreadsPerChild *isconfiguredtosomethinglessthanorequaltothenumber *oflisteners.Thisisnotausualcase,butpeoplehave *encounteredit.
*/ for (lr = ap_listeners; lr ; lr = lr->next) {
num_listeners++;
}
max_num_completion_contexts = ap_threads_per_child + num_listeners;
/* Now start a thread per listener */ for (lr = ap_listeners; lr; lr = lr->next) { if (lr->sd != NULL) { /* A smaller stack is sufficient. *ToconverttoCreateThread,thereturnedhandlecannotbe *ignored,itmustbeclosed/joined.
*/
_beginthreadex(NULL, 65536, winnt_accept,
(void *) lr, stack_res_flag, &tid);
}
}
}
void child_main(apr_pool_t *pconf, DWORD parent_pid)
{
apr_status_t status;
apr_hash_t *ht;
ap_listen_rec *lr;
HANDLE child_events[3];
HANDLE *child_handles; int listener_started = 0; int threads_created = 0; int watch_thread; int time_remains; int cld;
DWORD tid; int rv; int i; int num_events;
/* Get a sub context for global allocations in this child, so that *wecanhavecleanupsoccurwhenthechildexits.
*/
apr_pool_create(&pchild, pconf);
apr_pool_tag(pchild, "pchild");
while (1) { for (i = 0; i < ap_threads_per_child; i++) { int *score_idx; int status = ap_scoreboard_image->servers[0][i].status; if (status != SERVER_GRACEFUL && status != SERVER_DEAD) { continue;
}
ap_update_child_status_from_indexes(0, i, SERVER_STARTING, NULL);
child_handles[i] = CreateThread(NULL, ap_thread_stacksize,
worker_main, (void *) i,
stack_res_flag, &tid); if (child_handles[i] == 0) {
ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(),
ap_server_conf, APLOGNO(00355) "Child: CreateThread failed. Unable to " "create all worker threads. Created %d of the %d " "threads requested with the ThreadsPerChild " "configuration directive.",
threads_created, ap_threads_per_child);
ap_signal_parent(SIGNAL_PARENT_SHUTDOWN); goto shutdown;
}
threads_created++; /* Save the score board index in ht keyed to the thread handle. *Weneedthiswhencleaningupthreadsdownbelow...
*/
apr_thread_mutex_lock(child_lock);
score_idx = apr_pcalloc(pchild, sizeof(int));
*score_idx = i;
apr_hash_set(ht, &child_handles[i], sizeof(HANDLE), score_idx);
apr_thread_mutex_unlock(child_lock);
} /* Start the listener only when workers are available */ if (!listener_started && threads_created) {
create_listener_thread();
listener_started = 1;
winnt_mpm_state = AP_MPMQ_RUNNING;
} if (threads_created == ap_threads_per_child) { break;
} /* Check to see if the child has been told to exit */ if (WaitForSingleObject(exit_event, 0) != WAIT_TIMEOUT) { break;
} /* wait for previous generation to clean up an entry in the scoreboard
*/
apr_sleep(1 * APR_USEC_PER_SEC);
}
/* Wait for one of these events: *exit_event: *Theexit_eventissignaledbytheparentprocesstonotify *thechildthatitistimetoexit. * *max_requests_per_child_event: *Thiseventissignaledbytheworkerthreadstoindicatethat *theprocesshashandledMaxConnectionsPerChildconnections. * *parentprocessexiting * *TIMEOUT: *Todoperiodicmaintenanceontheserver(checkforthreadexits, *numberofcompletioncontexts,etc.) * *XXX:threadexits*aren't*beingchecked. * *XXX:other_child-weneedtheprocesshandlestotheotherchildren *inordertomapthemtoapr_proc_other_child_read(whichisnot *namedwell,it'smorelikea_p_o_c_died.) * *XXX:however-ifwegeta_p_o_chandleinheritanceworking,and *theparentprocesscreatesotherchildrenandpassesthepipes *toourworkerprocesses,thenwehavenobusinessdoingsuch *thingsinthechild_mainloop,butshouldhappeninmaster_main.
*/ while (1) { #if !APR_HAS_OTHER_CHILD
rv = WaitForMultipleObjects(num_events, (HANDLE *)child_events, FALSE, INFINITE);
cld = rv - WAIT_OBJECT_0; #else /* THIS IS THE EXPECTED BUILD VARIATION -- APR_HAS_OTHER_CHILD */
rv = WaitForMultipleObjects(num_events, (HANDLE *)child_events, FALSE, 1000);
cld = rv - WAIT_OBJECT_0; if (rv == WAIT_TIMEOUT) {
apr_proc_other_child_refresh_all(APR_OC_REASON_RUNNING);
} else #endif if (rv == WAIT_FAILED) { /* Something serious is wrong */
ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(),
ap_server_conf, APLOGNO(00356) "Child: WAIT_FAILED -- shutting down server"); /* check handle validity to identify a possible culprit */ for (i = 0; i < num_events; i++) {
DWORD out_flags;
if (0 == GetHandleInformation(child_events[i], &out_flags)) {
ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(),
ap_server_conf, APLOGNO(02644) "Child: Event handle #%d (%pp) is invalid",
i, child_events[i]);
}
} break;
} elseif (cld == 0) { /* Exit event was signaled */
ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf, APLOGNO(00357) "Child: Exit event signaled. Child process is " "ending."); break;
} elseif (cld == 2) { /* The parent is dead. Shutdown the child process. */
ap_log_error(APLOG_MARK, APLOG_CRIT, 0, ap_server_conf, APLOGNO(02538) "Child: Parent process exited abruptly. Child process " "is ending"); break;
} else { /* MaxConnectionsPerChild event set by the worker threads. *Signaltheparenttorestart
*/
ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, APLOGNO(00358) "Child: Process exiting because it reached " "MaxConnectionsPerChild. Signaling the parent to " "restart a new child process.");
ap_signal_parent(SIGNAL_PARENT_RESTART); break;
}
}
/* *Timetoshutdownthechildprocess
*/
shutdown:
winnt_mpm_state = AP_MPMQ_STOPPING;
/* Close the listening sockets. Note, we must close the listeners *beforeclosinganyacceptsocketspendinginAcceptExtoprevent *memoryleaksinthekernel.
*/ for (lr = ap_listeners; lr ; lr = lr->next) {
apr_socket_close(lr->sd);
}
/* Tell the worker threads to exit */
workers_may_exit = 1;
/* Release the start_mutex to let the new process (in the restart *scenario)achancetobeginacceptingandservicingrequests
*/
rv = apr_proc_mutex_unlock(start_mutex); if (rv == APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf, APLOGNO(00359) "Child: Released the start mutex");
} else {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00360) "Child: Failure releasing the start mutex");
}
/* Shutdown the worker threads *PostworkerthreadsblockedontheThreadDispatchIOCompletionport
*/ while (g_blocked_threads > 0) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf, APLOGNO(00361) "Child: %d threads blocked on the completion port",
g_blocked_threads); for (i=g_blocked_threads; i > 0; i--) {
PostQueuedCompletionStatus(ThreadDispatchIOCP, 0,
IOCP_SHUTDOWN, NULL);
}
Sleep(1000);
} /* Empty the accept queue of completion contexts */
apr_thread_mutex_lock(qlock); while (qhead) {
CloseHandle(qhead->overlapped.hEvent);
closesocket(qhead->accept_socket);
qhead = qhead->next;
}
apr_thread_mutex_unlock(qlock);
/* Give busy threads a chance to service their connections *(nomorethantheglobalservertimeoutperiodwhich *wetrackinmsecremaining).
*/
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 *ofMAXIMUM_WAIT_OBJECTSthreads,takeabreather, *andinfrequentlyupdatetheerrorlog.
*/ 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, APLOGNO(00362) "Child: Waiting %d more seconds " "for %d worker threads to finish.",
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 *aftertheonewejustremoved,evenifit'sasinglethread. *Wedon'tblockhere.
*/
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;
} elseif (dwRet >= WAIT_ABANDONED_0) { /* We just got the ownership of the object, which *shouldhappenatmostMAXIMUM_WAIT_OBJECTStimes. *ItdoesNOTmeanthattheobjectissignaled.
*/ 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 */ if (threads_created) {
ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, APLOGNO(00363) "Child: Terminating %d threads that failed to exit.",
threads_created);
} for (i = 0; i < threads_created; i++) { int *idx;
TerminateThread(child_handles[i], 1);
CloseHandle(child_handles[i]); /* Reset the scoreboard entry for the thread we just whacked */
idx = apr_hash_get(ht, &child_handles[i], sizeof(HANDLE)); if (idx) {
ap_update_child_status_from_indexes(0, *idx, SERVER_DEAD, NULL);
}
}
ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, APLOGNO(00364) "Child: All worker threads have exited.");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.