-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MultiWallet struct should be tread safe #252
base: master
Are you sure you want to change the base?
MultiWallet struct should be tread safe #252
Conversation
@itswisdomagain please could you take a look this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will review again after this. As a rule of thumb, if an operation won't take long, don't make a copy of the wallets map or lock and unlock the mutex more than once.
multiwallet.go
Outdated
mw.badWallets[wallet.ID] = wallet | ||
mw.badWalletsUpdate(wallet.ID, wallet) | ||
log.Warnf("Ignored wallet load error for wallet %d (%s)", wallet.ID, wallet.Name) | ||
} else { | ||
mw.wallets[wallet.ID] = wallet | ||
mw.walletsUpdate(wallet.ID, wallet) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's safe to updates the maps directly here without locking the mutex as they won't be accessed concurrently during this initialization. Alternatively, you can lock the mutexes before the for loop and unlock after, rather than locking/unlocking for each insert.
multiwallet.go
Outdated
@@ -155,7 +160,7 @@ func (mw *MultiWallet) Shutdown() { | |||
mw.CancelRescan() | |||
mw.CancelSync() | |||
|
|||
for _, wallet := range mw.wallets { | |||
for _, wallet := range mw.walletsReadCopy() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
During shutdown, don't use a copy of the wallets. Lock the mutexes here and access the mw.wallets
map directly. Basically, any other operation that needs access to the map should wait for the shutting down to complete. Additionally, before unlocking the mutex, clear the map.
multiwallet.go
Outdated
@@ -275,7 +280,7 @@ func (mw *MultiWallet) OpenWallets(startupPassphrase []byte) error { | |||
return err | |||
} | |||
|
|||
for _, wallet := range mw.wallets { | |||
for _, wallet := range mw.walletsReadCopy() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment above applies here. Keep the mutex locked and prevent any other access to the map until the wallets are all opened.
multiwallet.go
Outdated
@@ -429,6 +434,60 @@ func (mw *MultiWallet) LinkExistingWallet(walletName, walletDataDir, originalPub | |||
}) | |||
} | |||
|
|||
// walletsReadCopy is concurrently-safe way to read from mw.wallets map. | |||
func (mw *MultiWallet) walletsReadCopy() map[int]*Wallet { | |||
result := make(map[int]*Wallet, len(mw.wallets)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lock before reading len(mw.wallets)
. Also applies to the badWalletsReadyCopy method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missed this one, good catch
Cleaned up a bit and addressed all the comments above. |
@itswisdomagain pls take a look when you can ^ |
9a4e59d
to
3a5d4ce
Compare
(likely) Resolves #865
This PR adds a mutexes around
MultiWallet.wallets
andMultiWallet.badWallets
maps to prevent races during concurrent access.Haven't tested these changes myself, though.