How to implement the functionality of lockfile command

I'm trying to implement a filesystem-based lock for a software, which runs on a cluster. The underlying shared filesystem is implemented using DRBD, so synchronicity can be considered guaranteed. My current implementation looks like the following:

# check the lock file                                                                                                 
if os.path.isfile(lockfile):                                                                                       
    if time.time() - os.path.getmtime(lockfile) > 3600:                                                             
        logInfo('lock is older than 3600s, removing it and going on collecting result.')                           
        os.remove(lockfile)                                                                                        
    else:                                                                                                             
        logInfo('Previous action is still on-going, please wait until it\'s finished or timeout.')
        sys.exit(1)

# create the lock file                                                                                                
open(lockfile, 'w').close();

Clearly, situations may occur when multiple instances of the script running on different machines in the cluster may unanimously consider the system unlocked, create the lockfile and perform the operations that would require mutual exclusion.

So to sum it up, I need a locking facility which is filesystem-based, and lock check and creation together forms an atomic operation.

The same functionality can be achieved in a shell script using the lockfile command.

Upvotes: 3

Views: 1067

Answers (1)

A solution can be achieved by using os.open, which wraps the open system call:

import os,errno
def lockfile(filename):
    try:
        os.close(os.open(filename, os.O_CREAT | os.O_EXCL | os.O_WRONLY));
    except OSError as e:
        if e.errno == errno.EEXIST:  # System is already locked as the file already exists.
            return False
        else:  # Something unexpected went wrong so reraise the exception.
            raise
    return True  # System has successfully been locked by the function

Note the second parameter of os.open(). According to this answer, when using the flag O_EXCL in conjunction with O_CREAT, and pathname already exists, then open() will fail, or to be more exact, will raise an OSError with errno EEXIST, which in our case means that the system is already locked. However, when the path points to a nonexistent file, it will immediately be created, not leaving a time frame for other users of the filesystem to concurrently do the same steps. And according to this one, the described technique can be considered widely platform-independent.

Upvotes: 2

Related Questions