Fix WidgetPool goroutine and ticker leak
- Add context cancellation to WidgetPool cleanup goroutine - Properly stop ticker when context is canceled - Add BackgroundContext() getter method to App for context access - Pass application context to NewWidgetPool for lifecycle management This fixes a resource leak where the cleanup goroutine would run indefinitely without any way to stop it, preventing proper cleanup on application shutdown.
This commit is contained in:
@@ -504,6 +504,12 @@ func (a *App) DeleteServerCacheDir(serverID uuid.UUID) error {
|
|||||||
return os.RemoveAll(path)
|
return os.RemoveAll(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BackgroundContext returns the application's background context
|
||||||
|
// which is canceled when the application shuts down.
|
||||||
|
func (a *App) BackgroundContext() context.Context {
|
||||||
|
return a.bgrndCtx
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) Shutdown() {
|
func (a *App) Shutdown() {
|
||||||
if a.logFile != nil {
|
if a.logFile != nil {
|
||||||
a.logFile.Close()
|
a.logFile.Close()
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func NewRouter(app *backend.App, controller *controller.Controller, nav Navigati
|
|||||||
App: app,
|
App: app,
|
||||||
Controller: controller,
|
Controller: controller,
|
||||||
Nav: nav,
|
Nav: nav,
|
||||||
widgetPool: util.NewWidgetPool(),
|
widgetPool: util.NewWidgetPool(app.BackgroundContext()),
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-2
@@ -1,6 +1,7 @@
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ const (
|
|||||||
type WidgetPool struct {
|
type WidgetPool struct {
|
||||||
mut sync.Mutex
|
mut sync.Mutex
|
||||||
pools [][]pooledWidget
|
pools [][]pooledWidget
|
||||||
|
cancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
type pooledWidget struct {
|
type pooledWidget struct {
|
||||||
@@ -40,15 +42,23 @@ type pooledWidget struct {
|
|||||||
releasedAt int64 // unixMillis
|
releasedAt int64 // unixMillis
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWidgetPool() *WidgetPool {
|
func NewWidgetPool(ctx context.Context) *WidgetPool {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
p := &WidgetPool{
|
p := &WidgetPool{
|
||||||
pools: make([][]pooledWidget, numWidgetTypes),
|
pools: make([][]pooledWidget, numWidgetTypes),
|
||||||
|
cancel: cancel,
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
t := time.NewTicker(2 * time.Minute)
|
t := time.NewTicker(2 * time.Minute)
|
||||||
for range t.C {
|
defer t.Stop()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-t.C:
|
||||||
p.cleanUpExpiredItems()
|
p.cleanUpExpiredItems()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user