~launchpad-results/launchpad-results/trunk

« back to all changes in this revision

Viewing changes to lib/lpresults/scripts/lock.py

  • Committer: Marc Tardif
  • Date: 2011-09-14 01:25:40 UTC
  • Revision ID: marc.tardif@canonical.com-20110914012540-1gs255vhv6kb0mg4
Added updating of submissions periodically.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2010-2011 Canonical Ltd.  This software is licensed under the
 
2
# GNU Affero General Public License version 3 (see the file LICENSE).
 
3
 
 
4
__metaclass__ = type
 
5
 
 
6
__all__ = [
 
7
    "Lock",
 
8
    ]
 
9
 
 
10
import os
 
11
import fcntl
 
12
 
 
13
from fcntl import (
 
14
    LOCK_UN,
 
15
    LOCK_SH,
 
16
    LOCK_EX,
 
17
    LOCK_NB,
 
18
    )
 
19
 
 
20
 
 
21
class Lock:
 
22
 
 
23
    def __init__(self, filename):
 
24
        self._lock_filename = filename
 
25
        self._lock_fd = None
 
26
        self._lock_inode = None
 
27
        self._lock_kind = LOCK_UN
 
28
        self._lock_count = 0
 
29
 
 
30
    def __del__(self):
 
31
        try:
 
32
            os.close(self._lock_fd)
 
33
        except:
 
34
            pass
 
35
 
 
36
    def acquired(self, write=False):
 
37
        if write:
 
38
            return self._lock_kind == LOCK_EX
 
39
        else:
 
40
            return self._lock_kind != LOCK_UN
 
41
 
 
42
    def acquire(self, write=False, block=True):
 
43
        """Acquire the lock for reading or writing.
 
44
 
 
45
        :param write: If true, the path is acquired for writing.
 
46
        :param block: If false, the call will return immediately on failure.
 
47
 
 
48
        This method is reentrant, but not thread-safe.  To use instances
 
49
        of this class concurrently across threads, some instructions
 
50
        will have to be protected to make it thread-safe.
 
51
        """
 
52
        if (self._lock_kind == LOCK_EX or
 
53
            self._lock_kind == LOCK_SH and not write):
 
54
            self._lock_count += 1
 
55
            return True
 
56
 
 
57
        result = False
 
58
        while True:
 
59
            if self._lock_fd is None:
 
60
                self._lock_fd = os.open(
 
61
                    self._lock_filename, os.O_RDWR | os.O_CREAT, 0644)
 
62
                flags = fcntl.fcntl(self._lock_fd, fcntl.F_GETFD, 0)
 
63
                flags |= fcntl.FD_CLOEXEC
 
64
                fcntl.fcntl(self._lock_fd, fcntl.F_SETFD, flags)
 
65
                self._lock_inode = os.fstat(self._lock_fd).st_ino
 
66
            if write:
 
67
                flags = kind = LOCK_EX
 
68
            else:
 
69
                flags = kind = LOCK_SH
 
70
            if not block:
 
71
                flags |= LOCK_NB
 
72
            try:
 
73
                # Locks threads and local system, bad network behavior.
 
74
                fcntl.flock(self._lock_fd, flags)
 
75
            except IOError:
 
76
                # Someone else has the lock and we're not blocking.
 
77
                break
 
78
 
 
79
            try:
 
80
                # Locks local system and network, bad thread behavior.
 
81
                fcntl.lockf(self._lock_fd, flags)
 
82
            except IOError:
 
83
                # Someone else has the lock and we're not blocking.
 
84
                fcntl.flock(self._lock_fd, LOCK_UN)
 
85
                break
 
86
 
 
87
            try:
 
88
                lock_inode = os.stat(self._lock_filename).st_ino
 
89
                if lock_inode != self._lock_inode:
 
90
                    raise OSError
 
91
            except OSError:
 
92
                # Oops.. someone removed the filename while
 
93
                # holding the lock.  Let's continue and try
 
94
                # to acquire the new lock.
 
95
                os.close(self._lock_fd)
 
96
                self._lock_fd = None
 
97
                continue
 
98
 
 
99
            self._lock_count += 1
 
100
 
 
101
            if write or self._lock_kind == LOCK_UN:
 
102
                self._lock_kind = kind
 
103
 
 
104
            result = True
 
105
            break
 
106
 
 
107
        return result
 
108
 
 
109
    def release(self):
 
110
        """Release the lock.
 
111
 
 
112
        This method is reentrant, but not thread safe.  To use instances
 
113
        of this class concurrently across threads, some instructions
 
114
        will have to be protected to make it thread-safe.
 
115
        """
 
116
        if self._lock_count == 0:
 
117
            raise RuntimeError("Lock not acquired")
 
118
 
 
119
        self._lock_count -= 1
 
120
 
 
121
        if self._lock_count == 0:
 
122
            os.close(self._lock_fd)
 
123
            os.unlink(self._lock_filename)
 
124
            self._lock_fd = None
 
125
            self._lock_kind = LOCK_UN