From 1ee7db6948bc698d70bcd77a486a6764fe501dfb Mon Sep 17 00:00:00 2001 From: Alexandr Stelnykovych Date: Thu, 28 Aug 2025 17:21:17 +0300 Subject: [PATCH 1/2] Unnecessary filterlists database update on service start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/safing/portmaster/issues/1988 --- The filterlists database is often updated when the service starts, even if the latest version is already cached in the database. This does not affect PM’s functionality, but it unnecessarily consumes CPU resources. **Technical explanation:** This happens because the filterlists update check sometimes runs before the module is fully initialized. **Steps to Reproduce:** 1. Start PM and ensure the filterlists are up to date. 2. Fully stop PM. 3. Start PM again. **Observed result:** PM reprocesses the filterlists cache database. **Expected result:** PM should update only when new data is available. Already processed data should not be reprocessed. --- service/intel/filterlists/database.go | 8 ------- service/intel/filterlists/module.go | 30 +++++++++++++++------------ 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/service/intel/filterlists/database.go b/service/intel/filterlists/database.go index 296ac347..f3dc6c95 100644 --- a/service/intel/filterlists/database.go +++ b/service/intel/filterlists/database.go @@ -118,14 +118,6 @@ func processListFile(ctx context.Context, filter *scopedBloom, file *updates.Art return }) - // Wait for the module initialization to complete to ensure the filter is fully loaded. - // This avoids prolonged locks during filter updates caused by concurrent database loading. - select { - case <-moduleInitDone: - case <-time.After(time.Second * 20): - log.Warning("intel/filterlists: timeout waiting for module initialization") - } - // Process each entry and send records to records channel. startSafe(func() error { defer close(records) diff --git a/service/intel/filterlists/module.go b/service/intel/filterlists/module.go index f2780e5f..6393bc24 100644 --- a/service/intel/filterlists/module.go +++ b/service/intel/filterlists/module.go @@ -35,9 +35,6 @@ func (fl *FilterLists) States() *mgr.StateMgr { } func (fl *FilterLists) Start() error { - if err := prep(); err != nil { - return err - } return start() } @@ -46,26 +43,21 @@ func (fl *FilterLists) Stop() error { } var ( - moduleInitDone chan struct{} - // booleans mainly used to decouple the module during testing. ignoreUpdateEvents = abool.New() ignoreNetEnvEvents = abool.New() ) func init() { - moduleInitDone = make(chan struct{}) - ignoreNetEnvEvents.Set() } -func prep() error { +func registerEventCallbacks() error { module.instance.IntelUpdates().EventResourcesUpdated.AddCallback("Check for blocklist updates", func(wc *mgr.WorkerCtx, s struct{}) (bool, error) { if ignoreUpdateEvents.IsSet() { return false, nil } - log.Debugf("performing filter list update") return false, tryListUpdate(wc.Ctx()) }) @@ -90,9 +82,22 @@ func start() error { filterListLock.Lock() defer filterListLock.Unlock() - // Signal that the module has been initialized. - // This indicates that the module is ready for use, with the default filter - defer close(moduleInitDone) + // Any call of tryListUpdate() must be only after module fully initialized + defer func() { + // Register event callbacks + if err := registerEventCallbacks(); err != nil { + log.Errorf("intel/filterlists: failed to register callbacks: %q", err.Error()) + } + + // Initial check filterlists updates + module.Manager().Go("intel/filterlists inital check for update", func(ctx *mgr.WorkerCtx) error { + if err := tryListUpdate(ctx.Ctx()); err != nil { + log.Errorf("intel/filterlists: tryListUpdate() failed: %q", err.Error()) + return err + } + return nil + }) + }() ver, err := getCacheDatabaseVersion() if err == nil { @@ -115,7 +120,6 @@ func start() error { } func stop() error { - moduleInitDone = make(chan struct{}) filterListsLoaded = make(chan struct{}) return nil } From 73ce148ad651ac12e5cdc1cb64cc91e6fc4a5721 Mon Sep 17 00:00:00 2001 From: Alexandr Stelnykovych Date: Wed, 3 Sep 2025 12:55:58 +0300 Subject: [PATCH 2/2] Refactor registerEventCallbacks to remove error handling and simplify callback registration --- service/intel/filterlists/module.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/service/intel/filterlists/module.go b/service/intel/filterlists/module.go index 6393bc24..7cc337c5 100644 --- a/service/intel/filterlists/module.go +++ b/service/intel/filterlists/module.go @@ -52,7 +52,7 @@ func init() { ignoreNetEnvEvents.Set() } -func registerEventCallbacks() error { +func registerEventCallbacks() { module.instance.IntelUpdates().EventResourcesUpdated.AddCallback("Check for blocklist updates", func(wc *mgr.WorkerCtx, s struct{}) (bool, error) { if ignoreUpdateEvents.IsSet() { @@ -74,8 +74,6 @@ func registerEventCallbacks() error { return false, tryListUpdate(wc.Ctx()) }) - - return nil } func start() error { @@ -85,12 +83,10 @@ func start() error { // Any call of tryListUpdate() must be only after module fully initialized defer func() { // Register event callbacks - if err := registerEventCallbacks(); err != nil { - log.Errorf("intel/filterlists: failed to register callbacks: %q", err.Error()) - } + registerEventCallbacks() // Initial check filterlists updates - module.Manager().Go("intel/filterlists inital check for update", func(ctx *mgr.WorkerCtx) error { + module.Manager().Go("intel/filterlists initial check for update", func(ctx *mgr.WorkerCtx) error { if err := tryListUpdate(ctx.Ctx()); err != nil { log.Errorf("intel/filterlists: tryListUpdate() failed: %q", err.Error()) return err