Skip to content

Commit 61a575b

Browse files
author
Tim Smith
committed
Tolerate a restarting fairlock service while connecting
Cope with the case where the fairlock service is restarted while something is in the middle of connecting to it. Signed-off-by: Tim Smith <[email protected]>
1 parent ce5b554 commit 61a575b

File tree

3 files changed

+23
-8
lines changed

3 files changed

+23
-8
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*.o
66
*.swp
77
*.pyc
8+
*.kate-swp
89
tests/faultinjection/pfilter
910
tests/faultinjection/libipq.c
1011
tests/faultinjection/libipq/libipq.h

misc/fairlock/fairlock.py

+14-6
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,29 @@ def _ensure_service(self):
4848
if time.time() > timeout:
4949
raise FairlockServiceTimeout(f"Timed out starting service {service}")
5050

51+
def _connect_and_recv(self):
52+
while True:
53+
self.sock.connect(self.sockname)
54+
# Merely being connected is not enough. Read a small blob of data.
55+
b = self.sock.recv(10)
56+
if len(b) > 0:
57+
return True
58+
# If we got a zero-length return, it means the service exited while we
59+
# were waiting. Any timeout we put here would be a max wait time to acquire
60+
# the lock, which is dangerous.
61+
self._ensure_service()
62+
5163
def __enter__(self):
5264
if self.connected:
5365
raise FairlockDeadlock(f"Deadlock on Fairlock resource '{self.name}'")
5466

5567
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
5668
self.sock.setblocking(True)
5769
try:
58-
self.sock.connect(self.sockname)
59-
# Merely being connected is not enough. Read a small blob of data.
60-
self.sock.recv(10)
70+
self._connect_and_recv()
6171
except (FileNotFoundError, ConnectionRefusedError):
6272
self._ensure_service()
63-
self.sock.connect(self.sockname)
64-
# Merely being connected is not enough. Read a small blob of data.
65-
self.sock.recv(10)
73+
self._connect_and_recv()
6674

6775
self.sock.send(f'{os.getpid()} - {time.monotonic()}'.encode())
6876
self.connected = True

tests/test_fairlock.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def test_first_lock(self):
2323
mock_sock = mock.MagicMock()
2424
self.mock_socket.socket.return_value = mock_sock
2525
mock_sock.connect.side_effect = [FileNotFoundError(), 0]
26+
mock_sock.recv.side_effect = [b'Foop']
2627
self.mock_os.system.side_effect = [0, 1, 0]
2728
self.mock_time.time.side_effect = [0, 0, 0]
2829

@@ -38,6 +39,7 @@ def test_first_lock_timeout(self):
3839
mock_sock = mock.MagicMock()
3940
self.mock_socket.socket.return_value = mock_sock
4041
mock_sock.connect.side_effect = [FileNotFoundError(), 0]
42+
mock_sock.recv.side_effect = [b'Foop']
4143
self.mock_os.system.side_effect = [0, 1, 1, 1, 0]
4244
self.mock_time.time.side_effect = [0, 1, 3]
4345

@@ -53,6 +55,7 @@ def test_second_lock(self):
5355
mock_sock = mock.MagicMock()
5456
self.mock_socket.socket.return_value = mock_sock
5557
mock_sock.connect.side_effect = [0]
58+
mock_sock.recv.side_effect = [b'Foop']
5659

5760
with Fairlock("test"):
5861
print("Hello World")
@@ -68,6 +71,8 @@ def test_two_locks(self):
6871
self.mock_socket.socket.side_effect = [mock_sock1, mock_sock2]
6972
mock_sock1.connect.side_effect = [FileNotFoundError(), 0]
7073
mock_sock2.connect.side_effect = [FileNotFoundError(), 0]
74+
mock_sock1.recv.side_effect = [b'Foop']
75+
mock_sock2.recv.side_effect = [b'Foop']
7176
self.mock_os.system.side_effect = [0, 1, 0, 0, 1, 0]
7277
self.mock_time.time.side_effect = [0, 0, 0, 0, 0, 0]
7378

@@ -81,15 +86,16 @@ def test_double_lock_deadlock(self):
8186
Test double usage of the same lock
8287
"""
8388
mock_sock = mock.MagicMock()
84-
self.mock_socket.socket.side_effect = [mock_sock]
89+
self.mock_socket.socket.side_effect = [mock_sock]
8590
mock_sock.connect.side_effect = [FileNotFoundError(), 0]
91+
mock_sock.recv.side_effect = [b'Foop', b'Foop']
8692
self.mock_os.system.side_effect = [0, 1, 0, 0, 1, 0]
8793
self.mock_time.time.side_effect = [0, 0, 0, 0, 0, 0]
8894

8995
with self.assertRaises(FairlockDeadlock) as err:
9096
with Fairlock("test") as l:
9197
n = Fairlock("test")
92-
self.assertEquals(l, n)
98+
self.assertEqual(l, n)
9399
# Real code would use another 'with Fairlock("test")' here but we cannot
94100
# do that because it insists on having a code block as a body, which would
95101
# then not be reached, causing a "Test code not fully covered" failure

0 commit comments

Comments
 (0)