From 0f386ed3e14d2b4349c76aeb8620c35e1d061233 Mon Sep 17 00:00:00 2001 From: Dylan Stephano-Shachter Date: Mon, 21 Oct 2019 14:24:53 -0400 Subject: [PATCH] Now uses posix_spawn to avoid deadlock --- README.md | 2 +- benchmark/bench | 10 +++++++--- benchmark/old_fptest.py | 8 ++++++++ fastprocess/__init__.py | 2 +- fastprocess/fastprocess.py | 24 ++++++++++++++++++++++++ setup.py | 3 ++- 6 files changed, 43 insertions(+), 6 deletions(-) create mode 100755 benchmark/old_fptest.py diff --git a/README.md b/README.md index 2c8f2e1..7a0661a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -WARNING: This library is unsafe to use until python 3.8 is released due to deadlock issues with fork(). +WARNING: This library requires python 3.8 # Fastprocess A fast subprocess library diff --git a/benchmark/bench b/benchmark/bench index 13277b4..3f1a594 100755 --- a/benchmark/bench +++ b/benchmark/bench @@ -5,13 +5,17 @@ export PYTHONPATH=$(realpath ${scriptdir}/..) printf %"$(tput cols)"s |tr " " "-" echo echo "10000 spawns with fork and exec..." -time ${scriptdir}/forktest.py +time python ${scriptdir}/forktest.py printf %"$(tput cols)"s |tr " " "-" echo echo "10000 spawns with fastprocess..." -time ${scriptdir}/fptest.py +time python ${scriptdir}/fptest.py +printf %"$(tput cols)"s |tr " " "-" +echo +echo "10000 spawns with old fastprocess..." +time python ${scriptdir}/old_fptest.py printf %"$(tput cols)"s |tr " " "-" echo echo "10000 spawns with subprocess..." -time ${scriptdir}/sptest.py +time python ${scriptdir}/sptest.py printf %"$(tput cols)"s |tr " " "-" diff --git a/benchmark/old_fptest.py b/benchmark/old_fptest.py new file mode 100755 index 0000000..efc3e3f --- /dev/null +++ b/benchmark/old_fptest.py @@ -0,0 +1,8 @@ +#!/usr/bin/python3 + +from fastprocess import FastProcess, OldFastProcess + +procs = [] + +for i in range(10000): + procs.append(OldFastProcess(['echo', 'hello', 'world'], stdout=open('/dev/null', 'w'))) diff --git a/fastprocess/__init__.py b/fastprocess/__init__.py index 3ee40af..b0b27a1 100644 --- a/fastprocess/__init__.py +++ b/fastprocess/__init__.py @@ -1 +1 @@ -from fastprocess.fastprocess import FastProcess +from fastprocess.fastprocess import FastProcess, OldFastProcess diff --git a/fastprocess/fastprocess.py b/fastprocess/fastprocess.py index d075261..b716bfb 100644 --- a/fastprocess/fastprocess.py +++ b/fastprocess/fastprocess.py @@ -2,6 +2,30 @@ import signal class FastProcess: + def __init__(self, cmd, stdin=None, stdout=None, stderr=None, env=None): + file_actions = [] + if stdin: + file_actions.append((os.POSIX_SPAWN_DUP2, stdin.fileno(), 0)) + if stdout: + file_actions.append((os.POSIX_SPAWN_DUP2, stdout.fileno(), 1)) + if stderr: + file_actions.append((os.POSIX_SPAWN_DUP2, stderr.fileno(), 2)) + self.pid = os.posix_spawnp(cmd[0], cmd, env if env else os.environ, file_actions=file_actions) + + def __del__(self): + os.kill(self.pid, signal.SIGTERM) + + def terminate(self): + os.kill(self.pid, signal.SIGTERM) + + def signal(self, sig): + os.kill(self.pid, sig) + + def wait(self): + return os.WEXITSTATUS(os.waitpid(self.pid, 0)[1]) + + +class OldFastProcess: def __init__(self, cmd, stdin=None, stdout=None, stderr=None): self.pid = os.fork() if self.pid == 0: diff --git a/setup.py b/setup.py index ab4b75e..2e71718 100644 --- a/setup.py +++ b/setup.py @@ -3,13 +3,14 @@ setup( name='fastprocess', - version='0.1.0', + version='1.0.0', author='Dylan Stephano-Shachter', author_email='dstephanoshachter@gmail.com', description=('A fast subprocess library'), license='LGPL', url='https://github.com/dstathis/fastprocess', packages=['fastprocess'], + python_requires='>=3.8', long_description=open( os.path.join(os.path.dirname(os.path.abspath(__file__)), 'README.md') ).read(),