-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtimeout_lock.py
More file actions
119 lines (92 loc) · 3.94 KB
/
timeout_lock.py
File metadata and controls
119 lines (92 loc) · 3.94 KB
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
""" A locking mechanism that is similar to :class:`threading.Lock`, but implements Timeout on the
acquire method via an internal :class:`threading.Event` flag.
Copyright (C) 2019 Shane Matthijs Boissevain
This program is free software: you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along with this program.
If not, see <https://www.gnu.org/licenses/>.
"""
##
# Python Imports
import threading
class Timeout_Lock(object):
""" By implementing an internal :class:`threading.Event` flag as the locking mechanism, it is
possible to leverage the :meth:`threading.Event.wait` function in order to gracefully wait for
the lock to become available, while also implementing a timeout feature.
This is accomplished by "flipping" the internal :class:`threading.Event` flag around.
**IE:** When the lock is free, the event is ``True``; when locked, the flag is ``False``.
:ivar str name: The name of this lock. Defaults to the class name.
:vartype __event: :class:`threading.Event`
:ivar __event: The underlying event that actually "controls" the lock mechanism.
"""
def __init__(self, Name=None):
##
# Instance Variables
if Name == None:
self.name = self.__class__.__name__
else:
self.name = str(Name)
self.__event = threading.Event()
self.__event.set()
def __str__(self):
if self.available:
return self.name + " (Available)"
return self.name + " (NOT Available)"
def __enter__(self):
self.acquire()
return self
def __exit__(self, exception_type, exception_value, traceback):
self.release()
##############
# Properties #
##############
@property
def available(self):
""" While not useful for acquiring or releasing the lock, provides some use in logging and
logical tests.
:rtype: bool
:returns: ``True`` if calls to acquire would return immediately and acquire the lock.
"""
return self.__event.is_set()
@property
def locked(self):
""" While not useful for acquiring or releasing the lock, provides some use in logging and
logical tests.
:rtype: bool
:returns: ``True`` if calls to acquire would block.
"""
return not self.available
####################
# Public Functions #
####################
def acquire(self, Timeout=None):
""" Acquires the lock so that all further calls will either return ``False`` if the lock is
not released before Timeout.
:type Timeout: int
:param Timeout: Number of seconds to wait for the lock to become available. ``None``
(the default) will block until :func:`~timeout_lock.Timeout_Lock.release` is called.
.. warning::
A ``Timeout`` value of ``None`` can deadlock the application if the lock never becomes
available.
:rtype: bool
:returns: ``True`` if the lock was acquired. ``False`` if the lock was not acquired within
``Timeout`` seconds.
"""
if Timeout == None:
self.__event.wait()
self.__event.clear()
return True
else:
if not self.__event.wait(Timeout):
return False
self.__event.clear()
return True
def release(self):
""" Releases the lock so that other calls can acquire the lock.
"""
if not self.__event.is_set():
self.__event.set()