Possible cause and solution of 100% cpu and massive log file in Windows

Ricardo V G ricvgdev at gmail.com
Wed Jul 18 20:24:30 UTC 2012


Hi,

Recently one of my employer's products (Trend Micro) that includes
nginx Windows binary had an issue similar to [1] where the error
message "[alert] 2548#2552: WaitForMultipleObjects() failed (6: The
handle is invalid)" is generated indefinitely hence creating a massive
log file. This wasn't new. Previously a change was done to the
configuration and it seemed that the issue was fixed. But it
reappeared and I was asked to look into it. Here's what I think could
be the problem along with the solution (patch) at the end of this
email.

When creating threads in a Windows system the following calls are made
(using release 1.2.2):

main() ->
    ngx_master_process_cycle() ->
        ngx_start_worker_processes() ->
            ngx_start_worker_processes() ->
                ngx_spawn_process()

The issue appears in one documented case when
ngx_start_worker_processes() fails to create a thread and the
following error message is generated:

    [alert] 2548#2552: the event "ngx_master_2548" was not signaled for 5s

When this message is generated, the execution jumps to the failed
label using a goto in os/win32/ngx_process.c at line 150. From there
on, the function attempts to close all opened handles and returns an
error in the form of an invalid process/thread ID. It's important to
note that the thread handle is closed if necessary but the global
variable that holds it is not cleared (i.e. the global array
ngx_processes[] defined in os/win32/ngx_process.c at line 17 keeps all
handles).


    ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, char *name,
ngx_int_t respawn)
    {
    [...]
        rc = WaitForMultipleObjects(2, events, 0, 5000);
    [...]
        switch (rc) {
    [...]
        case WAIT_FAILED:
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "WaitForSingleObject(\"%s\") failed",
                          ngx_master_process_event_name);

            goto failed;
        }
    [...]
    failed:
    [...]
        if (ngx_processes[s].handle) {
            ngx_close_handle(ngx_processes[s].handle);
        }

        return NGX_INVALID_PID;
    }


With the error in hand, ngx_start_worker_processes() in
os/win32/ngx_process_cycle.c breaks the loop and returns with the
number of threads that were successfully started.

    static ngx_int_t ngx_start_worker_processes(ngx_cycle_t *cycle,
ngx_int_t type)
    {
    [...]
        for (n = 0; n < ccf->worker_processes; n++) {
            if (ngx_spawn_process(cycle, "worker", type) == NGX_INVALID_PID) {
                break;
            }
        }

        return n;
    }


Finally ngx_master_process_cycle(), defined in
os/win32/ngx_process_cycle.c, checks if there are any threads and
exits if none has started. But if there are any threads that were
successfully started then it continues.

At this point, ngx_master_process_cycle() enters a forever loop where
a call is made to WaitForMultipleObjects() using all the handles
obtained from ngx_processes[] including the handle that was already
closed by ngx_start_worker_processes().

    void ngx_master_process_cycle(ngx_cycle_t *cycle)
    {
    [...]
        if (ngx_start_worker_processes(cycle, NGX_PROCESS_RESPAWN) == 0) {
            exit(2);
        }
    [...]
        for ( ;; ) {
    [...]
            for (n = 0; n < ngx_last_process; n++) {
                if (ngx_processes[n].handle) {
                    events[nev++] = ngx_processes[n].handle;
                }
            }
    [...]
            ev = WaitForMultipleObjects(nev, events, 0, timeout);
    [...]
            if (ev == WAIT_FAILED) {
                ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
                              "WaitForMultipleObjects() failed");

                continue;
            }
    [...]
        }
    }


Because the handle is invalid, WaitForMultipleObjects() returns
immediately with WAIT_FAILED triggering the first error shown in this
email followed by a "continue" statement which goes back at the start
of the loop again still with the invalid handle. This will be repeated
forever since the handle is always invalid.

I believe that the simplest fix that doesn't modify the logic of the
program is to clear the thread handle in ngx_process.c failed section
once they have been closed by setting the variable zero as shown
below.

What do you think?

/ricardo

[1] nginx windows freezes with 100% CPU and massive error log
http://forum.nginx.org/read.php?2,27839,27839



--- a/src/os/win32/ngx_process.c
+++ b/src/os/win32/ngx_process.c
@@ -196,6 +196,7 @@ failed:

     if (ngx_processes[s].handle) {
         ngx_close_handle(ngx_processes[s].handle);
+        ngx_processes[s].handle = 0;
     }

     return NGX_INVALID_PID;



More information about the nginx-devel mailing list