Pass around the reactor explicitly (#3385)

This commit is contained in:
Amber Brown 2018-06-22 09:37:10 +01:00 committed by GitHub
parent c2eff937ac
commit 77ac14b960
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 141 additions and 93 deletions

View file

@ -13,15 +13,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from synapse.util.logcontext import PreserveLoggingContext
from twisted.internet import defer, reactor, task
import time
import logging
from itertools import islice
import attr
from twisted.internet import defer, task
from synapse.util.logcontext import PreserveLoggingContext
logger = logging.getLogger(__name__)
@ -31,16 +30,24 @@ def unwrapFirstError(failure):
return failure.value.subFailure
@attr.s
class Clock(object):
"""A small utility that obtains current time-of-day so that time may be
mocked during unit-tests.
TODO(paul): Also move the sleep() functionality into it
"""
A Clock wraps a Twisted reactor and provides utilities on top of it.
"""
_reactor = attr.ib()
@defer.inlineCallbacks
def sleep(self, seconds):
d = defer.Deferred()
with PreserveLoggingContext():
self._reactor.callLater(seconds, d.callback, seconds)
res = yield d
defer.returnValue(res)
def time(self):
"""Returns the current system time in seconds since epoch."""
return time.time()
return self._reactor.seconds()
def time_msec(self):
"""Returns the current system time in miliseconds since epoch."""
@ -56,6 +63,7 @@ class Clock(object):
msec(float): How long to wait between calls in milliseconds.
"""
call = task.LoopingCall(f)
call.clock = self._reactor
call.start(msec / 1000.0, now=False)
return call
@ -73,7 +81,7 @@ class Clock(object):
callback(*args, **kwargs)
with PreserveLoggingContext():
return reactor.callLater(delay, wrapped_callback, *args, **kwargs)
return self._reactor.callLater(delay, wrapped_callback, *args, **kwargs)
def cancel_call_later(self, timer, ignore_errs=False):
try:

View file

@ -13,14 +13,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from twisted.internet import defer, reactor
from twisted.internet import defer
from twisted.internet.defer import CancelledError
from twisted.python import failure
from .logcontext import (
PreserveLoggingContext, make_deferred_yieldable, run_in_background
)
from synapse.util import logcontext, unwrapFirstError
from synapse.util import logcontext, unwrapFirstError, Clock
from contextlib import contextmanager
@ -31,15 +31,6 @@ from six.moves import range
logger = logging.getLogger(__name__)
@defer.inlineCallbacks
def sleep(seconds):
d = defer.Deferred()
with PreserveLoggingContext():
reactor.callLater(seconds, d.callback, seconds)
res = yield d
defer.returnValue(res)
class ObservableDeferred(object):
"""Wraps a deferred object so that we can add observer deferreds. These
observer deferreds do not affect the callback chain of the original
@ -172,13 +163,18 @@ class Linearizer(object):
# do some work.
"""
def __init__(self, name=None):
def __init__(self, name=None, clock=None):
if name is None:
self.name = id(self)
else:
self.name = name
self.key_to_defer = {}
if not clock:
from twisted.internet import reactor
clock = Clock(reactor)
self._clock = clock
@defer.inlineCallbacks
def queue(self, key):
# If there is already a deferred in the queue, we pull it out so that
@ -219,7 +215,7 @@ class Linearizer(object):
# the context manager, but it needs to happen while we hold the
# lock, and the context manager's exit code must be synchronous,
# so actually this is the only sensible place.
yield sleep(0)
yield self._clock.sleep(0)
else:
logger.info("Acquired uncontended linearizer lock %r for key %r",
@ -396,7 +392,7 @@ class DeferredTimeoutError(Exception):
"""
def add_timeout_to_deferred(deferred, timeout, on_timeout_cancel=None):
def add_timeout_to_deferred(deferred, timeout, reactor, on_timeout_cancel=None):
"""
Add a timeout to a deferred by scheduling it to be cancelled after
timeout seconds.
@ -411,6 +407,7 @@ def add_timeout_to_deferred(deferred, timeout, on_timeout_cancel=None):
Args:
deferred (defer.Deferred): deferred to be timed out
timeout (Number): seconds to time out after
reactor (twisted.internet.reactor): the Twisted reactor to use
on_timeout_cancel (callable): A callable which is called immediately
after the deferred times out, and not if this deferred is

View file

@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from twisted.internet import threads, reactor
from twisted.internet import threads
from synapse.util.logcontext import make_deferred_yieldable, run_in_background
@ -27,6 +27,7 @@ class BackgroundFileConsumer(object):
Args:
file_obj (file): The file like object to write to. Closed when
finished.
reactor (twisted.internet.reactor): the Twisted reactor to use
"""
# For PushProducers pause if we have this many unwritten slices
@ -34,9 +35,11 @@ class BackgroundFileConsumer(object):
# And resume once the size of the queue is less than this
_RESUME_ON_QUEUE_SIZE = 2
def __init__(self, file_obj):
def __init__(self, file_obj, reactor):
self._file_obj = file_obj
self._reactor = reactor
# Producer we're registered with
self._producer = None
@ -71,7 +74,10 @@ class BackgroundFileConsumer(object):
self._producer = producer
self.streaming = streaming
self._finished_deferred = run_in_background(
threads.deferToThread, self._writer
threads.deferToThreadPool,
self._reactor,
self._reactor.getThreadPool(),
self._writer,
)
if not streaming:
self._producer.resumeProducing()
@ -109,7 +115,7 @@ class BackgroundFileConsumer(object):
# producer.
if self._producer and self._paused_producer:
if self._bytes_queue.qsize() <= self._RESUME_ON_QUEUE_SIZE:
reactor.callFromThread(self._resume_paused_producer)
self._reactor.callFromThread(self._resume_paused_producer)
bytes = self._bytes_queue.get()
@ -121,7 +127,7 @@ class BackgroundFileConsumer(object):
# If its a pull producer then we need to explicitly ask for
# more stuff.
if not self.streaming and self._producer:
reactor.callFromThread(self._producer.resumeProducing)
self._reactor.callFromThread(self._producer.resumeProducing)
except Exception as e:
self._write_exception = e
raise

View file

@ -17,7 +17,6 @@ from twisted.internet import defer
from synapse.api.errors import LimitExceededError
from synapse.util.async import sleep
from synapse.util.logcontext import (
run_in_background, make_deferred_yieldable,
PreserveLoggingContext,
@ -153,7 +152,7 @@ class _PerHostRatelimiter(object):
"Ratelimit [%s]: sleeping req",
id(request_id),
)
ret_defer = run_in_background(sleep, self.sleep_msec / 1000.0)
ret_defer = run_in_background(self.clock.sleep, self.sleep_msec / 1000.0)
self.sleeping_requests.add(request_id)