Welcome, guest | Sign In | My Account | Store | Cart

Because I didn't see a good implementation, I've posted here my own implementation of a lockfile context manager. It's POSIX-only because I don't have a Windows machine to test cross-platform atomicity on. Sorry about that.

Python, 18 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import contextlib, errno, os, time

@contextlib.contextmanager
def flock(path, wait_delay=.1):
    while True:
        try:
            fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR)
        except OSError, e:
            if e.errno != errno.EEXIST:
                raise
            time.sleep(wait_delay)
            continue
        else:
            break
    try:
        yield fd
    finally:
        os.unlink(path)

Usage is:

with flock('.lockfile'):
   # do whatever.

If you want to actually use the file descriptor, 'as' and 'fdopen' are all you need:

with flock('.lockfile') as fd:
  lockfile = os.fdopen(fd, 'r+')

6 comments

Florian Mayer 15 years, 4 months ago  # | flag

I don't get why you unlink(remove) the file in the end? Could you explain?

Florian Mayer 15 years, 4 months ago  # | flag

Please delete the previous comment, I shouldn't post when it's 1AM. I misread lockfile as filelock.

Vadim Zaliva 14 years, 12 months ago  # | flag

repeated usage as:

with flock('.lockfile'):
   # do whatever.

is causing file descriptor leak!

Max Polk 14 years, 8 months ago  # | flag

See also recipe 576891

Aryeh Leib Taurog 14 years, 6 months ago  # | flag

To fix the file descriptor leak the finally clause should also recycle the descriptor:

finally:
    os.close(fd)
    os.unlink(path)
Nick Demou 10 years, 8 months ago  # | flag

Researched the topic of having a single instance of a program running for about an hour and the simplest and robust solution is using unix domain sockets --linux only-- (see http://stackoverflow.com/a/1662504/1011025).

The code to acquire the lock is only 6 lines long:

try:
  s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
  s.bind( '\0your_programs_name_here__lock') 
except socket.error:
  print "Process already running"
  sys.exit(0)
...
## code that needs to run only once here
...
# no clean up needed! -- lock is released as soon as
# s gets out of scope or program ends/killed etc