Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit b45e485

Browse files
author
David Robertson
authored
Merge branch 'develop' into dmr/add-importlib_metadata-dependency
2 parents 86481a5 + 7ec9b06 commit b45e485

File tree

12 files changed

+70
-14
lines changed

12 files changed

+70
-14
lines changed

.github/workflows/docs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
- name: Setup mdbook
2323
uses: peaceiris/actions-mdbook@4b5ef36b314c2599664ca107bb8c02412548d79d # v1.1.14
2424
with:
25-
mdbook-version: '0.4.9'
25+
mdbook-version: '0.4.17'
2626

2727
- name: Build the documentation
2828
# mdbook will only create an index.html if we're including docs/README.md in SUMMARY.md.

changelog.d/12320.misc

Lines changed: 0 additions & 1 deletion
This file was deleted.

changelog.d/12339.doc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Upgrade the version of `mdbook` in CI to 0.4.17.

changelog.d/12354.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Update docstrings for `ReadWriteLock` tests.

changelog.d/12358.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a long-standing bug where `Linearizer`s could get stuck if a cancellation were to happen at the wrong time.

changelog.d/12366.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make `StreamToken.from_string` and `RoomStreamToken.parse` propagate cancellations instead of replacing them with `SynapseError`s.

changelog.d/12379.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Remove temporary pin of signedjson<=1.1.1 that was added in Synapse 1.56.0.

synapse/python_dependencies.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
"unpaddedbase64>=1.1.0",
4949
"canonicaljson>=1.4.0",
5050
# we use the type definitions added in signedjson 1.1.
51-
"signedjson>=1.1.0,<=1.1.1",
51+
"signedjson>=1.1.0",
5252
"pynacl>=1.2.1",
5353
"idna>=2.5",
5454
# validating SSL certs for IP addresses requires service_identity 18.1.

synapse/types.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from unpaddedbase64 import decode_base64
4040
from zope.interface import Interface
4141

42+
from twisted.internet.defer import CancelledError
4243
from twisted.internet.interfaces import (
4344
IReactorCore,
4445
IReactorPluggableNameResolver,
@@ -540,6 +541,8 @@ async def parse(cls, store: "PurgeEventsStore", string: str) -> "RoomStreamToken
540541
stream=stream,
541542
instance_map=frozendict(instance_map),
542543
)
544+
except CancelledError:
545+
raise
543546
except Exception:
544547
pass
545548
raise SynapseError(400, "Invalid room stream token %r" % (string,))
@@ -705,6 +708,8 @@ async def from_string(cls, store: "DataStore", string: str) -> "StreamToken":
705708
return cls(
706709
await RoomStreamToken.parse(store, keys[0]), *(int(k) for k in keys[1:])
707710
)
711+
except CancelledError:
712+
raise
708713
except Exception:
709714
raise SynapseError(400, "Invalid stream token")
710715

synapse/util/async_helpers.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,11 @@ async def _acquire_lock(self, key: Hashable) -> _LinearizerEntry:
453453
#
454454
# This needs to happen while we hold the lock. We could put it on the
455455
# exit path, but that would slow down the uncontended case.
456-
await self._clock.sleep(0)
456+
try:
457+
await self._clock.sleep(0)
458+
except CancelledError:
459+
self._release_lock(key, entry)
460+
raise
457461

458462
return entry
459463

tests/util/test_linearizer.py

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16-
from typing import Callable, Hashable, Tuple
16+
from typing import Hashable, Tuple
17+
18+
from typing_extensions import Protocol
1719

1820
from twisted.internet import defer, reactor
1921
from twisted.internet.base import ReactorBase
@@ -25,10 +27,15 @@
2527
from tests import unittest
2628

2729

30+
class UnblockFunction(Protocol):
31+
def __call__(self, pump_reactor: bool = True) -> None:
32+
...
33+
34+
2835
class LinearizerTestCase(unittest.TestCase):
2936
def _start_task(
3037
self, linearizer: Linearizer, key: Hashable
31-
) -> Tuple["Deferred[None]", "Deferred[None]", Callable[[], None]]:
38+
) -> Tuple["Deferred[None]", "Deferred[None]", UnblockFunction]:
3239
"""Starts a task which acquires the linearizer lock, blocks, then completes.
3340
3441
Args:
@@ -52,11 +59,12 @@ async def task() -> None:
5259

5360
d = defer.ensureDeferred(task())
5461

55-
def unblock() -> None:
62+
def unblock(pump_reactor: bool = True) -> None:
5663
unblock_d.callback(None)
5764
# The next task, if it exists, will acquire the lock and require a kick of
5865
# the reactor to advance.
59-
self._pump()
66+
if pump_reactor:
67+
self._pump()
6068

6169
return d, acquired_d, unblock
6270

@@ -212,3 +220,38 @@ def test_cancellation(self) -> None:
212220
)
213221
unblock3()
214222
self.successResultOf(d3)
223+
224+
def test_cancellation_during_sleep(self) -> None:
225+
"""Tests cancellation during the sleep just after waiting for a `Linearizer`."""
226+
linearizer = Linearizer()
227+
228+
key = object()
229+
230+
d1, acquired_d1, unblock1 = self._start_task(linearizer, key)
231+
self.assertTrue(acquired_d1.called)
232+
233+
# Create a second task, waiting for the first task.
234+
d2, acquired_d2, _ = self._start_task(linearizer, key)
235+
self.assertFalse(acquired_d2.called)
236+
237+
# Create a third task, waiting for the second task.
238+
d3, acquired_d3, unblock3 = self._start_task(linearizer, key)
239+
self.assertFalse(acquired_d3.called)
240+
241+
# Once the first task completes, cancel the waiting second task while it is
242+
# sleeping just after acquiring the lock.
243+
unblock1(pump_reactor=False)
244+
self.successResultOf(d1)
245+
d2.cancel()
246+
self._pump()
247+
248+
self.assertTrue(d2.called)
249+
self.failureResultOf(d2, CancelledError)
250+
251+
# The third task should continue running.
252+
self.assertTrue(
253+
acquired_d3.called,
254+
"Third task did not get the lock after the second task was cancelled",
255+
)
256+
unblock3()
257+
self.successResultOf(d3)

tests/util/test_rwlock.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ def _start_reader_or_writer(
4040
4141
Returns:
4242
A tuple of three `Deferred`s:
43-
* A `Deferred` that resolves with `return_value` once the reader or writer
44-
completes successfully.
43+
* A cancellable `Deferred` for the entire read or write operation that
44+
resolves with `return_value` on successful completion.
4545
* A `Deferred` that resolves once the reader or writer acquires the lock.
4646
* A `Deferred` that blocks the reader or writer. Must be resolved by the
4747
caller to allow the reader or writer to release the lock and complete.
@@ -87,8 +87,8 @@ def _start_nonblocking_reader(
8787
8888
Returns:
8989
A tuple of two `Deferred`s:
90-
* A `Deferred` that resolves with `return_value` once the reader completes
91-
successfully.
90+
* A cancellable `Deferred` for the entire read operation that resolves with
91+
`return_value` on successful completion.
9292
* A `Deferred` that resolves once the reader acquires the lock.
9393
"""
9494
d, acquired_d, unblock_d = self._start_reader_or_writer(
@@ -106,8 +106,8 @@ def _start_nonblocking_writer(
106106
107107
Returns:
108108
A tuple of two `Deferred`s:
109-
* A `Deferred` that resolves with `return_value` once the writer completes
110-
successfully.
109+
* A cancellable `Deferred` for the entire write operation that resolves
110+
with `return_value` on successful completion.
111111
* A `Deferred` that resolves once the writer acquires the lock.
112112
"""
113113
d, acquired_d, unblock_d = self._start_reader_or_writer(

0 commit comments

Comments
 (0)