lightning: synchronize sdk service access#4185
Conversation
|
@CodeRabbit review |
✅ Action performedReview finished.
|
📝 WalkthroughWalkthroughThe PR introduces thread-safe access to the Breez SDK service instance by adding a ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/lightning/lightning.go`:
- Around line 139-147: activeSDK() returns the sdkService pointer after
releasing the mutex, creating a race where Disconnect() can call Destroy() and
nil out sdkService while the caller still uses it; change sdkServiceMu from
sync.Mutex to sync.RWMutex and replace activeSDK() with a helper that acquires
an RLock for the duration of the SDK operation (either expose a new
withActiveSDK(fn(*breez_sdk_spark.BreezSdk) error) that holds RLock/RUnlock
around calling fn or require callers to take RLock/RUnlock themselves), update
Balance(), other callers, connect() and Disconnect() to use RLock for reads and
Lock for mutations, and ensure Disconnect() calls Destroy() while holding the
exclusive Lock to prevent use-after-destroy of sdkService.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 831949bd-1be3-4397-b5c1-a5fa2ee22ad1
📒 Files selected for processing (2)
backend/lightning/lightning.gobackend/lightning/payments.go
| func (lightning *Lightning) activeSDK() (*breez_sdk_spark.BreezSdk, error) { | ||
| lightning.sdkServiceMu.Lock() | ||
| defer lightning.sdkServiceMu.Unlock() | ||
|
|
||
| if lightning.Account() == nil || lightning.sdkService == nil { | ||
| return nil, errp.New("Lightning not initialized") | ||
| } | ||
| return lightning.sdkService, nil | ||
| } |
There was a problem hiding this comment.
activeSDK() releases the lock before the SDK is actually used, leaving a race window.
The mutex is released when activeSDK() returns (via defer), but the caller then uses the returned SDK pointer without holding the lock. If Disconnect() is called concurrently between activeSDK() returning and the SDK method call, the SDK will be destroyed while still in use:
- Thread A:
sdkService, _ := activeSDK()→ lock acquired, SDK returned, lock released - Thread B:
Disconnect()→ acquires lock, callsDestroy(), setssdkService = nil - Thread A:
sdkService.GetInfo(...)→ use-after-destroy
Consider using sync.RWMutex with a read lock held for the entire SDK operation duration:
🔒 Suggested pattern using RWMutex
- sdkServiceMu sync.Mutex
+ sdkServiceMu sync.RWMutexThen in Balance() and similar methods:
func (lightning *Lightning) Balance() (*accounts.Balance, error) {
- sdkService, err := lightning.activeSDK()
- if err != nil {
+ lightning.sdkServiceMu.RLock()
+ defer lightning.sdkServiceMu.RUnlock()
+
+ if lightning.Account() == nil || lightning.sdkService == nil {
+ return nil, errp.New("Lightning not initialized")
+ }
- return nil, err
- }
ensureSynced := false
- info, err := sdkService.GetInfo(breez_sdk_spark.GetInfoRequest{
+ info, err := lightning.sdkService.GetInfo(breez_sdk_spark.GetInfoRequest{And Disconnect() / connect() would use Lock() for exclusive access.
Also applies to: 177-184
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/lightning/lightning.go` around lines 139 - 147, activeSDK() returns
the sdkService pointer after releasing the mutex, creating a race where
Disconnect() can call Destroy() and nil out sdkService while the caller still
uses it; change sdkServiceMu from sync.Mutex to sync.RWMutex and replace
activeSDK() with a helper that acquires an RLock for the duration of the SDK
operation (either expose a new withActiveSDK(fn(*breez_sdk_spark.BreezSdk)
error) that holds RLock/RUnlock around calling fn or require callers to take
RLock/RUnlock themselves), update Balance(), other callers, connect() and
Disconnect() to use RLock for reads and Lock for mutations, and ensure
Disconnect() calls Destroy() while holding the exclusive Lock to prevent
use-after-destroy of sdkService.
Guard Breez SDK service reads and reconnects with a mutex, and route payment operations through the active SDK helper so disconnects cannot race direct service access.
e57e87a to
a4fc12d
Compare
Guard Breez SDK service reads and reconnects with a mutex, and route payment operations through the active SDK helper so disconnects cannot race direct service access.
Before asking for reviews, here is a check list of the most common things you might need to consider: