Skip to content

Conversation

@cdecker
Copy link
Member

@cdecker cdecker commented Feb 7, 2019

This PR allows RPC methods and hook-callbacks to defer returning a result by adding the sync=False argument to the @plugin.hook or @plugin.method annotations. If the annotation includes the sync=False argument it'll disregard the return value of the callback function. The function can then use the set_result or set_exception methods on the request object that they get as a parameter, when it suits them.

This allows passing a pending request off to another thread, stashing them in the local state, or any other operation, and still return a result at a later point in time. set_exception and set_result are threadsafe and can be called from anywhere.

I'm still a bit unsure if sync is the best name for the argument, I wanted to use async but the linter yells at me that async will become a keyword in python 3.8, so no luck there. Maybe @renepickhardt or our namer in chief @niftynei has a good idea? 😉

Fixes #2167

@cdecker cdecker added this to the v0.6.4 milestone Feb 7, 2019
@cdecker cdecker self-assigned this Feb 7, 2019
from lightning import Plugin

plugin = Plugin()
requests = []
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can just dynamically attach this to object instance

plugin.requests = []

Rather that using globals below.

I suspect in the future people may also just subclass Plugin and add their local state there

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, will fixup

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decided to initialize with a @plugin.init() decorator to make sure we have the new field whenever it is used.

monkey_patch(self, stdout=True, stderr=True)

self.add_method("getmanifest", self._getmanifest)
self.add_method("getmanifest", self._getmanifest, sync=True)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have an whimsical ideas, but if async is taken, how about is_async ?

Copy link
Member Author

@cdecker cdecker Feb 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a few variants that I thought of that might also work: direct=False, background=True, _async=True, or future=True. (All variants are shown with the async behavior as enabled here)

I tend to like background since it sort of describes that we want the thing to continue in the background.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for background. i also like wait=False

Copy link
Collaborator

@niftynei niftynei left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@rustyrussell
Copy link
Contributor

It seems weird to me to use a variant of the same annotation for a completely different beast.

After all, these functions will look quite different, won't they? Maybe @method_async ?

@cdecker cdecker modified the milestones: v0.6.4, v0.7 Feb 11, 2019
@cdecker
Copy link
Member Author

cdecker commented Feb 19, 2019

I went with the background proposal, since that is the best description I could come up with. It's best not to have too many decorators since the interface gets too terse imho.

@conscott
Copy link
Collaborator

Concept ACK - tested this before, but it needs rebase

As for having this is a new decorator vs decorator arg, I don't have a super strong option, but I see rusty's case. A decorator should do one predictable thing and the background significantly changes the behavior, and expected way to handle the result.

You guys have better visibility on how many foreseeable decorators may be apart of the plugin lib, so I can't make a call on that part, which may be a valid concern.

Look there, I managed to agree with you both and say nothing :)

@cdecker
Copy link
Member Author

cdecker commented Feb 21, 2019

As for having this is a new decorator vs decorator arg, I don't have a super strong option, but I see rusty's case. A decorator should do one predictable thing and the background significantly changes the behavior, and expected way to handle the result.

Ok, I consider myself overruled, and will create a separate decorator instead. tbh I didn't have a super strong reason to go for the single decorator, but you convinced me :-)

Sending around unnamed tuples is bound to cause some issues sooner or
later, so we just create a quick class that holds all the information
about a plugin method.

Signed-off-by: Christian Decker <[email protected]>
We well need this in the next commit to be able to return from an
asynchronous call. We also guard stdout access with a reentrant lock
since we are no longer guaranteed that all communication happens on
the same thread.

Signed-off-by: Christian Decker <[email protected]>
This caused me to backtrack quite a bit, so this should help debugging
in the future.

Signed-off-by: Christian Decker <[email protected]>
These are a bit special and are handled separately.

Signed-off-by: Christian Decker <[email protected]>
This isn't a problem for now since we don't support multithreading,
and only allow synchronous calls, but eventually this'll become
important.

Signed-off-by: Christian Decker <[email protected]>
This indicates that the method or hook will accepts a request
parameter, and will use that to return the result or raise an
exception instead of returning the return value. This allows the hook
or method to stash the incomplete request or pass it around, without
blocking the JSON-RPC interface.

Signed-off-by: Christian Decker <[email protected]>
Technically this is a node, not a direct peer, so this is correct.

Signed-off-by: Christian Decker <[email protected]>
Suggested-by: Rusty Russell <@rustyrussell>
Suggested-by: Conor Scott <@conscott>
Signed-off-by: Christian Decker <[email protected]>
These weren't checked by CI yet, and they are really short so I just added
them to the check-python target.

Signed-off-by: Christian Decker <[email protected]>
Copy link
Contributor

@rustyrussell rustyrussell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack 93be4d9

Great work, as always!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants