Opened 14 years ago

Closed 14 years ago

#1 closed enhancement (fixed)

IRemoteReference(localobj) should provide something with .callRemote

Reported by: Brian Warner Owned by: Brian Warner
Priority: major Milestone: 0.1.5
Component: introduction Version: 0.1.4
Keywords: Cc:

Description

robk ran into this problem: you have a central introducer and a bunch of clients, and the clients are introducing themselves to each other (to create a fully-connected mesh), and the introductions are using live references (i.e. (their-reference) sequences), then when a specific client learns about itself from the introducer, that client will receive a local object instead of a RemoteReference?. Each client will wind up with n-1 RemoteReferences? and a single local object.

which is a drag because really you'd like to treat the list of clients as a uniform array of RemoteReferences?. But on the other hand, the roundtrip behavior (which turns local objects into RemoteReferences? and back into local objects again) is awfully useful, since it lets you do EQ.

(E solves this by allowing EQ between NearReferences? and FarReferences?, but we don't have that luxury here).

Note that if the introduction is done by FURL instead of by a live reference, this is not a problem, since the client will always be performing a tub.getReference(furl) and thus will always receive a RemoteReference?, even if that furl points back to the client's own tub.

So the proposal is that we establish an interface named IRemoteReference, which contains the callRemote and callRemoteOnly methods, and create an adapter that wraps non-RemoteReference? objects in some code which provides a callRemote that looks something like this:

def callRemote(self, methname, *args, **kwargs):
    def _try():
        meth = getattr(self.original, "remote_" + methname)
        return meth(*args, **kwargs)
    return defer.maybeDeferred(_try)

and maybe even an eventual() in there for good measure.

Then the introducer code that robk uses can just do:

for peer in peers:
    d = IRemoteReference(peer).callRemote(stuff)

and not have to worry about whether 'peer' might be a local object.

Change History (2)

comment:1 Changed 14 years ago by evilrob

since callRemote on an rref always returns asynchronously, I'd argue that there should definitely be an 'eventually' in there. e.g.

def callRemote(self, methname, *args, **kwargs):
    def _try(junk):
        meth = getattr(self.original, "remote_" + methname)
        return meth(*args, **kwargs)
    d = fireEventually()
    d.addCallback(_try)
    return d

comment:2 Changed 14 years ago by Brian Warner

Resolution: fixed
Status: newclosed

Done, in [85ea73086db25f5af487045c545fc73f510c71a0], exactly as you suggested. The wrapper also implements notifyOnDisconnect and dontNotifyOnDisconnect.

To use it, do:

from foolscap.ipb import IRemoteReference

IRemoteReference(t).callRemote("methname", args)
Note: See TracTickets for help on using tickets.