Skip to content

Commit 066327a

Browse files
authored
Merge pull request #61 from lightninglabs/mobile-connection-nonblocking
Mobile: spin off blocking RPCConnection call into goroutine
2 parents b66c421 + 1d483e8 commit 066327a

File tree

1 file changed

+141
-119
lines changed

1 file changed

+141
-119
lines changed

mobile/mobile.go

Lines changed: 141 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ type mobileClient struct {
5050
localPrivCreateCallback NativeCallback
5151
remoteKeyReceiveCallback NativeCallback
5252
authDataCallback NativeCallback
53+
54+
mutex sync.Mutex
5355
}
5456

5557
func newMobileClient() *mobileClient {
@@ -91,7 +93,7 @@ var (
9193

9294
m = make(map[string]*mobileClient)
9395

94-
// mMutex should always be used to guard the m map
96+
// mMutex should always be used to guard the mutex map.
9597
mMutex sync.RWMutex
9698

9799
registry = make(map[string]func(context.Context,
@@ -100,6 +102,20 @@ var (
100102
interceptorLogsInitialize = false
101103
)
102104

105+
// getClient returns the mobile client for the given namespace or an error if no
106+
// client exists.
107+
func getClient(nameSpace string) (*mobileClient, error) {
108+
mMutex.Lock()
109+
defer mMutex.Unlock()
110+
111+
mc, ok := m[nameSpace]
112+
if !ok {
113+
return nil, fmt.Errorf("unknown namespace: %v", nameSpace)
114+
}
115+
116+
return mc, nil
117+
}
118+
103119
// InitLNC sets up everything required for LNC to run including
104120
// signal interceptor, logs, and an instance of the mobile client.
105121
func InitLNC(nameSpace, debugLevel string) error {
@@ -173,85 +189,91 @@ func ConnectServer(nameSpace string, mailboxServer string, isDevServer bool,
173189
}
174190
}
175191

176-
mMutex.Lock()
177-
defer mMutex.Unlock()
178-
179-
mc, ok := m[nameSpace]
180-
if !ok {
181-
return fmt.Errorf("unknown namespace: %s", nameSpace)
182-
}
183-
184-
statusChecker, lndConnect, err := core.MailboxRPCConnection(
185-
mailboxServer, pairingPhrase, localPriv, remotePub,
186-
func(key *btcec.PublicKey) error {
187-
mc.remoteKeyReceiveCallback.SendResult(
188-
hex.EncodeToString(key.SerializeCompressed()),
189-
)
190-
191-
return nil
192-
}, func(data []byte) error {
193-
parts := strings.Split(string(data), ": ")
194-
if len(parts) != 2 || parts[0] != "Macaroon" {
195-
return fmt.Errorf("authdata does " +
196-
"not contain a macaroon")
197-
}
198-
199-
macBytes, err := hex.DecodeString(parts[1])
200-
if err != nil {
201-
return err
202-
}
203-
204-
mac := &macaroon.Macaroon{}
205-
err = mac.UnmarshalBinary(macBytes)
206-
if err != nil {
207-
return fmt.Errorf("unable to decode "+
208-
"macaroon: %v", err)
209-
}
210-
211-
mc.mac = mac
192+
// Since the connection function is blocking, we need to spin it off
193+
// in another goroutine here. See https://pkg.go.dev/syscall/js#FuncOf.
194+
go func() {
195+
mc, err := getClient(nameSpace)
196+
if err != nil {
197+
log.Errorf("Error getting client: %v", err)
198+
return
199+
}
212200

213-
mc.authDataCallback.SendResult(string(data))
201+
mc.mutex.Lock()
202+
defer mc.mutex.Unlock()
203+
204+
statusChecker, lndConnect, err := core.MailboxRPCConnection(
205+
mailboxServer, pairingPhrase, localPriv, remotePub,
206+
func(key *btcec.PublicKey) error {
207+
mc.remoteKeyReceiveCallback.SendResult(
208+
hex.EncodeToString(key.SerializeCompressed()),
209+
)
210+
211+
return nil
212+
}, func(data []byte) error {
213+
parts := strings.Split(string(data), ": ")
214+
if len(parts) != 2 || parts[0] != "Macaroon" {
215+
return fmt.Errorf("authdata does " +
216+
"not contain a macaroon")
217+
}
218+
219+
macBytes, err := hex.DecodeString(parts[1])
220+
if err != nil {
221+
return err
222+
}
223+
224+
mac := &macaroon.Macaroon{}
225+
err = mac.UnmarshalBinary(macBytes)
226+
if err != nil {
227+
return fmt.Errorf("unable to decode "+
228+
"macaroon: %v", err)
229+
}
230+
231+
mc.mac = mac
232+
233+
mc.authDataCallback.SendResult(string(data))
234+
235+
return nil
236+
},
237+
)
238+
if err != nil {
239+
log.Errorf("Error running wasm client: %v", err)
240+
}
214241

215-
return nil
216-
},
217-
)
218-
if err != nil {
219-
return err
220-
}
242+
mc.statusChecker = statusChecker
243+
mc.lndConn, err = lndConnect()
244+
if err != nil {
245+
log.Errorf("Error running wasm client: %v", err)
246+
}
221247

222-
mc.statusChecker = statusChecker
223-
mc.lndConn, err = lndConnect()
224-
if err != nil {
225-
return err
226-
}
248+
log.Debugf("Mobile client connected to RPC")
249+
}()
227250

228-
log.Debugf("Mobile client connected to RPC")
229251
return nil
230252
}
231253

232254
// IsConnected returns whether or not there is an active connection.
233255
func IsConnected(nameSpace string) (bool, error) {
234-
mMutex.Lock()
235-
defer mMutex.Unlock()
236-
237-
mc, ok := m[nameSpace]
238-
if !ok {
239-
return false, fmt.Errorf("unknown namespace: %s", nameSpace)
256+
mc, err := getClient(nameSpace)
257+
if err != nil {
258+
return false, fmt.Errorf("error getting client: %v", err)
240259
}
241260

261+
mc.mutex.Lock()
262+
defer mc.mutex.Unlock()
263+
242264
return mc.lndConn != nil, nil
243265
}
244266

245267
// Disconnect closes the RPC connection.
246268
func Disconnect(nameSpace string) error {
247-
mMutex.Lock()
248-
defer mMutex.Unlock()
249-
250-
mc, ok := m[nameSpace]
251-
if !ok {
252-
return fmt.Errorf("unknown namespace: %s", nameSpace)
269+
mc, err := getClient(nameSpace)
270+
if err != nil {
271+
return fmt.Errorf("error getting client: %v", err)
253272
}
254273

274+
mc.mutex.Lock()
275+
defer mc.mutex.Unlock()
276+
255277
if mc.lndConn != nil {
256278
if err := mc.lndConn.Close(); err != nil {
257279
log.Errorf("Error closing RPC connection: %v", err)
@@ -264,14 +286,14 @@ func Disconnect(nameSpace string) error {
264286

265287
// Status returns the status of the LNC RPC connection.
266288
func Status(nameSpace string) (string, error) {
267-
mMutex.Lock()
268-
defer mMutex.Unlock()
269-
270-
mc, ok := m[nameSpace]
271-
if !ok {
272-
return "", fmt.Errorf("unknown namespace: %s", nameSpace)
289+
mc, err := getClient(nameSpace)
290+
if err != nil {
291+
return "", fmt.Errorf("error getting client: %v", err)
273292
}
274293

294+
mc.mutex.Lock()
295+
defer mc.mutex.Unlock()
296+
275297
if mc.statusChecker == nil {
276298
return "", nil
277299
}
@@ -284,14 +306,14 @@ func Status(nameSpace string) (string, error) {
284306
func RegisterLocalPrivCreateCallback(nameSpace string,
285307
c NativeCallback) error {
286308

287-
mMutex.Lock()
288-
defer mMutex.Unlock()
289-
290-
mc, ok := m[nameSpace]
291-
if !ok {
292-
return fmt.Errorf("unknown namespace: %s", nameSpace)
309+
mc, err := getClient(nameSpace)
310+
if err != nil {
311+
return fmt.Errorf("error getting client: %v", err)
293312
}
294313

314+
mc.mutex.Lock()
315+
defer mc.mutex.Unlock()
316+
295317
mc.localPrivCreateCallback = c
296318

297319
return nil
@@ -302,14 +324,14 @@ func RegisterLocalPrivCreateCallback(nameSpace string,
302324
func RegisterRemoteKeyReceiveCallback(nameSpace string,
303325
c NativeCallback) error {
304326

305-
mMutex.Lock()
306-
defer mMutex.Unlock()
307-
308-
mc, ok := m[nameSpace]
309-
if !ok {
310-
return fmt.Errorf("unknown namespace: %s", nameSpace)
327+
mc, err := getClient(nameSpace)
328+
if err != nil {
329+
return fmt.Errorf("error getting client: %v", err)
311330
}
312331

332+
mc.mutex.Lock()
333+
defer mc.mutex.Unlock()
334+
313335
mc.remoteKeyReceiveCallback = c
314336

315337
return nil
@@ -318,14 +340,14 @@ func RegisterRemoteKeyReceiveCallback(nameSpace string,
318340
// RegisterAuthDataCallback sets up the native callbacks upon
319341
// receiving auth data.
320342
func RegisterAuthDataCallback(nameSpace string, c NativeCallback) error {
321-
mMutex.Lock()
322-
defer mMutex.Unlock()
323-
324-
mc, ok := m[nameSpace]
325-
if !ok {
326-
return fmt.Errorf("unknown namespace: %s", nameSpace)
343+
mc, err := getClient(nameSpace)
344+
if err != nil {
345+
return fmt.Errorf("error getting client: %v", err)
327346
}
328347

348+
mc.mutex.Lock()
349+
defer mc.mutex.Unlock()
350+
329351
mc.authDataCallback = c
330352

331353
return nil
@@ -335,14 +357,14 @@ func RegisterAuthDataCallback(nameSpace string, c NativeCallback) error {
335357
func InvokeRPC(nameSpace string, rpcName string, requestJSON string,
336358
c NativeCallback) error {
337359

338-
mMutex.Lock()
339-
defer mMutex.Unlock()
340-
341-
mc, ok := m[nameSpace]
342-
if !ok {
343-
return fmt.Errorf("unknown namespace: %s", nameSpace)
360+
mc, err := getClient(nameSpace)
361+
if err != nil {
362+
return fmt.Errorf("error getting client: %v", err)
344363
}
345364

365+
mc.mutex.Lock()
366+
defer mc.mutex.Unlock()
367+
346368
if rpcName == "" {
347369
return fmt.Errorf("param rpcName required")
348370
}
@@ -380,14 +402,14 @@ func InvokeRPC(nameSpace string, rpcName string, requestJSON string,
380402

381403
// GetExpiry returns the expiration time of the connection macaroon.
382404
func GetExpiry(nameSpace string) (string, error) {
383-
mMutex.Lock()
384-
defer mMutex.Unlock()
385-
386-
mc, ok := m[nameSpace]
387-
if !ok {
388-
return "", fmt.Errorf("unknown namespace: %s", nameSpace)
405+
mc, err := getClient(nameSpace)
406+
if err != nil {
407+
return "", fmt.Errorf("error getting client: %v", err)
389408
}
390409

410+
mc.mutex.Lock()
411+
defer mc.mutex.Unlock()
412+
391413
if mc.mac == nil {
392414
return "", fmt.Errorf("macaroon not obtained yet. GetExpiry" +
393415
"should only be called once the connection is" +
@@ -404,14 +426,14 @@ func GetExpiry(nameSpace string) (string, error) {
404426

405427
// IsReadOnly returns whether or not the connection macaroon is read-only.
406428
func IsReadOnly(nameSpace string) (bool, error) {
407-
mMutex.Lock()
408-
defer mMutex.Unlock()
409-
410-
mc, ok := m[nameSpace]
411-
if !ok {
412-
return false, fmt.Errorf("unknown namespace: %s", nameSpace)
429+
mc, err := getClient(nameSpace)
430+
if err != nil {
431+
return false, fmt.Errorf("error getting client: %v", err)
413432
}
414433

434+
mc.mutex.Lock()
435+
defer mc.mutex.Unlock()
436+
415437
if mc.mac == nil {
416438
log.Errorf("macaroon not obtained yet. IsReadOnly should " +
417439
"only be called once the connection is complete")
@@ -430,16 +452,16 @@ func IsReadOnly(nameSpace string) (bool, error) {
430452
}
431453

432454
// HasPermissions returns whether or not the connection macaroon
433-
// has a specificed permission.
455+
// has a specified permission.
434456
func HasPermissions(nameSpace, permission string) (bool, error) {
435-
mMutex.Lock()
436-
defer mMutex.Unlock()
437-
438-
mc, ok := m[nameSpace]
439-
if !ok {
440-
return false, fmt.Errorf("unknown namespace: %s", nameSpace)
457+
mc, err := getClient(nameSpace)
458+
if err != nil {
459+
return false, fmt.Errorf("error getting client: %v", err)
441460
}
442461

462+
mc.mutex.Lock()
463+
defer mc.mutex.Unlock()
464+
443465
if permission == "" {
444466
return false, nil
445467
}
@@ -553,17 +575,17 @@ func validateArgs(mailboxServer, localPrivKey, remotePubKey string) error {
553575
// parseKeys parses the given keys from their string format and calls callback
554576
// functions where appropriate. NOTE: This function assumes that the parameter
555577
// combinations have been checked by validateArgs.
556-
func parseKeys(nameSpace, localPrivKey, remotePubKey string) (
557-
keychain.SingleKeyECDH, *btcec.PublicKey, error) {
578+
func parseKeys(nameSpace, localPrivKey,
579+
remotePubKey string) (keychain.SingleKeyECDH, *btcec.PublicKey, error) {
558580

559-
mMutex.Lock()
560-
defer mMutex.Unlock()
561-
562-
mc, ok := m[nameSpace]
563-
if !ok {
564-
return nil, nil, fmt.Errorf("unknown namespace: %s", nameSpace)
581+
mc, err := getClient(nameSpace)
582+
if err != nil {
583+
return nil, nil, fmt.Errorf("error getting client: %v", err)
565584
}
566585

586+
mc.mutex.Lock()
587+
defer mc.mutex.Unlock()
588+
567589
var (
568590
localStaticKey keychain.SingleKeyECDH
569591
remoteStaticKey *btcec.PublicKey

0 commit comments

Comments
 (0)