Merge pull request #858 from tahoe-lafs/3471.immediate-localwrapper

Allow LocalWrapper to be immediate

Fixes: ticket:3471
This commit is contained in:
Jean-Paul Calderone 2020-10-15 13:15:35 -04:00 committed by GitHub
commit 4d56b5f4ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 31 additions and 6 deletions

0
newsfragments/3471.minor Normal file
View File

View File

@ -26,13 +26,19 @@ if PY2:
from past.builtins import unicode from past.builtins import unicode
import os import os
from base64 import b32encode
from functools import (
partial,
)
from zope.interface import implementer from zope.interface import implementer
from twisted.application import service from twisted.application import service
from twisted.internet import defer from twisted.internet import defer
from twisted.python.failure import Failure from twisted.python.failure import Failure
from twisted.web.error import Error from twisted.web.error import Error
from foolscap.api import Referenceable, fireEventually, RemoteException from foolscap.api import Referenceable, fireEventually, RemoteException
from base64 import b32encode from foolscap.ipb import (
IRemoteReference,
)
import treq import treq
from allmydata.util.assertutil import _assert from allmydata.util.assertutil import _assert
@ -59,14 +65,29 @@ class IntentionalError(Exception):
class Marker(object): class Marker(object):
pass pass
fireNow = partial(defer.succeed, None)
@implementer(IRemoteReference)
class LocalWrapper(object): class LocalWrapper(object):
def __init__(self, original): """
A ``LocalWrapper`` presents the remote reference interface to a local
object which implements a ``RemoteInterface``.
"""
def __init__(self, original, fireEventually=fireEventually):
"""
:param Callable[[], Deferred[None]] fireEventually: Get a Deferred
that will fire at some point. This is used to control when
``callRemote`` calls the remote method. The default value allows
the reactor to iterate before the call happens. Use ``fireNow``
to call the remote method synchronously.
"""
self.original = original self.original = original
self.broken = False self.broken = False
self.hung_until = None self.hung_until = None
self.post_call_notifier = None self.post_call_notifier = None
self.disconnectors = {} self.disconnectors = {}
self.counter_by_methname = {} self.counter_by_methname = {}
self._fireEventually = fireEventually
def _clear_counters(self): def _clear_counters(self):
self.counter_by_methname = {} self.counter_by_methname = {}
@ -82,7 +103,7 @@ class LocalWrapper(object):
# selected return values. # selected return values.
def wrap(a): def wrap(a):
if isinstance(a, Referenceable): if isinstance(a, Referenceable):
return LocalWrapper(a) return self._wrap(a)
else: else:
return a return a
args = tuple([wrap(a) for a in args]) args = tuple([wrap(a) for a in args])
@ -110,7 +131,7 @@ class LocalWrapper(object):
return d2 return d2
return _really_call() return _really_call()
d = fireEventually() d = self._fireEventually()
d.addCallback(lambda res: _call()) d.addCallback(lambda res: _call())
def _wrap_exception(f): def _wrap_exception(f):
return Failure(RemoteException(f)) return Failure(RemoteException(f))
@ -124,10 +145,10 @@ class LocalWrapper(object):
if methname == "allocate_buckets": if methname == "allocate_buckets":
(alreadygot, allocated) = res (alreadygot, allocated) = res
for shnum in allocated: for shnum in allocated:
allocated[shnum] = LocalWrapper(allocated[shnum]) allocated[shnum] = self._wrap(allocated[shnum])
if methname == "get_buckets": if methname == "get_buckets":
for shnum in res: for shnum in res:
res[shnum] = LocalWrapper(res[shnum]) res[shnum] = self._wrap(res[shnum])
return res return res
d.addCallback(_return_membrane) d.addCallback(_return_membrane)
if self.post_call_notifier: if self.post_call_notifier:
@ -141,6 +162,10 @@ class LocalWrapper(object):
def dontNotifyOnDisconnect(self, marker): def dontNotifyOnDisconnect(self, marker):
del self.disconnectors[marker] del self.disconnectors[marker]
def _wrap(self, value):
return LocalWrapper(value, self._fireEventually)
def wrap_storage_server(original): def wrap_storage_server(original):
# Much of the upload/download code uses rref.version (which normally # Much of the upload/download code uses rref.version (which normally
# comes from rrefutil.add_version_to_remote_reference). To avoid using a # comes from rrefutil.add_version_to_remote_reference). To avoid using a