]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Add a work in progress, a completely new, "Simple MPM".
authorPaul Querna <pquerna@apache.org>
Tue, 28 Oct 2008 07:00:15 +0000 (07:00 +0000)
committerPaul Querna <pquerna@apache.org>
Tue, 28 Oct 2008 07:00:15 +0000 (07:00 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@708462 13f79535-47bb-0310-9956-ffa450edef68

18 files changed:
server/mpm/MPM.NAMING
server/mpm/config.m4
server/mpm/simple/Makefile.in [new file with mode: 0644]
server/mpm/simple/SIMPLE.README [new file with mode: 0644]
server/mpm/simple/config.m4 [new file with mode: 0644]
server/mpm/simple/mpm.h [new file with mode: 0644]
server/mpm/simple/mpm_default.h [new file with mode: 0644]
server/mpm/simple/simple_api.c [new file with mode: 0644]
server/mpm/simple/simple_children.c [new file with mode: 0644]
server/mpm/simple/simple_children.h [new file with mode: 0644]
server/mpm/simple/simple_core.c [new file with mode: 0644]
server/mpm/simple/simple_event.c [new file with mode: 0644]
server/mpm/simple/simple_event.h [new file with mode: 0644]
server/mpm/simple/simple_io.c [new file with mode: 0644]
server/mpm/simple/simple_io.h [new file with mode: 0644]
server/mpm/simple/simple_run.c [new file with mode: 0644]
server/mpm/simple/simple_run.h [new file with mode: 0644]
server/mpm/simple/simple_types.h [new file with mode: 0644]

index 83c0694d115bfbb3b17d6be88a081b51cd6b8362..7902263ee097ee3b6e904913b5745e5e5c25b38d 100644 (file)
@@ -1,6 +1,9 @@
 
 The following MPMs currently exist:
 
+  simple ........ Single or Multi Process, with Preforking or Threading,
+                  depending on configuration and operating systems.  Should
+                  be able to run on all modern operating systems.
   prefork ....... Multi  Process Model with Preforking (Apache 1.3)
   perchild ...... Multi  Process Model with Threading.
                   Constant number of processes, variable number of threads
index f9012a415b841e0f78430167ff3935d1bfda938b..09390223620ee719d29e05d1312ff89a18f21eb5 100644 (file)
@@ -1,7 +1,7 @@
 AC_MSG_CHECKING(which MPM to use)
 AC_ARG_WITH(mpm,
 APACHE_HELP_STRING(--with-mpm=MPM,Choose the process model for Apache to use.
-                          MPM={beos|event|worker|prefork|mpmt_os2|perchild|leader|threadpool|winnt}),[
+                          MPM={simple|beos|event|worker|prefork|mpmt_os2|perchild|leader|threadpool|winnt}),[
   APACHE_MPM=$withval
 ],[
   if test "x$APACHE_MPM" = "x"; then
@@ -14,7 +14,7 @@ apache_cv_mpm=$APACHE_MPM
        
 ap_mpm_is_threaded ()
 {
-    if test "$apache_cv_mpm" = "worker" -o "$apache_cv_mpm" = "event" -o "$apache_cv_mpm" = "perchild" -o "$apache_cv_mpm" = "leader" -o "$apache_cv_mpm" = "winnt" -o "$apache_cv_mpm" = "threadpool" ; then
+    if test "$apache_cv_mpm" = "worker" -o "$apache_cv_mpm" = "event" -o "$apache_cv_mpm" = "simple" -o "$apache_cv_mpm" = "perchild" -o "$apache_cv_mpm" = "leader" -o "$apache_cv_mpm" = "winnt" -o "$apache_cv_mpm" = "threadpool" ; then
         return 0
     else
         return 1
diff --git a/server/mpm/simple/Makefile.in b/server/mpm/simple/Makefile.in
new file mode 100644 (file)
index 0000000..4c9dee0
--- /dev/null
@@ -0,0 +1,4 @@
+LTLIBRARY_NAME    = libsimple.la
+LTLIBRARY_SOURCES = simple_api.c simple_children.c simple_core.c simple_event.c simple_run.c simple_io.c
+
+include $(top_srcdir)/build/ltlib.mk
diff --git a/server/mpm/simple/SIMPLE.README b/server/mpm/simple/SIMPLE.README
new file mode 100644 (file)
index 0000000..f04090b
--- /dev/null
@@ -0,0 +1,6 @@
+The Simple MPM aims to create a single MPM, that runs on all modern
+Unix and Win32 platforms, by using APR as much as possible.
+
+The Simple MPM core run loop revovles around a Poll CB event system, with 
+timers being built in.  When an event, either an IO or Timer is ready to run, 
+it is dispatched to any available threads in the currrent process.
diff --git a/server/mpm/simple/config.m4 b/server/mpm/simple/config.m4
new file mode 100644 (file)
index 0000000..4b027a7
--- /dev/null
@@ -0,0 +1,3 @@
+if test "$MPM_NAME" = "simple" ; then
+    APACHE_FAST_OUTPUT(server/mpm/$MPM_NAME/Makefile)
+fi
diff --git a/server/mpm/simple/mpm.h b/server/mpm/simple/mpm.h
new file mode 100644 (file)
index 0000000..610b617
--- /dev/null
@@ -0,0 +1,32 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "httpd.h"
+
+#ifndef APACHE_MPM_SIMPLE_H
+#define APACHE_MPM_SIMPLE_H
+
+#define MPM_NAME "Simple"
+
+/* pqXXXXXX: shouldn't need this really. */
+#define AP_MPM_WANT_SIGNAL_SERVER
+
+/* pqXXXXXX: signal server has a hidden dependency */
+#define AP_MPM_WANT_SET_PIDFILE
+
+extern server_rec *ap_server_conf;
+
+#endif /* APACHE_MPM_SIMPLE_H */
diff --git a/server/mpm/simple/mpm_default.h b/server/mpm/simple/mpm_default.h
new file mode 100644 (file)
index 0000000..c26ee71
--- /dev/null
@@ -0,0 +1,79 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * @file  event/mpm_default.h
+ * @brief Event MPM defaults
+ *
+ * @addtogroup APACHE_MPM_EVENT
+ * @{
+ */
+
+#ifndef APACHE_MPM_DEFAULT_H
+#define APACHE_MPM_DEFAULT_H
+
+/* Number of servers to spawn off by default --- also, if fewer than
+ * this free when the caretaker checks, it will spawn more.
+ */
+#ifndef DEFAULT_START_DAEMON
+#define DEFAULT_START_DAEMON 3
+#endif
+
+/* Maximum number of *free* server processes --- more than this, and
+ * they will die off.
+ */
+
+#ifndef DEFAULT_MAX_FREE_DAEMON
+#define DEFAULT_MAX_FREE_DAEMON 10
+#endif
+
+/* Minimum --- fewer than this, and more will be created */
+
+#ifndef DEFAULT_MIN_FREE_DAEMON
+#define DEFAULT_MIN_FREE_DAEMON 3
+#endif
+
+#ifndef DEFAULT_THREADS_PER_CHILD
+#define DEFAULT_THREADS_PER_CHILD 25
+#endif
+
+/* File used for accept locking, when we use a file */
+#ifndef DEFAULT_LOCKFILE
+#define DEFAULT_LOCKFILE DEFAULT_REL_RUNTIMEDIR "/accept.lock"
+#endif
+
+/* Where the main/parent process's pid is logged */
+#ifndef DEFAULT_PIDLOG
+#define DEFAULT_PIDLOG DEFAULT_REL_RUNTIMEDIR "/httpd.pid"
+#endif
+
+/*
+ * Interval, in microseconds, between scoreboard maintenance.
+ */
+#ifndef SCOREBOARD_MAINTENANCE_INTERVAL
+#define SCOREBOARD_MAINTENANCE_INTERVAL 1000000
+#endif
+
+/* Number of requests to try to handle in a single process.  If <= 0,
+ * the children don't die off.
+ */
+#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD
+#define DEFAULT_MAX_REQUESTS_PER_CHILD 10000
+#endif
+
+#endif /* AP_MPM_DEFAULT_H */
+/** @} */
diff --git a/server/mpm/simple/simple_api.c b/server/mpm/simple/simple_api.c
new file mode 100644 (file)
index 0000000..fdde726
--- /dev/null
@@ -0,0 +1,315 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mpm.h"
+#include "ap_mpm.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "mpm_common.h"
+#include "http_log.h"
+#include "scoreboard.h"
+#include "ap_listen.h"
+#include "simple_types.h"
+#include "simple_run.h"
+#include "http_core.h"
+
+/* Thie file contains the absolute minimal MPM API, to interface with httpd. */
+
+ap_generation_t volatile ap_my_generation = 0;
+server_rec *ap_server_conf = NULL;
+
+
+
+int
+ap_mpm_run(apr_pool_t *pconf,
+           apr_pool_t *plog,
+           server_rec *s)
+{
+  simple_core_t *sc = simple_core_get();
+  
+  sc->mpm_state = AP_MPMQ_RUNNING;
+  
+  if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
+    sc->mpm_state = AP_MPMQ_STOPPING;
+    return 1;
+  }
+
+  return simple_main_loop(sc);
+}
+
+apr_status_t
+ap_mpm_query(int query_code, int *result)
+{
+  simple_core_t* sc = simple_core_get();
+
+  switch (query_code) {
+    case AP_MPMQ_IS_THREADED:
+      *result = AP_MPMQ_STATIC;
+      return APR_SUCCESS;
+      break;
+    case AP_MPMQ_IS_FORKED:
+      *result = AP_MPMQ_DYNAMIC;
+      return APR_SUCCESS;
+      break;
+    case AP_MPMQ_IS_ASYNC:
+      *result = 1;
+      return APR_SUCCESS;
+      break;
+    case AP_MPMQ_MAX_DAEMON_USED:
+      *result = sc->procmgr.proc_count;
+      return APR_SUCCESS;
+      break;
+    case AP_MPMQ_HARD_LIMIT_DAEMONS:
+      *result = sc->procmgr.proc_count;
+      return APR_SUCCESS;
+      break;
+    case AP_MPMQ_HARD_LIMIT_THREADS:
+      *result = sc->procmgr.thread_count;
+      return APR_SUCCESS;
+      break;
+    case AP_MPMQ_MAX_THREADS:
+      *result = sc->procmgr.thread_count;
+      return APR_SUCCESS;
+      break;
+    case AP_MPMQ_MAX_SPARE_DAEMONS:
+      *result = sc->procmgr.proc_count;
+      return APR_SUCCESS;
+      break;
+    case AP_MPMQ_MIN_SPARE_DAEMONS:
+      *result = sc->procmgr.proc_count;
+      return APR_SUCCESS;
+      break;
+    case AP_MPMQ_MIN_SPARE_THREADS:
+    case AP_MPMQ_MAX_SPARE_THREADS:
+      *result = sc->procmgr.thread_count;
+      return APR_SUCCESS;
+      break;
+    case AP_MPMQ_MAX_REQUESTS_DAEMON:
+      *result = sc->procmgr.max_requests_per_child;
+      return APR_SUCCESS;
+      break;
+    case AP_MPMQ_MAX_DAEMONS:
+      *result = sc->procmgr.proc_count;
+      return APR_SUCCESS;
+      break;
+    case AP_MPMQ_MPM_STATE:
+      *result = sc->mpm_state;
+      return APR_SUCCESS;
+    default:
+      break;
+  }
+
+  return APR_ENOTIMPL;
+}
+
+static int
+simple_open_logs(apr_pool_t *p,
+                 apr_pool_t *plog,
+                 apr_pool_t *ptemp,
+                 server_rec *s)
+{
+  ap_server_conf = s;
+
+  int nsock = ap_setup_listeners(s);
+
+  if (nsock < 1) {
+    ap_log_error(APLOG_MARK, APLOG_ALERT, 0,
+                 s,
+                 "simple_open_logs: no listening sockets available, shutting down");
+    return DONE;
+  }
+
+  return OK;
+}
+
+static int 
+simple_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
+                  apr_pool_t *ptemp)
+{
+  apr_status_t rv;
+  simple_core_t* sc = simple_core_get();
+
+  sc->restart_num++;
+
+  int run_debug = ap_exists_config_define("DEBUG");
+
+  if (run_debug) {
+    sc->run_foreground = 1;
+    sc->run_single_process = 1;
+  }
+  else {
+    sc->run_foreground = ap_exists_config_define("FOREGROUND");
+  }
+
+  if (sc->restart_num == 2) {
+    
+    if (sc->run_foreground) {
+      rv = apr_proc_detach(APR_PROC_DETACH_FOREGROUND);
+    }
+    else {
+      rv = apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
+    }
+
+    if (rv) {
+      ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+                   "simple_pre_config: apr_proc_detach(%s) failed",
+                   sc->run_foreground ? "FOREGROUND" : "DAEMONIZE");
+      return HTTP_INTERNAL_SERVER_ERROR;
+    }
+  }
+
+  return OK;
+}
+
+static void
+simple_process_start(process_rec *process)
+{
+  apr_status_t rv;
+
+  /* this is our first 'real' entry point, so setup everything here. */
+  rv = simple_core_init(simple_core_get(), process->pool);
+  
+  if (rv) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+                 "simple_core_init: Fatal Error Encountered");
+    exit(EXIT_FAILURE);
+  }
+
+  ap_mpm_rewrite_args(process);
+}
+
+#define SIMPLE_MAX_PROC (500000)
+#define SIMPLE_MIN_PROC (1)
+
+#define SIMPLE_MAX_THREADS (500000)
+#define SIMPLE_MIN_THREADS (1)
+
+static int
+simple_check_config(apr_pool_t *p, apr_pool_t *plog,
+                    apr_pool_t *ptemp, server_rec *s)
+{
+  simple_core_t* sc = simple_core_get();
+
+  if (sc->procmgr.proc_count > SIMPLE_MAX_PROC) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
+                 "simple_check_config: SimpleProcCount must be less than %d", SIMPLE_MAX_PROC);
+    return !OK;
+  }
+
+  if (sc->procmgr.proc_count < SIMPLE_MIN_PROC) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
+                 "simple_check_config: SimpleProcCount must be more than %d", SIMPLE_MIN_PROC);
+    return !OK;
+  }
+
+  if (sc->procmgr.thread_count > SIMPLE_MAX_THREADS) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
+                 "simple_check_config: SimpleThreadCount must be less than %d", SIMPLE_MAX_THREADS);
+    return !OK;
+  }
+
+  if (sc->procmgr.thread_count < SIMPLE_MIN_THREADS) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
+                 "simple_check_config: SimpleThreadCount must be more than %d", SIMPLE_MIN_THREADS);
+    return !OK;
+  }
+
+  return OK;
+}
+  
+static void
+simple_hooks(apr_pool_t *p)
+{
+  static const char *const aszSucc[] = {"core.c", NULL};
+
+  ap_hook_open_logs(simple_open_logs, NULL,
+                    aszSucc, APR_HOOK_REALLY_FIRST);
+
+  ap_hook_pre_config(simple_pre_config,
+                     NULL, NULL, APR_HOOK_REALLY_FIRST);
+
+  ap_hook_check_config(simple_check_config,
+                       NULL, NULL, APR_HOOK_MIDDLE);
+
+}
+
+static const char*
+set_proccount(cmd_parms *cmd, void *baton, const char *arg)
+{
+  const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+  if (err != NULL) {
+    return err;
+  }
+  
+  simple_core_get()->procmgr.proc_count = atoi(arg);
+  return NULL;
+}
+
+
+static const char*
+set_threadcount(cmd_parms *cmd, void *baton, const char *arg)
+{
+  const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+  if (err != NULL) {
+    return err;
+  }
+
+  simple_core_get()->procmgr.thread_count = atoi(arg);
+
+  return NULL;
+}
+
+static const char*
+set_user(cmd_parms *cmd, void *baton, const char *arg)
+{
+  const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+  if (err != NULL) {
+    return err;
+  }
+  
+  simple_core_get()->procmgr.user_name = arg;
+  simple_core_get()->procmgr.user_id = ap_uname2id(arg);
+
+  return NULL;
+}
+
+static const command_rec simple_cmds[] = {
+  AP_INIT_TAKE1("SimpleProcCount", set_proccount, NULL, RSRC_CONF,
+                "Number of child processes launched at server startup"),
+  AP_INIT_TAKE1("SimpleThreadCount", set_threadcount, NULL, RSRC_CONF,
+                "Set the number of Worker Threads Per-Process"),
+  AP_INIT_TAKE1("SimpleUser", set_user, NULL, RSRC_CONF,
+                "Sets the user to run child processes as"),
+  /* pqXXXXXXXXX: These do NOT belong in the MPM configuration commands. */
+  LISTEN_COMMANDS,
+  { NULL }
+};
+
+
+
+module AP_MODULE_DECLARE_DATA mpm_simple_module = {
+  MPM20_MODULE_STUFF,
+  simple_process_start,       /* hook to run before apache parses args */
+  NULL,                       /* create per-directory config structure */
+  NULL,                       /* merge per-directory config structures */
+  NULL,                       /* create per-server config structure */
+  NULL,                       /* merge per-server config structures */
+  simple_cmds,                /* command apr_table_t */
+  simple_hooks                /* register_hooks */
+};
+
+  
+
diff --git a/server/mpm/simple/simple_children.c b/server/mpm/simple/simple_children.c
new file mode 100644 (file)
index 0000000..8bd385e
--- /dev/null
@@ -0,0 +1,104 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "simple_types.h"
+#include "simple_event.h"
+#include "simple_run.h"
+#include "apr_hash.h"
+
+#define SPAWN_CHILDREN_INTERVAL (apr_time_from_sec(5))
+
+static void
+simple_kill_random_child(simple_core_t *sc)
+{
+  apr_thread_mutex_lock(sc->mtx);
+  /* See comment in simple_spawn_child for why we check here. */
+  if (!sc->run_single_process) {
+  }
+  apr_thread_mutex_unlock(sc->mtx);
+}
+
+static void
+simple_spawn_child(simple_core_t *sc)
+{
+  apr_thread_mutex_lock(sc->mtx);
+  /* Although we could cut this off 'earlier', and not even invoke this 
+   * function, I would like to keep the functions invoked when in debug mode
+   * to be as close as possible to those when not in debug... So, we just skip
+   * the actual spawn itself, but go through all of the motions...
+   */
+  if (!sc->run_single_process) {
+    
+  }
+  apr_thread_mutex_unlock(sc->mtx);
+}
+
+void
+simple_check_children_size(simple_core_t *sc,
+                           void *baton)
+{
+  simple_register_timer(sc,
+                        simple_check_children_size,
+                        NULL,
+                        SPAWN_CHILDREN_INTERVAL);
+  unsigned int count;
+  int wanted;
+  int i;
+
+  if (sc->run_single_process && sc->restart_num == 2) {
+    static int run = 0;
+    /* This is kinda of hack, but rather than spawning a child process, 
+     * we register the normal IO handlers in the main event loop....
+     */
+    if (run == 0) {
+      simple_single_process_hack(sc);
+      run++;
+    }
+  }
+
+  {
+    apr_thread_mutex_lock(sc->mtx);
+    count = apr_hash_count(sc->children);
+    wanted = sc->procmgr.proc_count;
+    apr_thread_mutex_unlock(sc->mtx);
+  }
+
+  if (count > wanted) {
+    /* kill some kids */
+    int to_kill = count - wanted;
+    for (i = 0;
+         i < to_kill;
+         i++)
+    {
+      simple_kill_random_child(sc);
+    }
+  }
+  else if (count < wanted) {
+    /* spawn some kids */
+    int to_spawn = wanted - count;
+    for (i = 0;
+         i < to_spawn;
+         i++)
+    {
+      simple_spawn_child(sc);
+    }
+  }
+  else {
+    /* juuuuust right. */
+  }
+}
diff --git a/server/mpm/simple/simple_children.h b/server/mpm/simple/simple_children.h
new file mode 100644 (file)
index 0000000..5fa94a1
--- /dev/null
@@ -0,0 +1,32 @@
+
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "simple_types.h"
+
+#ifndef APACHE_MPM_SIMPLE_CHILDREN_H
+#define APACHE_MPM_SIMPLE_CHILDREN_H
+
+void
+simple_check_children_size(simple_core_t *sc,
+                           void *baton);
+
+void
+simple_check_children_status(simple_core_t *sc,
+                             void *baton);
+
+
+#endif /* APACHE_MPM_SIMPLE_CHILDREN_H */
diff --git a/server/mpm/simple/simple_core.c b/server/mpm/simple/simple_core.c
new file mode 100644 (file)
index 0000000..6f6e7cd
--- /dev/null
@@ -0,0 +1,82 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Simple Core utility methods.
+ */
+#include "simple_types.h"
+#include "mpm.h"
+#include "ap_mpm.h"
+#include "httpd.h"
+#include "http_log.h"
+
+static simple_core_t g_simple_core;
+
+#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD
+#define DEFAULT_MAX_REQUESTS_PER_CHILD 0
+#endif
+
+
+simple_core_t*
+simple_core_get()
+{
+  return &g_simple_core;
+}
+
+apr_status_t
+simple_core_init(simple_core_t* sc, apr_pool_t* pool)
+{
+  apr_status_t rv;
+
+  memset(sc, 0, sizeof(simple_core_t));
+  
+  apr_pool_create(&sc->pool, pool);
+
+  apr_pool_tag(sc->pool, "simple-mpm-core");
+
+  sc->mpm_state = AP_MPMQ_STARTING;
+  sc->procmgr.max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
+  
+  sc->children = apr_hash_make(sc->pool);
+
+  APR_RING_INIT(&sc->timer_ring, simple_timer_t, link);
+  APR_RING_INIT(&sc->dead_timer_ring, simple_timer_t, link);
+
+  rv = apr_thread_mutex_create(&sc->mtx,
+                               0,
+                               sc->pool);
+
+  if (rv) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+                "simple_core_init: apr_thread_mutex_create failed.");
+    return rv;
+  }
+
+  rv = apr_pollcb_create(&sc->pollcb,
+                         512 /* pqXXXXX: make configrable */,
+                         sc->pool,
+                         0);
+  
+  if (rv) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+                 "simple_core_init: apr_pollcb_create failed.");
+    return rv;
+  }
+
+  return APR_SUCCESS;
+}
+
+
diff --git a/server/mpm/simple/simple_event.c b/server/mpm/simple/simple_event.c
new file mode 100644 (file)
index 0000000..1b8f378
--- /dev/null
@@ -0,0 +1,81 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define APR_RING_DEBUG 1
+
+#include "simple_types.h"
+#include "simple_event.h"
+
+void 
+simple_register_timer(simple_core_t *sc,
+                      simple_timer_cb cb, 
+                      void *baton,
+                      apr_time_t relative_time)
+{
+  simple_timer_t *elem = NULL;
+  simple_timer_t *ep = NULL;
+  int inserted  = 0;
+  apr_time_t t = apr_time_now() + relative_time;
+
+  apr_thread_mutex_lock(sc->mtx);
+
+  APR_RING_CHECK_CONSISTENCY(&sc->timer_ring, simple_timer_t, link);
+  APR_RING_CHECK_CONSISTENCY(&sc->dead_timer_ring, simple_timer_t, link);
+  
+  if (!APR_RING_EMPTY(&sc->dead_timer_ring, simple_timer_t, link)) {
+    elem = APR_RING_FIRST(&sc->dead_timer_ring);
+    APR_RING_REMOVE(elem, link);
+    APR_RING_CHECK_CONSISTENCY(&sc->dead_timer_ring, simple_timer_t, link);
+  }
+  else {
+    elem = (simple_timer_t *) apr_pcalloc(sc->pool, sizeof(simple_timer_t));
+  }
+
+  APR_RING_ELEM_INIT(elem, link);
+  elem->expires = t;
+  elem->cb = cb;
+  elem->baton = baton;
+
+  APR_RING_CHECK_CONSISTENCY(&sc->timer_ring, simple_timer_t, link);
+
+  /* pqXXXXXX: skiplist would be a nice optimization here. */
+  if (!APR_RING_EMPTY(&sc->timer_ring, simple_timer_t, link)) {
+    ep = APR_RING_FIRST(&sc->timer_ring);
+    while (inserted == 0 && 
+           ep != APR_RING_SENTINEL(&sc->timer_ring, simple_timer_t, link))
+    {
+      if (ep->expires < elem->expires) {
+        APR_RING_CHECK_CONSISTENCY(&sc->timer_ring, simple_timer_t, link);
+        APR_RING_INSERT_BEFORE(ep, elem, link);
+        inserted = 1;
+        APR_RING_CHECK_CONSISTENCY(&sc->timer_ring, simple_timer_t, link);
+      }
+      ep = APR_RING_NEXT(ep, link);
+    }
+  }
+
+  APR_RING_CHECK_CONSISTENCY(&sc->timer_ring, simple_timer_t, link);
+
+  if (!inserted) {
+    APR_RING_INSERT_TAIL(&sc->timer_ring,
+                         elem, simple_timer_t, link);
+  }
+
+  APR_RING_CHECK_CONSISTENCY(&sc->timer_ring, simple_timer_t, link);
+
+  apr_thread_mutex_unlock(sc->mtx);
+}
+
diff --git a/server/mpm/simple/simple_event.h b/server/mpm/simple/simple_event.h
new file mode 100644 (file)
index 0000000..c439d5b
--- /dev/null
@@ -0,0 +1,57 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr.h"
+#include "apr_pools.h"
+#include "apr_poll.h"
+
+#ifndef APACHE_MPM_SIMPLE_EVENT_H
+#define APACHE_MPM_SIMPLE_EVENT_H
+
+/* pqXXXXXX: Pool based cleanups
+ */
+
+void 
+simple_register_timer(simple_core_t *sc,
+                      simple_timer_cb cb, 
+                      void *baton,
+                      apr_time_t relative_time);
+
+/**
+ * @see apr_poll.h for watch_for values
+ */
+void
+simple_register_sock_io(simple_core_t *sc,
+                        simple_io_sock_cb cb, 
+                        void *baton,
+                        apr_socket_t *sock,
+                        int watch_for,
+                        apr_time_t relative_timeout);
+
+/**
+ * @see apr_poll.h for watch_for values
+ */
+void
+simple_register_file_io(simple_core_t *sc,
+                        simple_io_file_cb cb, 
+                        void *baton,
+                        apr_file_t *file,
+                        int watch_for,
+                        apr_time_t relative_timeout);
+
+
+#endif /* APACHE_MPM_SIMPLE_EVENT_H */
+
diff --git a/server/mpm/simple/simple_io.c b/server/mpm/simple/simple_io.c
new file mode 100644 (file)
index 0000000..561185b
--- /dev/null
@@ -0,0 +1,279 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "httpd.h"
+#include "http_log.h"
+#include "ap_listen.h"
+#include "simple_types.h"
+#include "simple_io.h"
+#include "simple_event.h"
+
+#include "http_connection.h"
+#include "util_filter.h"
+#include "http_main.h"
+#include "mpm.h"
+#include "scoreboard.h"
+#include "http_vhost.h"
+
+void
+simple_io_timeot_cb(simple_core_t *sc,
+                           void *baton)
+{
+  simple_conn_t *scon = (simple_conn_t *)baton;
+  /* pqXXXXX: handle timeouts. */
+  conn_rec *c = scon->c;
+  conn_state_t *cs = c->cs;
+
+  cs = NULL;
+
+  ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf,
+               "io timeout hit (?)");
+}
+
+static apr_status_t
+simple_io_process(simple_conn_t *scon)
+{
+  apr_status_t rv;
+
+  if (scon->c->clogging_input_filters && !scon->c->aborted) {
+    /* Since we have an input filter which 'cloggs' the input stream,
+     * like mod_ssl, lets just do the normal read from input filters,
+     * like the Worker MPM does.
+     */
+    ap_run_process_connection(scon->c);
+    if (scon->c->cs->state != CONN_STATE_SUSPENDED) {
+      scon->c->cs->state = CONN_STATE_LINGER;
+    }
+  }
+
+  simple_core_t *sc = scon->sc;
+  conn_rec *c = scon->c;
+  conn_state_t *cs = c->cs;
+
+  while (!c->aborted) {
+    if (cs->state == CONN_STATE_READ_REQUEST_LINE) {
+      if (!c->aborted) {
+        ap_run_process_connection(c);
+        /* state will be updated upon return
+         * fall thru to either wait for readability/timeout or
+         * do lingering close
+         */
+      }
+      else {
+        cs->state = CONN_STATE_LINGER;
+      }
+    }
+
+    if (cs->state == CONN_STATE_WRITE_COMPLETION) {
+      ap_filter_t *output_filter = c->output_filters;
+      while (output_filter->next != NULL) {
+        output_filter = output_filter->next;
+      }
+
+      rv = output_filter->frec->filter_func.out_func(output_filter, NULL);
+      
+      if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
+                     "network write failure in core output filter");
+        cs->state = CONN_STATE_LINGER;
+      }
+      else if (c->data_in_output_filters) {
+        /* Still in WRITE_COMPLETION_STATE:
+         * Set a write timeout for this connection, and let the
+         * event thread poll for writeability.
+         */
+
+        simple_register_timer(scon->sc,
+                              simple_io_timeot_cb,
+                              scon,
+                              scon->c->base_server != NULL ? scon->c->base_server->timeout : ap_server_conf->timeout);
+
+        cs->pfd.reqevents = APR_POLLOUT | APR_POLLHUP | APR_POLLERR;
+
+        rv = apr_pollcb_add(sc->pollcb, &cs->pfd);
+
+        if (rv != APR_SUCCESS) {
+          ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
+                       "apr_pollcb_add: failed in write completion");
+          AP_DEBUG_ASSERT(rc == APR_SUCCESS);
+        }
+        return APR_SUCCESS;
+      }
+      else if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted) {
+        c->cs->state = CONN_STATE_LINGER;
+      }
+      else if (c->data_in_input_filters) {
+        cs->state = CONN_STATE_READ_REQUEST_LINE;
+      }
+      else {
+        cs->state = CONN_STATE_CHECK_REQUEST_LINE_READABLE;
+      }
+    }
+
+    if (cs->state == CONN_STATE_LINGER) {
+      ap_lingering_close(c);
+      apr_pool_destroy(scon->pool);
+      return APR_SUCCESS;
+    }
+
+    if (cs->state == CONN_STATE_CHECK_REQUEST_LINE_READABLE) {
+      simple_register_timer(scon->sc,
+                            simple_io_timeot_cb,
+                            scon,
+                            scon->c->base_server != NULL ? scon->c->base_server->timeout : ap_server_conf->timeout);
+
+      cs->pfd.reqevents = APR_POLLIN;
+
+      rv = apr_pollcb_add(sc->pollcb, &cs->pfd);
+
+      if (rv) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
+                     "process_socket: apr_pollset_add failure in read request line");
+        AP_DEBUG_ASSERT(rc == APR_SUCCESS);
+      }
+
+      return APR_SUCCESS;
+    }
+  }
+
+  ap_lingering_close(c);
+  apr_pool_destroy(scon->pool);
+  return APR_SUCCESS;
+}
+
+static void *
+simple_io_invoke(apr_thread_t* thread, void *baton)
+{
+  simple_sb_t *sb = (simple_sb_t *)baton;
+  simple_conn_t *scon = (simple_conn_t *)sb->baton;
+  apr_status_t rv;
+  
+  rv = simple_io_process(scon);
+  
+  if (rv) {
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf,
+                 "simple_io_invoke: simple_io_process failed (?)");
+  }
+
+  return NULL;
+}
+
+static void *
+simple_io_setup_conn(apr_thread_t* thread, void *baton)
+{
+  apr_status_t rv;
+  simple_conn_t *scon = (simple_conn_t *)baton;
+
+  /* pqXXXXX: remove this. */
+  ap_sb_handle_t *sbh;
+  ap_create_sb_handle(&sbh, scon->pool, 0, 0);
+  long conn_id = 0;
+
+  scon->ba = apr_bucket_alloc_create(scon->pool);
+
+  scon->c = ap_run_create_connection(scon->pool, ap_server_conf, scon->sock,
+                               conn_id, sbh, scon->ba);
+
+  scon->c->cs = apr_pcalloc(scon->pool, sizeof(conn_state_t));
+  conn_state_t *cs = scon->c->cs;
+  simple_sb_t *sb = apr_pcalloc(scon->pool, sizeof(simple_sb_t));
+
+  cs->pfd.p = scon->pool;
+  cs->pfd.desc_type = APR_POLL_SOCKET;
+  cs->pfd.desc.s = scon->sock;
+  cs->pfd.reqevents = APR_POLLIN;
+
+  sb->type = SIMPLE_PT_CORE_IO;
+  sb->baton = scon;
+  cs->pfd.client_data = sb;
+
+  ap_update_vhost_given_ip(scon->c);
+
+  rv = ap_run_pre_connection(scon->c, scon->sock);
+  if (rv != OK && rv != DONE) {
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
+                 "simple_io_setup_conn: connection aborted");
+    scon->c->aborted = 1;
+  }
+
+  scon->c->cs->state = CONN_STATE_READ_REQUEST_LINE;
+
+  rv = simple_io_process(scon);
+
+  if (rv) {
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf,
+                 "simple_io_setup_conn: simple_io_process failed (?)");
+  }
+
+  return NULL;
+}
+
+apr_status_t
+simple_io_accept(simple_core_t *sc, simple_sb_t *sb)
+{
+  apr_status_t rv;
+  ap_listen_rec *lr = (ap_listen_rec *)sb->baton;
+  apr_pool_t *ptrans;
+
+  /* pqXXXXXX: Consider doing pool recycling like the event/worker MPMs do. */
+  apr_pool_create(&ptrans, NULL);
+  apr_socket_t *socket;
+
+  apr_pool_tag(ptrans, "transaction");
+
+  rv = apr_socket_accept(&socket, lr->sd, ptrans);
+  if (rv) {
+    /* pqXXXXXX: unixd.c has _tons_ of custom handling on return values
+     * from accept, but it seems really crazy, it either worked, or didn't, 
+     * but taking this approach of swallowing the error it is possible we have a 
+     * fatal error on our listening socket, but we don't notice.  
+     * 
+     * Need to discuss this on dev@
+     */
+    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+                 "simple_io_accept: apr_socket_accept failed");
+    return APR_SUCCESS;
+  }
+  else {
+    simple_conn_t *scon = apr_pcalloc(ptrans, sizeof(simple_conn_t));
+    scon->pool = ptrans;
+    scon->sock = socket;
+    scon->sc = sc;
+
+    return apr_thread_pool_push(sc->workers,
+                                simple_io_setup_conn,
+                                scon,
+                                APR_THREAD_TASK_PRIORITY_NORMAL,
+                                NULL);
+  }
+
+  return APR_SUCCESS;
+}
+
+apr_status_t
+simple_io_event_process(simple_core_t *sc, simple_sb_t *sb)
+{
+  /* pqXXXXX: In theory, if we have non-blocking operations on the connection
+   *  we can do them here, before pushing to another thread, thats just
+   * not implemented right now.
+   */
+  return apr_thread_pool_push(sc->workers,
+                              simple_io_invoke,
+                              sb,
+                              APR_THREAD_TASK_PRIORITY_NORMAL,
+                              NULL);
+}
+
diff --git a/server/mpm/simple/simple_io.h b/server/mpm/simple/simple_io.h
new file mode 100644 (file)
index 0000000..fce65db
--- /dev/null
@@ -0,0 +1,28 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "simple_types.h"
+
+#ifndef APACHE_MPM_SIMPLE_IO_H
+#define APACHE_MPM_SIMPLE_IO_H
+
+apr_status_t
+simple_io_accept(simple_core_t *sc, simple_sb_t *sb);
+
+apr_status_t
+simple_io_event_process(simple_core_t *sc, simple_sb_t *sb);
+
+#endif
diff --git a/server/mpm/simple/simple_run.c b/server/mpm/simple/simple_run.c
new file mode 100644 (file)
index 0000000..a11081b
--- /dev/null
@@ -0,0 +1,314 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define APR_RING_DEBUG 1
+
+#include "httpd.h"
+#include "http_log.h"
+#include "simple_types.h"
+#include "simple_event.h"
+#include "simple_children.h"
+#include "simple_run.h"
+#include "simple_io.h"
+#include "ap_mpm.h"
+#include "scoreboard.h"
+
+#include "ap_listen.h"
+
+/**
+ * Create Timers.
+ */
+static apr_status_t
+simple_main_setup_timers(simple_core_t *sc)
+{
+  simple_register_timer(sc,
+                        simple_check_children_size,
+                        NULL,
+                        0);
+
+  return APR_SUCCESS;
+}
+
+/**
+ * Create worker thread pool.
+ */
+static apr_status_t
+simple_setup_workers(simple_core_t *sc)
+{
+  apr_status_t rv;
+
+  ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
+               "simple_setup_workers: spawning %d threads", sc->procmgr.thread_count);
+
+  rv = apr_thread_pool_create(&sc->workers,
+                              sc->procmgr.thread_count,
+                              sc->procmgr.thread_count,
+                              sc->pool);
+
+  if (rv) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+                 "simple_setup_workers: apr_thread_pool_create with %d threads failed", sc->procmgr.thread_count);
+    return rv;
+  }
+
+  return APR_SUCCESS;
+}
+
+static apr_status_t
+simple_setup_listeners(simple_core_t *sc)
+{
+  ap_listen_rec *lr;
+  apr_status_t rv;
+
+  for (lr = ap_listeners; lr != NULL; lr = lr->next) {
+    apr_pollfd_t *pfd = apr_palloc(sc->pool, sizeof(apr_pollfd_t));
+    simple_sb_t *sb = apr_pcalloc(sc->pool, sizeof(simple_sb_t));
+
+    pfd->p = sc->pool;
+    pfd->desc_type = APR_POLL_SOCKET;
+    pfd->desc.s = lr->sd;
+    pfd->reqevents = APR_POLLIN;
+    
+    sb->type = SIMPLE_PT_CORE_ACCEPT;
+    sb->baton = lr;
+
+    pfd->client_data = sb;
+
+    rv = apr_socket_opt_set(pfd->desc.s, APR_SO_NONBLOCK, 1);
+    if (rv) {
+      ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+                   "simple_setup_workers: apr_socket_opt_set(APR_SO_NONBLOCK = 1) failed on %pI", lr->bind_addr);
+      return rv;
+    }
+    
+    rv = apr_pollcb_add(sc->pollcb, pfd);
+    if (rv) {
+      ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+                   "simple_setup_workers: apr_pollcb_add failed on %pI", lr->bind_addr);
+      return rv;
+    }
+  }
+
+  return APR_SUCCESS;
+}
+
+
+
+static apr_status_t
+simple_io_callback(void *baton, apr_pollfd_t *pfd)
+{
+  apr_status_t rv = APR_SUCCESS;
+  simple_core_t *sc = (simple_core_t *)baton;
+  simple_sb_t *sb = pfd->client_data;
+
+  
+  if (sb->type == SIMPLE_PT_CORE_ACCEPT) {
+    rv = simple_io_accept(sc, sb);
+  }
+  else if (sb->type == SIMPLE_PT_CORE_IO) {
+    rv = simple_io_event_process(sc, sb);
+  }
+  else if (sb->type == SIMPLE_PT_USER) {
+    /* TODO: */
+    abort();
+  }
+  else {
+    abort();
+  }
+
+  return rv;
+}
+
+static void *
+simple_timer_invoke(apr_thread_t* thread, void *baton)
+{
+  simple_timer_t *ep = (simple_timer_t *)baton;
+  simple_core_t *sc = simple_core_get();
+
+  ep->cb(sc, ep->baton);
+  apr_thread_mutex_lock(sc->mtx);
+  APR_RING_INSERT_TAIL(&sc->dead_timer_ring, ep, simple_timer_t, link);
+  apr_thread_mutex_unlock(sc->mtx);
+
+  return NULL;
+}
+
+#ifndef apr_time_from_msec
+#define apr_time_from_msec(msec) ((apr_time_t)(msec) * 1000)
+#endif
+
+static int
+simple_run_loop(simple_core_t *sc)
+{
+  apr_status_t rv;
+  simple_timer_t *ep = NULL;
+  
+  while (sc->mpm_state == AP_MPMQ_RUNNING) {
+    apr_time_t now = apr_time_now();
+    apr_interval_time_t timeout = apr_time_from_msec(500);
+    
+    apr_thread_mutex_lock(sc->mtx);
+    simple_timer_t *head = APR_RING_FIRST(&sc->timer_ring);
+    
+    if (head != APR_RING_SENTINEL(&sc->timer_ring, simple_timer_t, link)) {
+      if (now < head->expires) {
+        timeout = (head->expires - now);
+        if (timeout > apr_time_from_msec(500)) {
+          /* pqXXXXX: I'm 95% sure that the Linux Powertop guys will slap me for this. */
+          timeout = apr_time_from_msec(500);
+        }
+      }
+    }
+    apr_thread_mutex_unlock(sc->mtx);
+
+    rv = apr_pollcb_poll(sc->pollcb,
+                         timeout,
+                         simple_io_callback,
+                         sc);
+
+    now = apr_time_now();
+
+    if (rv) {
+      if (!APR_STATUS_IS_EINTR(rv) && !APR_STATUS_IS_TIMEUP(rv)) {
+        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+                     "simple_main_loop: apr_pollcb_poll failed");
+        return !OK;
+      }
+    }
+    
+    APR_RING_HEAD(simple_temp_timer_ring_t, simple_timer_t) tmp_ring;
+    APR_RING_INIT(&tmp_ring, simple_timer_t, link);
+
+    apr_thread_mutex_lock(sc->mtx);
+
+    APR_RING_CHECK_CONSISTENCY(&sc->timer_ring, simple_timer_t, link);
+    
+    /* now iterate any timers */
+    if (!APR_RING_EMPTY(&sc->timer_ring, simple_timer_t, link)) {
+      for (ep = APR_RING_FIRST(&sc->timer_ring);
+           ep != APR_RING_SENTINEL(&sc->timer_ring,
+                                   simple_timer_t, link);
+           ep = APR_RING_NEXT(ep, link))
+      {
+        if (ep->expires < now) {
+          simple_timer_t *next = APR_RING_PREV(ep, link);
+          /* push this task */
+          APR_RING_REMOVE(ep, link);
+          APR_RING_CHECK_CONSISTENCY(&sc->timer_ring, simple_timer_t, link);
+          APR_RING_INSERT_TAIL(&tmp_ring, ep, simple_timer_t, link);
+          ep = next;
+        }
+        else {
+          break;
+        }
+      }
+    }
+
+    APR_RING_CHECK_CONSISTENCY(&sc->timer_ring, simple_timer_t, link);
+    
+    apr_thread_mutex_unlock(sc->mtx);
+
+    if (!APR_RING_EMPTY(&tmp_ring, simple_timer_t, link)) {
+      for (ep = APR_RING_FIRST(&tmp_ring);
+           ep != APR_RING_SENTINEL(&tmp_ring,
+                                   simple_timer_t, link);
+           ep = APR_RING_NEXT(ep, link))
+      {
+        apr_thread_pool_push(sc->workers,
+                             simple_timer_invoke,
+                             ep,
+                             APR_THREAD_TASK_PRIORITY_NORMAL,
+                             NULL);
+      }
+    }
+  }
+  
+  return 0;
+}
+
+void
+simple_single_process_hack(simple_core_t *sc)
+{
+  apr_status_t rv;
+  /* Normally this is only ran in the child processes, but we want to do it here too...*/
+  rv = simple_setup_listeners(sc);
+  if (rv) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+                 "simple_single_child_hack: simple_setup_listeners failed");
+  }
+}
+
+static int
+simple_setup_privs(simple_core_t *sc)
+{
+  /* TODO: These should be a series of hooks, chroot, drop user, SELinux support, etc. */
+  /* TODO: chroot support */
+  /* TODO: drop to configured user */
+  return 0;
+}
+
+int
+simple_child_loop(simple_core_t *sc)
+{
+  apr_status_t rv;
+
+  rv = simple_setup_workers(sc);
+  if (rv) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+                 "simple_child_loop: simple_setup_workers failed");
+    return !OK;
+  }
+
+  rv = simple_setup_listeners(sc);
+  if (rv) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+                 "simple_child_loop: simple_setup_sockets failed");
+    return !OK;
+  }
+
+  rv = simple_setup_privs(sc);
+  if (rv) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+                 "simple_child_loop: simple_drop_privs failed");
+    return !OK;
+  }
+
+  return simple_run_loop(sc);
+}
+
+int
+simple_main_loop(simple_core_t *sc)
+{
+  apr_status_t rv;
+
+  rv = simple_setup_workers(sc);
+  if (rv) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+                 "simple_main_loop: simple_setup_workers failed");
+    return !OK;
+  }
+
+  rv = simple_main_setup_timers(sc);
+  if (rv) {
+    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+                 "simple_main_loop: simple_setup_timers failed");
+    return !OK;
+  }
+
+  return simple_run_loop(sc);
+}
+
diff --git a/server/mpm/simple/simple_run.h b/server/mpm/simple/simple_run.h
new file mode 100644 (file)
index 0000000..2533228
--- /dev/null
@@ -0,0 +1,33 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "simple_types.h"
+
+#ifndef APACHE_MPM_SIMPLE_RUN_H
+#define APACHE_MPM_SIMPLE_RUN_H
+
+void 
+simple_single_process_hack(simple_core_t *sc);
+
+int 
+simple_child_loop(simple_core_t* sc);
+
+int 
+simple_main_loop(simple_core_t* sc);
+
+
+#endif
+
diff --git a/server/mpm/simple/simple_types.h b/server/mpm/simple/simple_types.h
new file mode 100644 (file)
index 0000000..d4454c7
--- /dev/null
@@ -0,0 +1,117 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr.h"
+#include "apr_pools.h"
+#include "apr_poll.h"
+#include "apr_hash.h"
+#include "apr_ring.h"
+#include "apr_thread_pool.h"
+#include "apr_buckets.h"
+#include "httpd.h"
+
+#ifndef APACHE_MPM_SIMPLE_TYPES_H
+#define APACHE_MPM_SIMPLE_TYPES_H
+
+typedef struct simple_core_t simple_core_t;
+
+typedef struct {
+  int proc_count;
+  int thread_count;
+  int max_requests_per_child;
+  int user_id;
+  const char *user_name;
+} simple_proc_mgr_t;
+
+typedef struct {
+  int pid;
+} simple_child_t;
+
+typedef void(*simple_timer_cb)(simple_core_t *sc, void *baton);
+typedef void(*simple_io_sock_cb)(simple_core_t *sc, apr_socket_t *sock,
+                                 int flags, void *baton);
+typedef void(*simple_io_file_cb)(simple_core_t *sc, apr_socket_t *sock,
+                                 int flags, void *baton);
+
+typedef struct simple_sb_t simple_sb_t;
+
+typedef enum
+{
+  SIMPLE_PT_CORE_ACCEPT,
+  SIMPLE_PT_CORE_IO,
+  /* pqXXXXXX: User IO not defined yet. */
+  SIMPLE_PT_USER,
+} simple_poll_type_e;
+
+struct simple_sb_t {
+  simple_poll_type_e type;
+  void *baton;
+};
+
+typedef struct simple_timer_t simple_timer_t;
+struct simple_timer_t {
+  APR_RING_ENTRY(simple_timer_t) link;
+  apr_time_t expires;
+  simple_timer_cb cb;
+  void *baton;
+};
+
+struct simple_core_t {
+  apr_pool_t *pool;
+  apr_thread_mutex_t *mtx;
+
+  int mpm_state;
+  int restart_num;
+
+  int run_single_process;
+  int run_foreground;
+
+  simple_proc_mgr_t procmgr;
+
+  /* PID -> simple_child_t map */
+  apr_hash_t *children;
+
+  apr_pollcb_t *pollcb;
+
+  /* List of upcoming timers, sorted by nearest first.
+   */
+  APR_RING_HEAD(simple_timer_ring_t, simple_timer_t) timer_ring;
+  
+  /* used to recycle simple_timer_t structs, since we allocate them out of 
+   * the global pool.
+   */
+  APR_RING_HEAD(simple_dead_timer_ring_t, simple_timer_t) dead_timer_ring;
+  
+  apr_thread_pool_t *workers;
+};
+
+typedef struct simple_conn_t simple_conn_t;
+struct simple_conn_t {
+  apr_pool_t *pool;
+  simple_core_t *sc;
+  apr_socket_t *sock;
+  apr_bucket_alloc_t *ba;
+  conn_rec *c;
+};
+
+simple_core_t* simple_core_get();
+
+/* Resets all variables to 0 for a simple_core_t */
+apr_status_t simple_core_init(simple_core_t* sc,
+                              apr_pool_t* pool);
+
+#endif /* APACHE_MPM_SIMPLE_TYPES_H */
+