/loggerhead/trunk

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/loggerhead/trunk
69 by Robey Pointer
switch the cache and text index to use file locking so they can be used by
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
83 by Robey Pointer
when creating a lockfile object, if there's an existing lock file on the
28
MAX_STALE_TIME = 5 * 60
29
69 by Robey Pointer
switch the cache and text index to use file locking so they can be used by
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.
144.1.10 by Michael Hudson
run reindent.py over the loggerhead package
38
69 by Robey Pointer
switch the cache and text index to use file locking so they can be used by
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
83 by Robey Pointer
when creating a lockfile object, if there's an existing lock file on the
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)
144.1.10 by Michael Hudson
run reindent.py over the loggerhead package
53
69 by Robey Pointer
switch the cache and text index to use file locking so they can be used by
54
    @with_lock
55
    def _try_acquire(self):
56
        if self._count > 0:
57
            self._count += 1
58
            return True
59
        try:
230.1.1 by Steve 'Ashcrow' Milner
Updated to follow pep8.
60
            fd = os.open(self._filename,
61
                         os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0600)
69 by Robey Pointer
switch the cache and text index to use file locking so they can be used by
62
            os.close(fd)
63
            self._count += 1
64
            return True
65
        except OSError:
66
            return False
144.1.10 by Michael Hudson
run reindent.py over the loggerhead package
67
69 by Robey Pointer
switch the cache and text index to use file locking so they can be used by
68
    def acquire(self):
230.1.1 by Steve 'Ashcrow' Milner
Updated to follow pep8.
69
        # try over and over, sleeping on exponential backoff with
70
        # an upper limit of about 5 seconds
69 by Robey Pointer
switch the cache and text index to use file locking so they can be used by
71
        pause = 0.1
71 by Robey Pointer
exponential backoff isn't really working for the lockfile, so keep it down
72
        #max_pause = 5.0
73
        max_pause = 0.1
69 by Robey Pointer
switch the cache and text index to use file locking so they can be used by
74
        while True:
75
            if self._try_acquire():
76
                return
77
            time.sleep(pause)
78
            pause = min(pause * 2.0, max_pause)
79
80
    @with_lock
81
    def release(self):
82
        self._count -= 1
83
        if self._count == 0:
84
            os.remove(self._filename)