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

Nothing fancy here. Locks can take many forms. Here are a few.

Python, 168 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
"""lock module"""

from time import time, sleep
import threading
import fcntl
from abc import ABCMeta


def wait(delay):
    delay = min(delay*2, .05)
    sleep(delay)
    return delay


class Lock(object):
    """A generic lock object, based on threading.Lock()."""
    __metaclass__ = ABCMeta
    def __init__(self):
        self._locked = False
    def acquire(self, blocking=True, timeout=None):
        if blocking:
            delay = 0.0005
            if timeout is None:
                while self._locked:
                    delay = wait(delay)
            else:
                end = time() + timeout
                while time() < end:
                    if not self._locked:
                        break
                    delay = wait(delay)
                else:
                    return False
            self._locked = True
            return True
        elif timeout is not None:
            raise ValueError("can't specify a timeout "
                             "for a non-blocking call")
        if self._locked:
            return False
        self._locked = True
        return True
    def release(self):
        if not self._locked:
            raise RuntimeError("release unlocked lock")
        self._locked = False
    def locked(self):
        return self._locked

    # for the "with" statement
    def __enter__(self): self.acquire()
    def __exit__(self, *args): self.release()
Lock.register(type(threading.Lock()))


class ThreadingLock(Lock):
    """A subclass of Lock that wraps a threading lock."""
    def __init__(self):
        self._lock = threading.Lock()
    def acquire(self, blocking=True, timeout=None):
        if not blocking and timeout is not None:
            raise ValueError("can't specify a timeout "
                             "for a non-blocking call")
        if timeout is None:
            return self._lock.acquire(blocking)
        else:
            #return self._lock.acquire(blocking, timeout)
            raise NotImplementedError
    def release(self):
        self._lock.release()
    def locked(self):
        return self._lock.locked()


class LockableFile(object):
    """A POSIX file with filesystem locking."""
    def __init__(self, file, mode="r"):
        self._own = False
        if isinstance(file, str):
            file = open(file, mode)
            self._own = True
        self.file = file
        self._locked = False
    def __getattr__(self, name):
        return getattr(self.file, name)
    def lock(self, timeout=3):
        if self._locked:
            return
        # couldn't get lock
        error = None
        end = time() + timeout
        while time() < end:
            try:
                fcntl.lockf(self.file, fcntl.LOCK_EX | fcntl.LOCK_NB)
            except IOError, e:
                error = e
                delay = wait(delay)
            else:
                break
        else:
            if error: raise error
        self._locked = True
    def unlock(self):
        fcntl.lockf(self.file, fcntl.LOCK_UN)
        self._locked = False
    def __enter__(self):
        self.lock()
    def __exit__(self, *args):
        self.unlock()
        if self._own:
            self.file.close()


class LockFile(Lock):
    """A Lock subclass implemented on top of a lock file."""
    DEFAULT = "locked"
    def __init__(self, path, value=DEFAULT):
        self.path = path
        self.default = value
    def acquire(self, value=None, blocking=True, timeout=None):
        if value is None:
            value = self.default
        if value is None:
            raise TypeError("missing value")
        try:
            lockfile = LockableFile(self.path, "r+")
        except IOError:
            lockfile = LockableFile(self.path, "a+")
            lockfile.seek(0)
        with lockfile:
            content = lockfile.read()
            #if value is not self.DEFAULT and content == value:
            #    return True
            if not content:
                lockfile.seek(0)
                lockfile.truncate()
                lockfile.write(value)
                return True
            if blocking:
                raise NotImplementedError
            elif timeout is not None:
                raise ValueError("can't specify a timeout "
                                 "for a non-blocking call")
        return False
    def release(self, value=None):
        if value is None:
            value = self.default
        value = str(value)
        try:
            lockfile = LockableFile(self.path, "r+")
        except IOError:
            return
        with lockfile:
            content = lockfile.read()
            if content != value:
                raise RuntimeError("got '%s', expected '%s'" %
                                   (value, content))
            lockfile.seek(0)
            lockfile.truncate()
    def value(self):
        return open(self.path).read()
    locked = value
    def clear(self):
        try:
            lockfile = LockableFile(self.path, "r+")
        except IOError:
            return
        lockfile.truncate()

Examples

lock = Lock()
assert lock.acquire()
assert not lock.acquire(False)
assert not lock.acquire(timeout=0.5)
assert lock.locked()
lock.release()
assert not lock.locked()

lock = ThreadingLock()
assert lock.acquire(False)
assert not lock.acquire(False)
assert lock.locked()
lock.release()
assert not lock.locked()

lock = LockFile("/tmp/.test_lock_1")
lock.clear()
assert lock.acquire(blocking=False)
assert not lock.acquire(blocking=False)
assert lock.locked()
lock.release()
assert not lock.locked()

lock = LockFile("/tmp/.test_lock_2")
lock.clear()
assert lock.acquire("disabled", blocking=False)
assert not lock.acquire("disabled", blocking=False)
assert lock.locked()
assert not lock.acquire("12345", blocking=False)
try: lock.release("12345")
except RuntimeError: pass
else: assert False, "expected RuntimeError"
lock.release("disabled")
assert not lock.locked()
assert lock.acquire("12345", blocking=False)
assert lock.locked()
assert not lock.acquire("12345", blocking=False)
assert not lock.acquire("disabled", blocking=False)
lock.release("12345")
assert not lock.locked()

lock = LockFile("/tmp/.test_lock_3")
lock.clear()
assert lock.acquire(blocking=False)
assert lock.locked()
lock = LockFile("/tmp/.test_lock_3")
assert not lock.acquire(blocking=False)
lock.release()
assert not lock.locked()
lock = LockFile("/tmp/.test_lock_3")
assert lock.acquire(blocking=False)
assert lock.locked()
lock.release()
Created by Eric Snow on Wed, 19 Oct 2011 (MIT)
Python recipes (4591)
Eric Snow's recipes (39)

Required Modules

  • (none specified)

Other Information and Tasks