diff src/os/win32/ngx_process_cycle.c @ 2725:d43d73277c5c

Win32 master/workers model
author Igor Sysoev <igor@sysoev.ru>
date Mon, 20 Apr 2009 06:08:47 +0000
parents c9da3e4dc706
children ae81441e23f4
line wrap: on
line diff
--- a/src/os/win32/ngx_process_cycle.c
+++ b/src/os/win32/ngx_process_cycle.c
@@ -10,70 +10,238 @@
 #include <nginx.h>
 
 
-static ngx_thread_value_t __stdcall ngx_worker_thread_cycle(void *data);
-static void ngx_process_tray(ngx_cycle_t *cycle);
-static long __stdcall ngx_window_procedure(HWND window, u_int message,
-    u_int wparam, long lparam);
-
-#if 0
-ngx_pid_t     ngx_new_binary;
-
-sig_atomic_t  ngx_reap;
-sig_atomic_t  ngx_timer;
-
-#endif
-
-ngx_uint_t    ngx_process;
-ngx_pid_t     ngx_pid;
-ngx_uint_t    ngx_threaded;
-ngx_uint_t    ngx_inherited;
+static void ngx_process_init(ngx_cycle_t *cycle);
+static void ngx_console_init(ngx_cycle_t *cycle);
+static int __stdcall ngx_console_handler(u_long type);
+static ngx_int_t ngx_create_events(ngx_cycle_t *cycle);
+static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t type);
+static void ngx_reopen_worker_processes(ngx_cycle_t *cycle);
+static void ngx_quit_worker_processes(ngx_cycle_t *cycle, ngx_uint_t old);
+static void ngx_terminate_worker_processes(ngx_cycle_t *cycle);
+static ngx_uint_t ngx_reap_worker(ngx_cycle_t *cycle, HANDLE h);
+static void ngx_master_process_exit(ngx_cycle_t *cycle);
+static void ngx_worker_process_cycle(ngx_cycle_t *cycle, char *mevn);
+static void ngx_worker_process_exit(ngx_cycle_t *cycle);
+static ngx_thread_value_t __stdcall ngx_worker_thread(void *data);
+static ngx_thread_value_t __stdcall ngx_cache_manager_thread(void *data);
+static void ngx_cache_manager_process_handler(void);
 
 
-sig_atomic_t  ngx_terminate;
-sig_atomic_t  ngx_quit;
-ngx_uint_t    ngx_exiting;
-
-#if 0
+ngx_uint_t     ngx_process;
+ngx_pid_t      ngx_pid;
+ngx_uint_t     ngx_threaded;
 
-sig_atomic_t  ngx_noaccept;
-sig_atomic_t  ngx_reconfigure;
-sig_atomic_t  ngx_reopen;
-sig_atomic_t  ngx_change_binary;
+ngx_uint_t     ngx_inherited;
+ngx_pid_t      ngx_new_binary;
 
-#endif
+sig_atomic_t   ngx_terminate;
+sig_atomic_t   ngx_quit;
+sig_atomic_t   ngx_reopen;
+sig_atomic_t   ngx_reconfigure;
+ngx_uint_t     ngx_exiting;
 
 
-static HMENU  ngx_menu;
+HANDLE         ngx_master_process_event;
+char           ngx_master_process_event_name[NGX_PROCESS_SYNC_NAME];
+
+static HANDLE  ngx_stop_event;
+static char    ngx_stop_event_name[NGX_PROCESS_SYNC_NAME];
+static HANDLE  ngx_quit_event;
+static char    ngx_quit_event_name[NGX_PROCESS_SYNC_NAME];
+static HANDLE  ngx_reopen_event;
+static char    ngx_reopen_event_name[NGX_PROCESS_SYNC_NAME];
+static HANDLE  ngx_reload_event;
+static char    ngx_reload_event_name[NGX_PROCESS_SYNC_NAME];
+
+HANDLE         ngx_cache_manager_mutex;
+char           ngx_cache_manager_mutex_name[NGX_PROCESS_SYNC_NAME];
+HANDLE         ngx_cache_manager_event;
 
 
 void
 ngx_master_process_cycle(ngx_cycle_t *cycle)
 {
-    ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "master mode is not supported");
+    u_long      nev, ev, timeout;
+    ngx_err_t   err;
+    ngx_int_t   n;
+    ngx_msec_t  timer;
+    ngx_uint_t  live;
+    HANDLE      events[MAXIMUM_WAIT_OBJECTS];
+
+    ngx_process_init(cycle);
+
+    ngx_sprintf((u_char *) ngx_master_process_event_name,
+                "ngx_master_%s%Z", ngx_unique);
+
+    if (ngx_process == NGX_PROCESS_WORKER) {
+        ngx_worker_process_cycle(cycle, ngx_master_process_event_name);
+        return;
+    }
+
+    ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0, "master started");
+
+    ngx_console_init(cycle);
+
+    SetEnvironmentVariable("ngx_unique", ngx_unique);
+
+    ngx_master_process_event = CreateEvent(NULL, 1, 0,
+                                           ngx_master_process_event_name);
+    if (ngx_master_process_event == NULL) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      "CreateEvent(\"%s\") failed",
+                      ngx_master_process_event_name);
+        exit(2);
+    }
+
+    if (ngx_create_events(cycle) != NGX_OK) {
+        exit(2);
+    }
+
+    ngx_sprintf((u_char *) ngx_cache_manager_mutex_name,
+                "ngx_cache_manager_mutex_%s%Z", ngx_unique);
+
+    ngx_cache_manager_mutex = CreateMutex(NULL, 0,
+                                          ngx_cache_manager_mutex_name);
+    if (ngx_cache_manager_mutex == NULL) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                   "CreateMutex(\"%s\") failed", ngx_cache_manager_mutex_name);
+        exit(2);
+    }
+
+
+    events[0] = ngx_stop_event;
+    events[1] = ngx_quit_event;
+    events[2] = ngx_reopen_event;
+    events[3] = ngx_reload_event;
+
+    ngx_close_listening_sockets(cycle);
+
+    ngx_start_worker_processes(cycle, NGX_PROCESS_RESPAWN);
+
+    timer = 0;
+    timeout = INFINITE;
+
+    for ( ;; ) {
+
+        nev = 4;
+        for (n = 0; n < ngx_last_process; n++) {
+            if (ngx_processes[n].handle) {
+                events[nev++] = ngx_processes[n].handle;
+            }
+        }
+
+        if (timer) {
+            timeout = timer > ngx_current_msec ? timer - ngx_current_msec : 0;
+        }
+
+        ev = WaitForMultipleObjects(nev, events, 0, timeout);
+
+        err = ngx_errno;
+        ngx_time_update(0, 0);
+
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+                       "master WaitForMultipleObjects: %ul", ev);
+
+        if (ev == WAIT_OBJECT_0) {
+            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
 
-    exit(2);
+            if (ResetEvent(ngx_stop_event) == 0) {
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                              "ResetEvent(\"%s\") failed", ngx_stop_event_name);
+            }
+
+            if (timer == 0) {
+                timer = ngx_current_msec + 5000;
+            }
+
+            ngx_terminate = 1;
+            ngx_quit_worker_processes(cycle, 0);
+
+            continue;
+        }
+
+        if (ev == WAIT_OBJECT_0 + 1) {
+            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "shutting down");
+
+            if (ResetEvent(ngx_quit_event) == 0) {
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                              "ResetEvent(\"%s\") failed", ngx_quit_event_name);
+            }
+
+            ngx_quit = 1;
+            ngx_quit_worker_processes(cycle, 0);
+
+            continue;
+        }
+
+        if (ev == WAIT_OBJECT_0 + 2) {
+            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
+
+            if (ResetEvent(ngx_reopen_event) == 0) {
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                              "ResetEvent(\"%s\") failed",
+                              ngx_reopen_event_name);
+            }
+
+            ngx_reopen_files(cycle, -1);
+            ngx_reopen_worker_processes(cycle);
+
+            continue;
+        }
+
+        if (ev == WAIT_OBJECT_0 + 3) {
+            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");
+
+            if (ResetEvent(ngx_reload_event) == 0) {
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                              "ResetEvent(\"%s\") failed",
+                              ngx_reload_event_name);
+            }
+
+            ngx_start_worker_processes(cycle, NGX_PROCESS_JUST_RESPAWN);
+            ngx_quit_worker_processes(cycle, 1);
+
+            continue;
+        }
+
+        if (ev > WAIT_OBJECT_0 + 3 && ev < WAIT_OBJECT_0 + nev) {
+
+            ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0, "reap worker");
+
+            live = ngx_reap_worker(cycle, events[ev]);
+
+            if (!live && (ngx_terminate || ngx_quit)) {
+                ngx_master_process_exit(cycle);
+            }
+
+            continue;
+        }
+
+        if (ev == WAIT_TIMEOUT) {
+            ngx_terminate_worker_processes(cycle);
+
+            ngx_master_process_exit(cycle);
+        }
+
+        if (ev == WAIT_FAILED) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+                          "WaitForMultipleObjects() failed");
+
+            continue;
+        }
+
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+            "WaitForMultipleObjects() returned unexpected value %ul", ev);
+    }
 }
 
 
-void
-ngx_single_process_cycle(ngx_cycle_t *cycle)
+static void
+ngx_process_init(ngx_cycle_t *cycle)
 {
-    ngx_int_t         i;
     ngx_err_t         err;
-    ngx_tid_t         tid;
     ngx_core_conf_t  *ccf;
 
-    ngx_init_temp_number();
-
-    for (i = 0; ngx_modules[i]; i++) {
-        if (ngx_modules[i]->init_process) {
-            if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
-                /* fatal */
-                exit(2);
-            }
-        }
-    }
-
     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
 
     if (ngx_init_threads(ngx_threads_n, ccf->thread_stack_size, cycle)
@@ -90,29 +258,288 @@ ngx_single_process_cycle(ngx_cycle_t *cy
         /* fatal */
         exit(2);
     }
+}
 
-    if (ngx_create_thread(&tid, ngx_worker_thread_cycle, NULL, cycle->log)
-        != 0)
-    {
-        /* fatal */
-        exit(2);
+
+static void
+ngx_console_init(ngx_cycle_t *cycle)
+{
+    ngx_core_conf_t  *ccf;
+
+    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+    if (ccf->daemon) {
+        if (FreeConsole() == 0) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "FreeConsole() failed");
+        }
+
+        return;
+    }
+
+    if (SetConsoleCtrlHandler(ngx_console_handler, 1) == 0) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      "SetConsoleCtrlHandler() failed");
+    }
+}
+
+
+static int __stdcall
+ngx_console_handler(u_long type)
+{
+    char  *msg;
+
+    switch (type) {
+
+    case CTRL_C_EVENT:
+        msg = "Ctrl-C pressed, exiting";
+        break;
+
+    case CTRL_BREAK_EVENT:
+        msg = "Ctrl-Break pressed, exiting";
+        break;
+
+    case CTRL_CLOSE_EVENT:
+        msg = "console closing, exiting";
+        break;
+
+    case CTRL_LOGOFF_EVENT:
+        msg = "user logs off, exiting";
+        break;
+
+    default:
+        return 0;
+    }
+
+    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, msg);
+
+    if (ngx_stop_event == NULL) {
+        return 1;
+    }
+
+    if (SetEvent(ngx_stop_event) == 0) {
+        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+                      "SetEvent(\"%s\") failed", ngx_stop_event_name);
     }
 
-    ngx_process_tray(cycle);
+    return 1;
+}
+
+
+static ngx_int_t
+ngx_create_events(ngx_cycle_t *cycle)
+{
+    ngx_sprintf((u_char *) ngx_stop_event_name, "ngx_stop_%s%Z", ngx_unique);
+
+    ngx_stop_event = CreateEvent(NULL, 1, 0, ngx_stop_event_name);
+    if (ngx_stop_event == NULL) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      "CreateEvent(\"%s\") failed", ngx_stop_event_name);
+        return NGX_ERROR;
+    }
+
+
+    ngx_sprintf((u_char *) ngx_quit_event_name, "ngx_quit_%s%Z", ngx_unique);
+
+    ngx_quit_event = CreateEvent(NULL, 1, 0, ngx_quit_event_name);
+    if (ngx_quit_event == NULL) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      "CreateEvent(\"%s\") failed", ngx_quit_event_name);
+        return NGX_ERROR;
+    }
+
+
+    ngx_sprintf((u_char *) ngx_reopen_event_name,
+                "ngx_reopen_%s%Z", ngx_unique);
+
+    ngx_reopen_event = CreateEvent(NULL, 1, 0, ngx_reopen_event_name);
+    if (ngx_reopen_event == NULL) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      "CreateEvent(\"%s\") failed", ngx_reopen_event_name);
+        return NGX_ERROR;
+    }
+
+
+    ngx_sprintf((u_char *) ngx_reload_event_name,
+                "ngx_reload_%s%Z", ngx_unique);
+
+    ngx_reload_event = CreateEvent(NULL, 1, 0, ngx_reload_event_name);
+    if (ngx_reload_event == NULL) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      "CreateEvent(\"%s\") failed", ngx_reload_event_name);
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
+static void
+ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t type)
+{
+    ngx_int_t         n;
+    ngx_core_conf_t  *ccf;
+
+    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes");
+
+    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+    for (n = 0; n < ccf->worker_processes; n++) {
+        if (ngx_spawn_process(cycle, "worker", type) == NGX_INVALID_PID) {
+            return;
+        }
+    }
 }
 
 
-static ngx_thread_value_t __stdcall
-ngx_worker_thread_cycle(void *data)
+static void
+ngx_reopen_worker_processes(ngx_cycle_t *cycle)
+{
+    ngx_int_t  n;
+
+    for (n = 0; n < ngx_last_process; n++) {
+
+        if (ngx_processes[n].handle == NULL) {
+            continue;
+        }
+
+        if (SetEvent(ngx_processes[n].reopen) == 0) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "SetEvent(\"%s\") failed",
+                          ngx_processes[n].reopen_event);
+        }
+    }
+}
+
+
+static void
+ngx_quit_worker_processes(ngx_cycle_t *cycle, ngx_uint_t old)
 {
-    ngx_cycle_t  *cycle;
+    ngx_int_t  n;
+
+    for (n = 0; n < ngx_last_process; n++) {
+
+        ngx_log_debug5(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+                       "process: %d %P %p e:%d t:%d r:%d j:%d",
+                       n,
+                       ngx_processes[n].pid,
+                       ngx_processes[n].handle,
+                       ngx_processes[n].exiting,
+                       ngx_processes[n].just_respawn);
+
+        if (old && ngx_processes[n].just_respawn) {
+            ngx_processes[n].just_respawn = 0;
+            continue;
+        }
+
+        if (ngx_processes[n].handle == NULL) {
+            continue;
+        }
+
+        if (SetEvent(ngx_processes[n].quit) == 0) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "SetEvent(\"%s\") failed",
+                          ngx_processes[n].quit_event);
+        }
+
+        ngx_processes[n].exiting = 1;
+    }
+}
+
+
+static void
+ngx_terminate_worker_processes(ngx_cycle_t *cycle)
+{
+    ngx_int_t  n;
+
+    for (n = 0; n < ngx_last_process; n++) {
+
+        if (ngx_processes[n].handle == NULL) {
+            continue;
+        }
+
+        if (TerminateProcess(ngx_processes[n].handle, 0) == 0) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "TerminateProcess(\"%p\") failed",
+                          ngx_processes[n].handle);
+        }
+
+        ngx_processes[n].exiting = 1;
 
-    cycle = (ngx_cycle_t *) ngx_cycle;
+        ngx_close_handle(ngx_processes[n].reopen);
+        ngx_close_handle(ngx_processes[n].quit);
+        ngx_close_handle(ngx_processes[n].term);
+        ngx_close_handle(ngx_processes[n].handle);
+    }
+}
+
+
+static ngx_uint_t
+ngx_reap_worker(ngx_cycle_t *cycle, HANDLE h)
+{
+    u_long     code;
+    ngx_int_t  n;
+
+    for (n = 0; n < ngx_last_process; n++) {
+
+        if (ngx_processes[n].handle != h) {
+            continue;
+        }
+
+        if (GetExitCodeProcess(h, &code) == 0) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                          "GetExitCodeProcess(%P) failed",
+                          ngx_processes[n].pid);
+        }
+
+        ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
+                      "%s process %P exited with code %Xul",
+                      ngx_processes[n].name, ngx_processes[n].pid, code);
+
+        ngx_close_handle(ngx_processes[n].reopen);
+        ngx_close_handle(ngx_processes[n].quit);
+        ngx_close_handle(ngx_processes[n].term);
+        ngx_close_handle(h);
 
-    while (!ngx_quit) {
-        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");
+        ngx_processes[n].handle = NULL;
+        ngx_processes[n].term = NULL;
+        ngx_processes[n].quit = NULL;
+        ngx_processes[n].reopen = NULL;
+
+        if (!ngx_processes[n].exiting && !ngx_terminate && !ngx_quit) {
+
+            if (ngx_spawn_process(cycle, ngx_processes[n].name, n)
+                == NGX_INVALID_PID)
+            {
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                              "can not respawn %s", ngx_processes[n].name);
+
+                if (n == ngx_last_process - 1) {
+                    ngx_last_process--;
+                }
+            }
+        }
 
-        ngx_process_events_and_timers(cycle);
+        goto found;
+    }
+
+    ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "unknown process handle %p", h);
+
+found:
+
+    for (n = 0; n < ngx_last_process; n++) {
+
+        ngx_log_debug5(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+                       "process: %d %P %p e:%d t:%d r:%d j:%d",
+                       n,
+                       ngx_processes[n].pid,
+                       ngx_processes[n].handle,
+                       ngx_processes[n].exiting,
+                       ngx_processes[n].just_respawn);
+
+        if (ngx_processes[n].handle) {
+            return 1;
+        }
     }
 
     return 0;
@@ -120,164 +547,544 @@ ngx_worker_thread_cycle(void *data)
 
 
 static void
-ngx_process_tray(ngx_cycle_t *cycle)
+ngx_master_process_exit(ngx_cycle_t *cycle)
+{
+    ngx_uint_t  i;
+
+    ngx_delete_pidfile(cycle);
+
+    ngx_close_handle(ngx_cache_manager_mutex);
+    ngx_close_handle(ngx_stop_event);
+    ngx_close_handle(ngx_quit_event);
+    ngx_close_handle(ngx_reopen_event);
+    ngx_close_handle(ngx_reload_event);
+    ngx_close_handle(ngx_master_process_event);
+
+    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exit");
+
+    for (i = 0; ngx_modules[i]; i++) {
+        if (ngx_modules[i]->exit_master) {
+            ngx_modules[i]->exit_master(cycle);
+        }
+    }
+
+    ngx_destroy_pool(cycle->pool);
+
+    exit(0);
+}
+
+
+static void
+ngx_worker_process_cycle(ngx_cycle_t *cycle, char *mevn)
 {
-    int        rc;
-    MSG        message;
-    HWND       window;
-    HMENU      menu;
-    HICON      icon, tray;
-    WNDCLASS   wc;
-    HINSTANCE  instance;
+    char        wtevn[NGX_PROCESS_SYNC_NAME];
+    char        wqevn[NGX_PROCESS_SYNC_NAME];
+    char        wroevn[NGX_PROCESS_SYNC_NAME];
+    HANDLE      mev, events[3];
+    u_long      nev, ev;
+    ngx_err_t   err;
+    ngx_tid_t   wtid, cmtid;
+    ngx_log_t  *log;
+
+    log = cycle->log;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "worker started");
+
+    ngx_sprintf((u_char *) wtevn, "ngx_worker_term_%ul%Z", ngx_pid);
+    events[0] = CreateEvent(NULL, 1, 0, wtevn);
+    if (events[0] == NULL) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      "CreateEvent(\"%s\") failed", wtevn);
+        goto failed;
+    }
+
+    ngx_sprintf((u_char *) wqevn, "ngx_worker_quit_%ul%Z", ngx_pid);
+    events[1] = CreateEvent(NULL, 1, 0, wqevn);
+    if (events[1] == NULL) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      "CreateEvent(\"%s\") failed", wqevn);
+        goto failed;
+    }
+
+    ngx_sprintf((u_char *) wroevn, "ngx_worker_reopen_%ul%Z", ngx_pid);
+    events[2] = CreateEvent(NULL, 1, 0, wroevn);
+    if (events[2] == NULL) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      "CreateEvent(\"%s\") failed", wroevn);
+        goto failed;
+    }
+
+    mev = OpenEvent(EVENT_MODIFY_STATE, 0, mevn);
+    if (mev == NULL) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      "OpenEvent(\"%s\") failed", mevn);
+        goto failed;
+    }
+
+    if (SetEvent(mev) == 0) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      "SetEvent(\"%s\") failed", mevn);
+        goto failed;
+    }
+
+
+    ngx_sprintf((u_char *) ngx_cache_manager_mutex_name,
+                "ngx_cache_manager_mutex_%s%Z", ngx_unique);
+
+    ngx_cache_manager_mutex = OpenMutex(SYNCHRONIZE, 0,
+                                        ngx_cache_manager_mutex_name);
+    if (ngx_cache_manager_mutex == NULL) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      "OpenMutex(\"%s\") failed", ngx_cache_manager_mutex_name);
+        goto failed;
+    }
+
+    ngx_cache_manager_event = CreateEvent(NULL, 1, 0, NULL);
+    if (ngx_cache_manager_event == NULL) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      "CreateEvent(\"ngx_cache_manager_event\") failed");
+        goto failed;
+    }
+
+
+    if (ngx_create_thread(&wtid, ngx_worker_thread, NULL, log) != 0) {
+        goto failed;
+    }
+
+    if (ngx_create_thread(&cmtid, ngx_cache_manager_thread, NULL, log) != 0) {
+        goto failed;
+    }
+
+    for ( ;; ) {
+        ev = WaitForMultipleObjects(3, events, 0, INFINITE);
+
+        err = ngx_errno;
+        ngx_time_update(0, 0);
 
-    instance = GetModuleHandle(NULL);
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,
+                       "worker WaitForMultipleObjects: %ul", ev);
+
+        if (ev == WAIT_OBJECT_0) {
+            ngx_terminate = 1;
+            ngx_log_error(NGX_LOG_NOTICE, log, 0, "exiting");
+
+            if (ResetEvent(events[0]) == 0) {
+                ngx_log_error(NGX_LOG_ALERT, log, 0,
+                              "ResetEvent(\"%s\") failed", wtevn);
+            }
+
+            break;
+        }
+
+        if (ev == WAIT_OBJECT_0 + 1) {
+            ngx_quit = 1;
+            ngx_log_error(NGX_LOG_NOTICE, log, 0, "gracefully shutting down");
+            break;
+        }
+
+        if (ev == WAIT_OBJECT_0 + 2) {
+            ngx_reopen = 1;
+            ngx_log_error(NGX_LOG_NOTICE, log, 0, "reopening logs");
+
+            if (ResetEvent(events[2]) == 0) {
+                ngx_log_error(NGX_LOG_ALERT, log, 0,
+                              "ResetEvent(\"%s\") failed", wroevn);
+            }
+
+            continue;
+        }
+
+        if (ev == WAIT_FAILED) {
+            ngx_log_error(NGX_LOG_ALERT, log, err,
+                          "WaitForMultipleObjects() failed");
+
+            goto failed;
+        }
+    }
+
+    /* wait threads */
 
-    icon = LoadIcon(instance, "nginx");
-    if (icon == NULL) {
-        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                      "LoadIcon(\"nginx\") failed");
-        /* fatal */
-        exit(2);
+    if (SetEvent(ngx_cache_manager_event) == 0) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                      "SetEvent(\"ngx_cache_manager_event\") failed");
+    }
+
+    events[1] = wtid;
+    events[2] = cmtid;
+
+    nev = 3;
+
+    for ( ;; ) {
+        ev = WaitForMultipleObjects(nev, events, 0, INFINITE);
+
+        err = ngx_errno;
+        ngx_time_update(0, 0);
+
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,
+                       "worker exit WaitForMultipleObjects: %ul", ev);
+
+        if (ev == WAIT_OBJECT_0) {
+            break;
+        }
+
+        if (ev == WAIT_OBJECT_0 + 1) {
+            if (nev == 2) {
+                break;
+            }
+
+            events[1] = events[2];
+            nev = 2;
+            continue;
+        }
+
+        if (ev == WAIT_OBJECT_0 + 2) {
+            nev = 2;
+            continue;
+        }
+
+        if (ev == WAIT_FAILED) {
+            ngx_log_error(NGX_LOG_ALERT, log, err,
+                          "WaitForMultipleObjects() failed");
+            break;
+        }
     }
 
-    tray = LoadIcon(instance, "tray");
-    if (icon == NULL) {
-        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                      "LoadIcon(\"tray\") failed");
-        /* fatal */
-        exit(2);
+    ngx_close_handle(ngx_cache_manager_event);
+    ngx_close_handle(events[0]);
+    ngx_close_handle(events[1]);
+    ngx_close_handle(events[2]);
+    ngx_close_handle(mev);
+
+    ngx_worker_process_exit(cycle);
+
+failed:
+
+    exit(2);
+}
+
+
+static ngx_thread_value_t __stdcall
+ngx_worker_thread(void *data)
+{
+    ngx_int_t          n;
+    ngx_uint_t         i;
+    ngx_cycle_t       *cycle;
+    ngx_connection_t  *c;
+
+    cycle = (ngx_cycle_t *) ngx_cycle;
+
+    ngx_init_temp_number();
+
+    for (n = 0; ngx_modules[n]; n++) {
+        if (ngx_modules[n]->init_process) {
+            if (ngx_modules[n]->init_process(cycle) == NGX_ERROR) {
+                /* fatal */
+                exit(2);
+            }
+        }
+    }
+
+    while (!ngx_quit) {
+
+        if (ngx_exiting) {
+
+            c = cycle->connections;
+
+            for (i = 0; i < cycle->connection_n; i++) {
+
+                /* THREAD: lock */
+
+                if (c[i].fd != -1 && c[i].idle) {
+                    c[i].close = 1;
+                    c[i].read->handler(c[i].read);
+                }
+            }
+
+            if (ngx_event_timer_rbtree.root
+                == ngx_event_timer_rbtree.sentinel)
+            {
+                break;
+            }
+        }
+
+        ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0, "worker cycle");
+
+        ngx_process_events_and_timers(cycle);
+
+        if (ngx_terminate) {
+            return 0;
+        }
+
+        if (ngx_quit) {
+            ngx_quit = 0;
+
+            if (!ngx_exiting) {
+                ngx_close_listening_sockets(cycle);
+                ngx_exiting = 1;
+            }
+        }
+
+        if (ngx_reopen) {
+            ngx_reopen = 0;
+            ngx_reopen_files(cycle, -1);
+        }
+    }
+
+    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
+
+    return 0;
+}
+
+
+static void
+ngx_worker_process_exit(ngx_cycle_t *cycle)
+{
+    ngx_uint_t         i;
+    ngx_connection_t  *c;
+
+    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exit");
+
+    for (i = 0; ngx_modules[i]; i++) {
+        if (ngx_modules[i]->exit_process) {
+            ngx_modules[i]->exit_process(cycle);
+        }
+    }
+
+    if (ngx_exiting) {
+        c = cycle->connections;
+        for (i = 0; i < cycle->connection_n; i++) {
+            if (c[i].fd != -1
+                && c[i].read
+                && !c[i].read->accept
+                && !c[i].read->channel
+                && !c[i].read->resolver)
+            {
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                              "open socket #%d left in connection %ui",
+                              c[i].fd, i);
+            }
+        }
     }
 
-    menu = LoadMenu(instance, "nginx");
-    if (menu == NULL) {
-        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                      "LoadMenu() failed");
-        /* fatal */
+    ngx_destroy_pool(cycle->pool);
+
+    exit(0);
+}
+
+
+static ngx_thread_value_t __stdcall
+ngx_cache_manager_thread(void *data)
+{
+    u_long        ev;
+    HANDLE        events[2];
+    ngx_err_t     err;
+    ngx_cycle_t  *cycle;
+
+    cycle = (ngx_cycle_t *) ngx_cycle;
+
+    events[0] = ngx_cache_manager_event;
+    events[1] = ngx_cache_manager_mutex;
+
+    for ( ;; ) {
+        ev = WaitForMultipleObjects(2, events, 0, INFINITE);
+
+        err = ngx_errno;
+        ngx_time_update(0, 0);
+
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+                       "cache manager WaitForMultipleObjects: %ul", ev);
+
+        if (ev == WAIT_FAILED) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+                          "WaitForMultipleObjects() failed");
+        }
+
+        /*
+         * ev == WAIT_OBJECT_0
+         * ev == WAIT_OBJECT_0 + 1
+         * ev == WAIT_ABANDONED_0 + 1
+         */
+
+        if (ngx_terminate || ngx_quit) {
+            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
+            return 0;
+        }
+
+       break;
+    }
+
+    for ( ;; ) {
+
+        if (ngx_terminate || ngx_quit) {
+            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
+            break;
+        }
+
+        ngx_cache_manager_process_handler();
+    }
+
+    if (ReleaseMutex(ngx_cache_manager_mutex) == 0) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      "ReleaseMutex() failed");
+    }
+
+    return 0;
+}
+
+
+static void
+ngx_cache_manager_process_handler(void)
+{
+    u_long        ev;
+    time_t        next, n;
+    ngx_uint_t    i;
+    ngx_path_t  **path;
+
+    next = 60 * 60;
+
+    path = ngx_cycle->pathes.elts;
+    for (i = 0; i < ngx_cycle->pathes.nelts; i++) {
+
+        if (path[i]->manager) {
+            n = path[i]->manager(path[i]->data);
+
+            next = (n <= next) ? n : next;
+
+            ngx_time_update(0, 0);
+        }
+    }
+
+    if (next == 0) {
+        next = 1;
+    }
+
+    ev = WaitForSingleObject(ngx_cache_manager_event, (u_long) next * 1000);
+
+    if (ev != WAIT_TIMEOUT) {
+
+        ngx_time_update(0, 0);
+
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
+                       "cache manager WaitForSingleObject: %ul", ev);
+    }
+}
+
+
+void
+ngx_single_process_cycle(ngx_cycle_t *cycle)
+{
+    ngx_int_t  i;
+    ngx_tid_t  tid;
+
+    ngx_init_temp_number();
+
+    for (i = 0; ngx_modules[i]; i++) {
+        if (ngx_modules[i]->init_process) {
+            if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
+                /* fatal */
+                exit(2);
+            }
+        }
+    }
+
+    ngx_process_init(cycle);
+
+    ngx_console_init(cycle);
+
+    if (ngx_create_events(cycle) != NGX_OK) {
         exit(2);
     }
 
-    ngx_menu = GetSubMenu(menu, 0);
-    if (ngx_menu == NULL) {
-        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                      "GetSubMenu() failed");
-        /* fatal */
-        exit(2);
-    }
-
-
-    wc.style = CS_HREDRAW|CS_VREDRAW;
-    wc.lpfnWndProc = ngx_window_procedure;
-    wc.cbClsExtra = 0;
-    wc.cbWndExtra = 0;
-    wc.hInstance = instance;
-    wc.hIcon = icon;
-    wc.hCursor = NULL;
-    wc.hbrBackground = NULL;
-    wc.lpszMenuName =  NULL;
-    wc.lpszClassName = "nginx";
-
-    if (RegisterClass(&wc) == 0) {
-        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                      "RegisterClass() failed");
-        /* fatal */
-        exit(2);
-    }
-
-    window = CreateWindow("nginx", "nginx", WS_OVERLAPPEDWINDOW,
-                          CW_USEDEFAULT, CW_USEDEFAULT,
-                          CW_USEDEFAULT, CW_USEDEFAULT,
-                          NULL, NULL, instance, NULL);
-
-    if (window == NULL) {
-        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                      "CreateWindow() failed");
+    if (ngx_create_thread(&tid, ngx_worker_thread, NULL, cycle->log) != 0) {
         /* fatal */
         exit(2);
     }
 
-
-    if (ngx_system_tray_icon(window, NIM_ADD, tray, (u_char *) " nginx")
-        != NGX_OK)
-    {
-        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
-                      "Shell_NotifyIcon(NIM_ADD) failed");
-        /* fatal */
-        exit(2);
-    }
-
-    for ( ;; ) {
-        rc = GetMessage(&message, NULL, 0, 0);
-
-        if (rc == -1) {
-            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
-                          "GetMessage() failed");
-            continue;
-        }
-
-        if (rc == 0) {
-            exit(0);
-        }
-
-        TranslateMessage(&message);
-        DispatchMessage(&message);
-    }
+    /* STUB */
+    WaitForSingleObject(ngx_stop_event, INFINITE);
 }
 
 
-static long __stdcall
-ngx_window_procedure(HWND window, u_int message, u_int wparam, long lparam)
+ngx_int_t
+ngx_signal_process(ngx_cycle_t *cycle, char *sig)
 {
-    POINT  mouse;
+    size_t            n;
+    HANDLE            ev;
+    ngx_int_t         rc, pid;
+    ngx_file_t        file;
+    ngx_core_conf_t  *ccf;
+    u_char            buf[NGX_INT64_LEN + 2];
+    char              evn[NGX_PROCESS_SYNC_NAME];
 
-    switch (message) {
+    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "signal process started");
+
+    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+    file.name = ccf->pid;
+    file.log = cycle->log;
 
-    case NGX_WM_TRAY:
-        if (lparam == WM_RBUTTONDOWN) {
-            if (GetCursorPos(&mouse) == 0) {
-                ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
-                              "GetCursorPos() failed");
-                return 0;
-            }
+    file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY,
+                            NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS);
+
+    if (file.fd == NGX_INVALID_FILE) {
+        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,
+                      ngx_open_file_n " \"%s\" failed", file.name.data);
+        return 1;
+    }
 
-            if (SetForegroundWindow(window) == 0) {
-                ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
-                              "SetForegroundWindow() failed");
-                return 0;
-            }
+    rc = 1;
+
+    n = ngx_read_file(&file, buf, NGX_INT64_LEN + 2, 0);
+
+    if (n == NGX_ERROR) {
+        goto failed;
+    }
+
+    while (n-- && (buf[n] == CR || buf[n] == LF)) { /* void */ }
+
+    pid = ngx_atoi(buf, ++n);
 
-            if (TrackPopupMenu(ngx_menu, TPM_RIGHTBUTTON,
-                               mouse.x, mouse.y, 0, window, NULL)
-                == 0)
-            {
-                ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
-                              "TrackPopupMenu() failed");
-                return 0;
-            }
-        }
+    if (pid == NGX_ERROR) {
+        ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
+                      "invalid PID number \"%*s\" in \"%s\"",
+                      n, buf, file.name.data);
+        goto failed;
+    }
 
-        return 0;
+    ngx_sprintf((u_char *) evn, "ngx_%s_%ul%Z", sig, pid);
+
+    ev = OpenEvent(EVENT_MODIFY_STATE, 0, evn);
+    if (ev == NULL) {
+        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,
+                      "OpenEvent(\"%s\") failed", evn);
+        goto failed;
+    }
 
-    case WM_COMMAND:
-        if (wparam == NGX_WM_ABOUT) {
-            ngx_message_box("nginx", MB_OK, 0,
-                            NGINX_VER CRLF "(C) 2002-2009 Igor Sysoev");
-            return 0;
-        }
+    if (SetEvent(ev) == 0) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      "SetEvent(\"%s\") failed", evn);
+    } else {
+        rc = 0;
+    }
+
+    ngx_close_handle(ev);
+
+failed:
 
-        if (wparam == NGX_WM_EXIT) {
-            if (ngx_system_tray_icon(window, NIM_DELETE, NULL, NULL)
-                != NGX_OK)
-            {
-                ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
-                              "Shell_NotifyIcon(NIM_DELETE) failed");
-            }
-        }
+    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      ngx_close_file_n " \"%s\" failed", file.name.data);
+    }
+
+    return rc;
+}
 
-        PostQuitMessage(0);
 
-        return 0;
-
-    default:
-        return DefWindowProc(window, message, wparam, lparam);
+void
+ngx_close_handle(HANDLE h)
+{
+    if (CloseHandle(h) == 0) {
+        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
+                      "CloseHandle(%p) failed", h);
     }
 }