mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2024-10-01 11:49:51 -04:00
Fix rare error in ReadWriteLock
when writers complete immediately (#12105)
Signed-off-by: Sean Quah <seanq@element.io>
This commit is contained in:
parent
300ed0b8a6
commit
4d6b6c17c8
1
changelog.d/12105.bugfix
Normal file
1
changelog.d/12105.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
Fix an extremely rare, long-standing bug in `ReadWriteLock` that would cause an error when a newly unblocked writer completes instantly.
|
@ -555,7 +555,10 @@ class ReadWriteLock:
|
|||||||
finally:
|
finally:
|
||||||
with PreserveLoggingContext():
|
with PreserveLoggingContext():
|
||||||
new_defer.callback(None)
|
new_defer.callback(None)
|
||||||
if self.key_to_current_writer[key] == new_defer:
|
# `self.key_to_current_writer[key]` may be missing if there was another
|
||||||
|
# writer waiting for us and it completed entirely within the
|
||||||
|
# `new_defer.callback()` call above.
|
||||||
|
if self.key_to_current_writer.get(key) == new_defer:
|
||||||
self.key_to_current_writer.pop(key)
|
self.key_to_current_writer.pop(key)
|
||||||
|
|
||||||
return _ctx_manager()
|
return _ctx_manager()
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
from twisted.internet.defer import Deferred
|
||||||
|
|
||||||
from synapse.util.async_helpers import ReadWriteLock
|
from synapse.util.async_helpers import ReadWriteLock
|
||||||
|
|
||||||
@ -83,3 +84,32 @@ class ReadWriteLockTestCase(unittest.TestCase):
|
|||||||
self.assertTrue(d.called)
|
self.assertTrue(d.called)
|
||||||
with d.result:
|
with d.result:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def test_lock_handoff_to_nonblocking_writer(self):
|
||||||
|
"""Test a writer handing the lock to another writer that completes instantly."""
|
||||||
|
rwlock = ReadWriteLock()
|
||||||
|
key = "key"
|
||||||
|
|
||||||
|
unblock: "Deferred[None]" = Deferred()
|
||||||
|
|
||||||
|
async def blocking_write():
|
||||||
|
with await rwlock.write(key):
|
||||||
|
await unblock
|
||||||
|
|
||||||
|
async def nonblocking_write():
|
||||||
|
with await rwlock.write(key):
|
||||||
|
pass
|
||||||
|
|
||||||
|
d1 = defer.ensureDeferred(blocking_write())
|
||||||
|
d2 = defer.ensureDeferred(nonblocking_write())
|
||||||
|
self.assertFalse(d1.called)
|
||||||
|
self.assertFalse(d2.called)
|
||||||
|
|
||||||
|
# Unblock the first writer. The second writer will complete without blocking.
|
||||||
|
unblock.callback(None)
|
||||||
|
self.assertTrue(d1.called)
|
||||||
|
self.assertTrue(d2.called)
|
||||||
|
|
||||||
|
# The `ReadWriteLock` should operate as normal.
|
||||||
|
d3 = defer.ensureDeferred(nonblocking_write())
|
||||||
|
self.assertTrue(d3.called)
|
||||||
|
Loading…
Reference in New Issue
Block a user