X-FTP
[x93.git/.git] / xftp.git / ext / pyftpdlib / prefork.py
diff --git a/xftp.git/ext/pyftpdlib/prefork.py b/xftp.git/ext/pyftpdlib/prefork.py
new file mode 100644 (file)
index 0000000..eb67365
--- /dev/null
@@ -0,0 +1,125 @@
+# Copyright (C) 2007 Giampaolo Rodola' <g.rodola@gmail.com>.\r
+# Use of this source code is governed by MIT license that can be\r
+# found in the LICENSE file.\r
+\r
+"""Process utils."""\r
+\r
+import errno\r
+import os\r
+import sys\r
+import time\r
+from binascii import hexlify\r
+try:\r
+    import multiprocessing\r
+except ImportError:\r
+    multiprocessing = None\r
+\r
+from ._compat import long\r
+from .log import logger\r
+\r
+\r
+_task_id = None\r
+\r
+\r
+def cpu_count():\r
+    """Returns the number of processors on this machine."""\r
+    if multiprocessing is None:\r
+        return 1\r
+    try:\r
+        return multiprocessing.cpu_count()\r
+    except NotImplementedError:\r
+        pass\r
+    try:\r
+        return os.sysconf("SC_NPROCESSORS_CONF")\r
+    except (AttributeError, ValueError):\r
+        pass\r
+    return 1\r
+\r
+\r
+def _reseed_random():\r
+    if 'random' not in sys.modules:\r
+        return\r
+    import random\r
+    # If os.urandom is available, this method does the same thing as\r
+    # random.seed (at least as of python 2.6).  If os.urandom is not\r
+    # available, we mix in the pid in addition to a timestamp.\r
+    try:\r
+        seed = long(hexlify(os.urandom(16)), 16)\r
+    except NotImplementedError:\r
+        seed = int(time.time() * 1000) ^ os.getpid()\r
+    random.seed(seed)\r
+\r
+\r
+def fork_processes(number, max_restarts=100):\r
+    """Starts multiple worker processes.\r
+\r
+    If *number* is None or <= 0, we detect the number of cores available\r
+    on this machine and fork that number of child processes.\r
+    If *number* is given and > 0, we fork that specific number of\r
+    sub-processes.\r
+\r
+    Since we use processes and not threads, there is no shared memory\r
+    between any server code.\r
+\r
+    In each child process, *fork_processes* returns its *task id*, a\r
+    number between 0 and *number*.  Processes that exit abnormally\r
+    (due to a signal or non-zero exit status) are restarted with the\r
+    same id (up to *max_restarts* times). In the parent process,\r
+    *fork_processes* returns None if all child processes have exited\r
+    normally, but will otherwise only exit by throwing an exception.\r
+    """\r
+    global _task_id\r
+    assert _task_id is None\r
+    if number is None or number <= 0:\r
+        number = cpu_count()\r
+    logger.info("starting %d pre-fork processes", number)\r
+    children = {}\r
+\r
+    def start_child(i):\r
+        pid = os.fork()\r
+        if pid == 0:\r
+            # child process\r
+            _reseed_random()\r
+            global _task_id\r
+            _task_id = i\r
+            return i\r
+        else:\r
+            children[pid] = i\r
+            return None\r
+\r
+    for i in range(number):\r
+        id = start_child(i)\r
+        if id is not None:\r
+            return id\r
+    num_restarts = 0\r
+    while children:\r
+        try:\r
+            pid, status = os.wait()\r
+        except OSError as e:\r
+            if e.errno == errno.EINTR:\r
+                continue\r
+            raise\r
+        if pid not in children:\r
+            continue\r
+        id = children.pop(pid)\r
+        if os.WIFSIGNALED(status):\r
+            logger.warning("child %d (pid %d) killed by signal %d, restarting",\r
+                           id, pid, os.WTERMSIG(status))\r
+        elif os.WEXITSTATUS(status) != 0:\r
+            logger.warning(\r
+                "child %d (pid %d) exited with status %d, restarting",\r
+                id, pid, os.WEXITSTATUS(status))\r
+        else:\r
+            logger.info("child %d (pid %d) exited normally", id, pid)\r
+            continue\r
+        num_restarts += 1\r
+        if num_restarts > max_restarts:\r
+            raise RuntimeError("Too many child restarts, giving up")\r
+        new_id = start_child(id)\r
+        if new_id is not None:\r
+            return new_id\r
+    # All child processes exited cleanly, so exit the master process\r
+    # instead of just returning to right after the call to\r
+    # fork_processes (which will probably just start up another IOLoop\r
+    # unless the caller checks the return value).\r
+    sys.exit(0)\r