Prevent double launch in Python (improved version)

Previously, I wrote a version using only the lock file, but it was unpopular (?), So I made a version using the pid file. It's easy to use because it's defined as a decorator. Just do the following:

@singleprocess
def main();
    pass

The operation is as follows.

  1. Write the pid to the pid file before starting the main process. Delete the pid file when the process is finished.
  2. If the pid process written in this pid file already has the same signature as its own process (it is recognized as the same program), it is determined that the same program is already running and started. do not do.
  3. As the signature to judge the identity of the process, use the part of the command line from the first parameter (python) and the second parameter (python file name) excluding the directory name.
  4. With this alone, there is a problem with consistency when the timing of reading and writing the pid file overlaps, so a locking mechanism is included when reading and writing the pid file. It uses a normal flock.
  5. Therefore, if the pid file terminates abnormally while reading or writing, it may not start unless the pid file is deleted manually, but it is not affected by the abnormal termination of the main process. Abnormal termination while reading and writing the pid file does not occur so often, so this is not a problem in practice.

The pid file is specified to be placed under / tmp, and security is not considered. In order to use it in production, it may be necessary to change the getSignature algorithm and the location of the pid file depending on the application.

def singleprocess(func):
    def basenameWithoutExt(path):
        return os.path.splitext(os.path.basename(path))[0]

    def getSignature(pid):
        processDirectory = '/proc/%d/' % (pid,)
        if os.path.isdir(processDirectory):
            args = open(processDirectory + 'cmdline').read().split("\x00")
            return [os.path.basename(args[0]), os.path.basename(args[1])]
        return None

    @contextmanager
    def filelockingcontext(path):
        open(path, "a").close() #ensure file to exist for locking
        with open(path) as lockFd:
            fcntl.flock(lockFd, fcntl.LOCK_EX)
            yield
            fcntl.flock(lockFd, fcntl.LOCK_UN)

    def wrapper(*args, **kwargs):
        pidFilePath = tempfile.gettempdir() + '/' + basenameWithoutExt(sys.argv[0]) + '.pid'
        with filelockingcontext(pidFilePath):
            runningPid = ""
            with open(pidFilePath) as readFd:
                runningPid = readFd.read()
            if runningPid != "" and getSignature(int(runningPid)) == getSignature(os.getpid()) is not None:
                print("process already exists", file=sys.stderr)
                exit()
            with open(pidFilePath, "w") as writeFd:
                writeFd.write(str(os.getpid()))
        try:
            func(*args, **kwargs)
        finally:
            with filelockingcontext(pidFilePath):
                os.remove(pidFilePath)
    return wrapper

Recommended Posts

Prevent double launch in Python (improved version)
Prevent double boot from cron in Python
Prevent double launch of django commands
python launch
How to check opencv version in python
Launch a Flask app in Python Anywhere
Double pendulum equation of motion in python
Quadtree in Python --2
CURL in python
Metaprogramming in Python
Python 3.3 in Anaconda
Geocoding in python
SendKeys in Python
Put python xgboost in max osx (llvm version)
Meta-analysis in Python
Unittest in python
Discord in Python
DCI in Python
quicksort in python
nCr in python
N-Gram in Python
Programming in python
Plink in Python
[Improved version] Script to monitor CPU with Python
Constant in python
Lifegame in Python.
FizzBuzz in Python
Sqlite in python
StepAIC in Python
N-gram in python
LINE-Bot [0] in Python
Csv in python
Disassemble in Python
Reflection in Python
PYTHON2.7 64bit version
Constant in python
nCr in Python.
format in python
Scons in Python3
Puyo Puyo in python
python in virtualenv
PPAP in Python
Quad-tree in Python
Reflection in Python
Chemistry in Python
Hashable in python
DirectLiNGAM in Python
LiNGAM in Python
Flatten in python
flatten in python
"Linear regression" and "Probabilistic version of linear regression" in Python "Bayesian linear regression"
Beginners can use Python for web scraping (1) Improved version
[Internal_math version (2)] Decoding the AtCoder Library ~ Implementation in Python ~
Sorted list in Python
Daily AtCoder # 36 in Python
Clustering text in Python
Daily AtCoder # 2 in Python
Implement Enigma in python
Daily AtCoder # 32 in Python
Daily AtCoder # 6 in Python
Edit fonts in Python