Skip to content

Commit 3b72d50

Browse files
committed
Fall back to credential refresh on EDEADLK
Fixes googleapis#335 This change fixes a bug where multiple threads and/or processes using multistore_file to access the same backing store could raise IOError errno.EDEADLK to the calling application. Since EDEADLK is a possibility with concurrent access, this change instead causes a fallback to read only mode and refresh credentials if necessary.
1 parent ab3e535 commit 3b72d50

File tree

2 files changed

+46
-0
lines changed

2 files changed

+46
-0
lines changed

oauth2client/multistore_file.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,9 @@ def _lock(self):
290290
elif e.errno == errno.ENOLCK:
291291
logger.warn('File system is out of resources for writing the '
292292
'credentials file (is your disk full?).')
293+
elif e.errno == errno.EDEADLK:
294+
logger.warn('Lock contention on multistore file, opening '
295+
'in read-only mode.')
293296
else:
294297
raise
295298
if not self._file.is_locked():

tests/test_multistore_file.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""Unit tests for oauth2client.multistore_file."""
2+
3+
import errno
4+
import os
5+
import tempfile
6+
import unittest
7+
8+
from oauth2client import multistore_file
9+
10+
11+
class _MockLockedFile(object):
12+
13+
def __init__(self, filename_str, error_code):
14+
self.filename_str = filename_str
15+
self.error_code = error_code
16+
self.open_and_lock_called = False
17+
18+
def open_and_lock(self):
19+
self.open_and_lock_called = True
20+
raise IOError(self.error_code, '')
21+
22+
def is_locked(self):
23+
return False
24+
25+
def filename(self):
26+
return self.filename_str
27+
28+
29+
class MultistoreFileTests(unittest.TestCase):
30+
31+
def test_lock_file_raises_ioerror(self):
32+
filehandle, filename = tempfile.mkstemp()
33+
os.close(filehandle)
34+
35+
try:
36+
for error_code in (errno.EDEADLK, errno.ENOSYS, errno.ENOLCK):
37+
multistore = multistore_file._MultiStore(filename)
38+
multistore._file = _MockLockedFile(filename, error_code)
39+
# Should not raise even though the underlying file class did.
40+
multistore._lock()
41+
self.assertTrue(multistore._file.open_and_lock_called)
42+
finally:
43+
os.unlink(filename)

0 commit comments

Comments
 (0)