mirror of
https://github.com/mudler/LocalAI.git
synced 2026-07-30 09:57:57 -04:00
StopP2P() read and wrote a.p2pCtx/a.p2pCancel without holding a.p2pMutex, and StartP2P() reassigned both fields with no lock at all -- including when RestartP2P() calls it from a background goroutine after releasing the mutex. Both paths are reachable from POST /api/settings (empty p2p_token -> StopP2P, non-empty -> RestartP2P), so concurrent requests race on the same fields. Take a.p2pMutex in StopP2P and around the field publication in StartP2P, factor the shared teardown into stopP2PLocked() so RestartP2P reuses it, and route the goroutine error path through StopP2P instead of touching a.p2pCancel unlocked. Signed-off-by: Anai-Guo <antai12232931@anaiguo.com> Co-authored-by: Anai-Guo <antai12232931@anaiguo.com>
This commit is contained in:
@@ -18,16 +18,26 @@ import (
|
||||
)
|
||||
|
||||
func (a *Application) StopP2P() error {
|
||||
if a.p2pCancel != nil {
|
||||
a.p2pCancel()
|
||||
a.p2pCancel = nil
|
||||
a.p2pCtx = nil
|
||||
// Wait a bit for shutdown to complete
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
a.p2pMutex.Lock()
|
||||
defer a.p2pMutex.Unlock()
|
||||
|
||||
a.stopP2PLocked()
|
||||
return nil
|
||||
}
|
||||
|
||||
// stopP2PLocked cancels the running P2P stack, if any, and clears the
|
||||
// context fields. Callers must hold a.p2pMutex.
|
||||
func (a *Application) stopP2PLocked() {
|
||||
if a.p2pCancel == nil {
|
||||
return
|
||||
}
|
||||
a.p2pCancel()
|
||||
a.p2pCancel = nil
|
||||
a.p2pCtx = nil
|
||||
// Wait a bit for shutdown to complete
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
|
||||
func (a *Application) StartP2P() error {
|
||||
// we need a p2p token
|
||||
if a.applicationConfig.P2PToken == "" {
|
||||
@@ -37,8 +47,13 @@ func (a *Application) StartP2P() error {
|
||||
networkID := a.applicationConfig.P2PNetworkID
|
||||
|
||||
ctx, cancel := context.WithCancel(a.ApplicationConfig().Context)
|
||||
// Publishing the context is the only shared-state write here; the node
|
||||
// and discoverer setup below works off the local ctx/cancel, so the lock
|
||||
// is not held across the network calls.
|
||||
a.p2pMutex.Lock()
|
||||
a.p2pCtx = ctx
|
||||
a.p2pCancel = cancel
|
||||
a.p2pMutex.Unlock()
|
||||
|
||||
var n *node.Node
|
||||
// Here we are avoiding creating multiple nodes:
|
||||
@@ -131,13 +146,7 @@ func (a *Application) RestartP2P() error {
|
||||
defer a.p2pMutex.Unlock()
|
||||
|
||||
// Stop existing P2P if running
|
||||
if a.p2pCancel != nil {
|
||||
a.p2pCancel()
|
||||
a.p2pCancel = nil
|
||||
a.p2pCtx = nil
|
||||
// Wait a bit for shutdown to complete
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
a.stopP2PLocked()
|
||||
|
||||
appConfig := a.ApplicationConfig()
|
||||
|
||||
@@ -151,8 +160,8 @@ func (a *Application) RestartP2P() error {
|
||||
go func() {
|
||||
if err := a.StartP2P(); err != nil {
|
||||
xlog.Error("Failed to start P2P stack", "error", err)
|
||||
if a.p2pCancel != nil {
|
||||
a.p2pCancel()
|
||||
if stopErr := a.StopP2P(); stopErr != nil {
|
||||
xlog.Error("Failed to stop partially started P2P stack", "error", stopErr)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
Reference in New Issue
Block a user