Merge pull request #3775 from halseth/mobile-listener-signal

[mobile] make Ready signal for custom listeners
This commit is contained in:
Johan T. Halseth 2019-12-17 12:26:04 +01:00 committed by GitHub
commit f0bd4e775b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 69 additions and 26 deletions

48
lnd.go

@ -59,16 +59,25 @@ var (
networkDir string networkDir string
) )
// ListnerWithSignal is a net.Listner that has an additional Ready channel that
// will be closed when a server starts listening.
type ListenerWithSignal struct {
net.Listener
// Ready will be closed by the server listening on Listener.
Ready chan struct{}
}
// ListenerCfg is a wrapper around custom listeners that can be passed to lnd // ListenerCfg is a wrapper around custom listeners that can be passed to lnd
// when calling its main method. // when calling its main method.
type ListenerCfg struct { type ListenerCfg struct {
// WalletUnlocker can be set to the listener to use for the wallet // WalletUnlocker can be set to the listener to use for the wallet
// unlocker. If nil a regular network listener will be created. // unlocker. If nil a regular network listener will be created.
WalletUnlocker net.Listener WalletUnlocker *ListenerWithSignal
// RPCListener can be set to the listener to use for the RPC server. If // RPCListener can be set to the listener to use for the RPC server. If
// nil a regular network listener will be created. // nil a regular network listener will be created.
RPCListener net.Listener RPCListener *ListenerWithSignal
} }
// rpcListeners is a function type used for closures that fetches a set of RPC // rpcListeners is a function type used for closures that fetches a set of RPC
@ -76,7 +85,8 @@ type ListenerCfg struct {
// with these listeners. If no custom listeners are present, this should return // with these listeners. If no custom listeners are present, this should return
// normal listeners from the RPC endpoints defined in the config, and server // normal listeners from the RPC endpoints defined in the config, and server
// options specifying TLS. // options specifying TLS.
type rpcListeners func() ([]net.Listener, func(), []grpc.ServerOption, error) type rpcListeners func() ([]*ListenerWithSignal, func(), []grpc.ServerOption,
error)
// Main is the true entry point for lnd. This function is required since defers // Main is the true entry point for lnd. This function is required since defers
// created in the top-level scope of a main method aren't executed if os.Exit() // created in the top-level scope of a main method aren't executed if os.Exit()
@ -235,10 +245,10 @@ func Main(lisCfg ListenerCfg) error {
// getListeners is a closure that creates listeners from the // getListeners is a closure that creates listeners from the
// RPCListeners defined in the config. It also returns a cleanup // RPCListeners defined in the config. It also returns a cleanup
// closure and the server options to use for the GRPC server. // closure and the server options to use for the GRPC server.
getListeners := func() ([]net.Listener, func(), []grpc.ServerOption, getListeners := func() ([]*ListenerWithSignal, func(),
error) { []grpc.ServerOption, error) {
var grpcListeners []net.Listener var grpcListeners []*ListenerWithSignal
for _, grpcEndpoint := range cfg.RPCListeners { for _, grpcEndpoint := range cfg.RPCListeners {
// Start a gRPC server listening for HTTP/2 // Start a gRPC server listening for HTTP/2
// connections. // connections.
@ -248,7 +258,11 @@ func Main(lisCfg ListenerCfg) error {
grpcEndpoint) grpcEndpoint)
return nil, nil, nil, err return nil, nil, nil, err
} }
grpcListeners = append(grpcListeners, lis) grpcListeners = append(
grpcListeners, &ListenerWithSignal{
Listener: lis,
Ready: make(chan struct{}),
})
} }
cleanup := func() { cleanup := func() {
@ -262,7 +276,7 @@ func Main(lisCfg ListenerCfg) error {
// walletUnlockerListeners is a closure we'll hand to the wallet // walletUnlockerListeners is a closure we'll hand to the wallet
// unlocker, that will be called when it needs listeners for its GPRC // unlocker, that will be called when it needs listeners for its GPRC
// server. // server.
walletUnlockerListeners := func() ([]net.Listener, func(), walletUnlockerListeners := func() ([]*ListenerWithSignal, func(),
[]grpc.ServerOption, error) { []grpc.ServerOption, error) {
// If we have chosen to start with a dedicated listener for the // If we have chosen to start with a dedicated listener for the
@ -271,8 +285,8 @@ func Main(lisCfg ListenerCfg) error {
// TODO(halseth): any point in adding TLS support for custom // TODO(halseth): any point in adding TLS support for custom
// listeners? // listeners?
if lisCfg.WalletUnlocker != nil { if lisCfg.WalletUnlocker != nil {
return []net.Listener{lisCfg.WalletUnlocker}, func() {}, return []*ListenerWithSignal{lisCfg.WalletUnlocker},
[]grpc.ServerOption{}, nil func() {}, []grpc.ServerOption{}, nil
} }
// Otherwise we'll return the regular listeners. // Otherwise we'll return the regular listeners.
@ -501,8 +515,8 @@ func Main(lisCfg ListenerCfg) error {
// rpcListeners is a closure we'll hand to the rpc server, that will be // rpcListeners is a closure we'll hand to the rpc server, that will be
// called when it needs listeners for its GPRC server. // called when it needs listeners for its GPRC server.
rpcListeners := func() ([]net.Listener, func(), []grpc.ServerOption, rpcListeners := func() ([]*ListenerWithSignal, func(),
error) { []grpc.ServerOption, error) {
// If we have chosen to start with a dedicated listener for the // If we have chosen to start with a dedicated listener for the
// rpc server, we return it directly, and empty server options // rpc server, we return it directly, and empty server options
@ -510,8 +524,8 @@ func Main(lisCfg ListenerCfg) error {
// TODO(halseth): any point in adding TLS support for custom // TODO(halseth): any point in adding TLS support for custom
// listeners? // listeners?
if lisCfg.RPCListener != nil { if lisCfg.RPCListener != nil {
return []net.Listener{lisCfg.RPCListener}, func() {}, return []*ListenerWithSignal{lisCfg.RPCListener},
[]grpc.ServerOption{}, nil func() {}, []grpc.ServerOption{}, nil
} }
// Otherwise we'll return the regular listeners. // Otherwise we'll return the regular listeners.
@ -841,9 +855,13 @@ func waitForWalletPassword(restEndpoints []net.Addr,
for _, lis := range listeners { for _, lis := range listeners {
wg.Add(1) wg.Add(1)
go func(lis net.Listener) { go func(lis *ListenerWithSignal) {
rpcsLog.Infof("password RPC server listening on %s", rpcsLog.Infof("password RPC server listening on %s",
lis.Addr()) lis.Addr())
// Close the ready chan to indicate we are listening.
close(lis.Ready)
wg.Done() wg.Done()
grpcServer.Serve(lis) grpcServer.Serve(lis)
}(lis) }(lis)

@ -16,7 +16,11 @@ import (
// extraArgs can be used to pass command line arguments to lnd that will // extraArgs can be used to pass command line arguments to lnd that will
// override what is found in the config file. Example: // override what is found in the config file. Example:
// extraArgs = "--bitcoin.testnet --lnddir=\"/tmp/folder name/\" --profile=5050" // extraArgs = "--bitcoin.testnet --lnddir=\"/tmp/folder name/\" --profile=5050"
func Start(extraArgs string, callback Callback) { //
// The unlockerReady callback is called when the WalletUnlocker service is
// ready, and rpcReady is called after the wallet has been unlocked and lnd is
// ready to accept RPC calls.
func Start(extraArgs string, unlockerReady, rpcReady Callback) {
// Split the argument string on "--" to get separated command line // Split the argument string on "--" to get separated command line
// arguments. // arguments.
var splitArgs []string var splitArgs []string
@ -33,11 +37,24 @@ func Start(extraArgs string, callback Callback) {
// startup. // startup.
os.Args = append(os.Args, splitArgs...) os.Args = append(os.Args, splitArgs...)
// Set up channels that will be notified when the RPC servers are ready
// to accept calls.
var (
unlockerListening = make(chan struct{})
rpcListening = make(chan struct{})
)
// We call the main method with the custom in-memory listeners called // We call the main method with the custom in-memory listeners called
// by the mobile APIs, such that the grpc server will use these. // by the mobile APIs, such that the grpc server will use these.
cfg := lnd.ListenerCfg{ cfg := lnd.ListenerCfg{
WalletUnlocker: walletUnlockerLis, WalletUnlocker: &lnd.ListenerWithSignal{
RPCListener: lightningLis, Listener: walletUnlockerLis,
Ready: unlockerListening,
},
RPCListener: &lnd.ListenerWithSignal{
Listener: lightningLis,
Ready: rpcListening,
},
} }
// Call the "real" main in a nested manner so the defers will properly // Call the "real" main in a nested manner so the defers will properly
@ -53,9 +70,15 @@ func Start(extraArgs string, callback Callback) {
} }
}() }()
// TODO(halseth): callback when RPC server is actually running. Since // Finally we start two go routines that will call the provided
// the RPC server might take a while to start up, the client might // callbacks when the RPC servers are ready to accept calls.
// assume it is ready to accept calls when this callback is sent, while go func() {
// it's not. <-unlockerListening
callback.OnResponse([]byte("started")) unlockerReady.OnResponse([]byte{})
}()
go func() {
<-rpcListening
rpcReady.OnResponse([]byte{})
}()
} }

@ -9,7 +9,6 @@ import (
"fmt" "fmt"
"io" "io"
"math" "math"
"net"
"net/http" "net/http"
"sort" "sort"
"strings" "strings"
@ -461,7 +460,7 @@ type rpcServer struct {
// listeners is a list of listeners to use when starting the grpc // listeners is a list of listeners to use when starting the grpc
// server. We make it configurable such that the grpc server can listen // server. We make it configurable such that the grpc server can listen
// on custom interfaces. // on custom interfaces.
listeners []net.Listener listeners []*ListenerWithSignal
// listenerCleanUp are a set of closures functions that will allow this // listenerCleanUp are a set of closures functions that will allow this
// main RPC server to clean up all the listening socket created for the // main RPC server to clean up all the listening socket created for the
@ -715,8 +714,11 @@ func (r *rpcServer) Start() error {
// With all the sub-servers started, we'll spin up the listeners for // With all the sub-servers started, we'll spin up the listeners for
// the main RPC server itself. // the main RPC server itself.
for _, lis := range r.listeners { for _, lis := range r.listeners {
go func(lis net.Listener) { go func(lis *ListenerWithSignal) {
rpcsLog.Infof("RPC server listening on %s", lis.Addr()) rpcsLog.Infof("RPC server listening on %s", lis.Addr())
// Close the ready chan to indicate we are listening.
close(lis.Ready)
r.grpcServer.Serve(lis) r.grpcServer.Serve(lis)
}(lis) }(lis)
} }