~loggerhead-team/loggerhead/trunk-rich

« back to all changes in this revision

Viewing changes to loggerhead/lockfile.py

  • Committer: Robey Pointer
  • Date: 2007-05-21 07:43:39 UTC
  • Revision ID: robey@lag.net-20070521074339-3wh6r0grwfzkvspj
bug 98826: allow "head:" to be used as a valid revid to represent the current
branch head.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
# Copyright (C) 2006  Robey Pointer <robey@lag.net>
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
#
 
18
 
 
19
import os
 
20
import threading
 
21
import time
 
22
 
 
23
from loggerhead import util
 
24
 
 
25
 
 
26
with_lock = util.with_lock('_tlock', 'LockFile')
 
27
 
 
28
MAX_STALE_TIME = 5 * 60
 
29
 
 
30
 
 
31
class LockFile (object):
 
32
    """
 
33
    simple lockfile implementation that mimics the API of threading.Lock, so
 
34
    it can be used interchangably.  it's actually a reentrant lock, so the
 
35
    lock may be acquired multiple times by the same thread, as long as it's
 
36
    released an equal number of times.  unlike threading.Lock, this lock can
 
37
    be used across processes.
 
38
    
 
39
    this uses os.open(O_CREAT|O_EXCL), which apparently works even on windows,
 
40
    but will not work over NFS, if anyone still uses that.  so don't put the
 
41
    cache folder on an NFS server...
 
42
    """
 
43
 
 
44
    def __init__(self, filename):
 
45
        self._filename = filename
 
46
        # thread lock to maintain internal consistency
 
47
        self._tlock = threading.Lock()
 
48
        self._count = 0
 
49
        if os.path.exists(filename):
 
50
            # remove stale locks left over from a previous run
 
51
            if time.time() - os.stat(filename).st_mtime > MAX_STALE_TIME:
 
52
                os.remove(filename)
 
53
    
 
54
    @with_lock
 
55
    def _try_acquire(self):
 
56
        if self._count > 0:
 
57
            self._count += 1
 
58
            return True
 
59
        try:
 
60
            fd = os.open(self._filename, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0600)
 
61
            os.close(fd)
 
62
            self._count += 1
 
63
            return True
 
64
        except OSError:
 
65
            return False
 
66
    
 
67
    def acquire(self):
 
68
        # try over and over, sleeping on exponential backoff with an upper limit of about 5 seconds
 
69
        pause = 0.1
 
70
        #max_pause = 5.0
 
71
        max_pause = 0.1
 
72
        while True:
 
73
            if self._try_acquire():
 
74
                return
 
75
            time.sleep(pause)
 
76
            pause = min(pause * 2.0, max_pause)
 
77
 
 
78
    @with_lock
 
79
    def release(self):
 
80
        self._count -= 1
 
81
        if self._count == 0:
 
82
            os.remove(self._filename)