Fall back to credential refresh on EDEADLK
Fixes https://github.com/google/oauth2client/issues/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.
This commit is contained in:
@@ -290,6 +290,9 @@ class _MultiStore(object):
|
||||
elif e.errno == errno.ENOLCK:
|
||||
logger.warn('File system is out of resources for writing the '
|
||||
'credentials file (is your disk full?).')
|
||||
elif e.errno == errno.EDEADLK:
|
||||
logger.warn('Lock contention on multistore file, opening '
|
||||
'in read-only mode.')
|
||||
else:
|
||||
raise
|
||||
if not self._file.is_locked():
|
||||
|
||||
43
tests/test_multistore_file.py
Normal file
43
tests/test_multistore_file.py
Normal file
@@ -0,0 +1,43 @@
|
||||
"""Unit tests for oauth2client.multistore_file."""
|
||||
|
||||
import errno
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from oauth2client import multistore_file
|
||||
|
||||
|
||||
class _MockLockedFile(object):
|
||||
|
||||
def __init__(self, filename_str, error_code):
|
||||
self.filename_str = filename_str
|
||||
self.error_code = error_code
|
||||
self.open_and_lock_called = False
|
||||
|
||||
def open_and_lock(self):
|
||||
self.open_and_lock_called = True
|
||||
raise IOError(self.error_code, '')
|
||||
|
||||
def is_locked(self):
|
||||
return False
|
||||
|
||||
def filename(self):
|
||||
return self.filename_str
|
||||
|
||||
|
||||
class MultistoreFileTests(unittest.TestCase):
|
||||
|
||||
def test_lock_file_raises_ioerror(self):
|
||||
filehandle, filename = tempfile.mkstemp()
|
||||
os.close(filehandle)
|
||||
|
||||
try:
|
||||
for error_code in (errno.EDEADLK, errno.ENOSYS, errno.ENOLCK):
|
||||
multistore = multistore_file._MultiStore(filename)
|
||||
multistore._file = _MockLockedFile(filename, error_code)
|
||||
# Should not raise even though the underlying file class did.
|
||||
multistore._lock()
|
||||
self.assertTrue(multistore._file.open_and_lock_called)
|
||||
finally:
|
||||
os.unlink(filename)
|
||||
Reference in New Issue
Block a user