Pull request 2048: AG-26594-fix-filtering-race
Squashed commit of the following: commit 9b5b035aa3edfe20cbc26772b8a5c76d81288116 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Fri Oct 20 13:00:29 2023 +0300 filtering: imp code commit 406f4015d80d8b11fbd0aeacfabe686931bbe3fb Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Thu Oct 19 15:04:13 2023 +0300 filtering: fix race
This commit is contained in:
parent
1d1de1bfb5
commit
cd09ba63b6
|
@ -263,30 +263,6 @@ func assignUniqueFilterID() int64 {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets up a timer that will be checking for filters updates periodically
|
|
||||||
func (d *DNSFilter) periodicallyRefreshFilters() {
|
|
||||||
const maxInterval = 1 * 60 * 60
|
|
||||||
ivl := 5 // use a dynamically increasing time interval
|
|
||||||
for {
|
|
||||||
isNetErr, ok := false, false
|
|
||||||
if d.conf.FiltersUpdateIntervalHours != 0 {
|
|
||||||
_, isNetErr, ok = d.tryRefreshFilters(true, true, false)
|
|
||||||
if ok && !isNetErr {
|
|
||||||
ivl = maxInterval
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isNetErr {
|
|
||||||
ivl *= 2
|
|
||||||
if ivl > maxInterval {
|
|
||||||
ivl = maxInterval
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(time.Duration(ivl) * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// tryRefreshFilters is like [refreshFilters], but backs down if the update is
|
// tryRefreshFilters is like [refreshFilters], but backs down if the update is
|
||||||
// already going on.
|
// already going on.
|
||||||
//
|
//
|
||||||
|
|
|
@ -257,6 +257,9 @@ type DNSFilter struct {
|
||||||
// conf contains filtering parameters.
|
// conf contains filtering parameters.
|
||||||
conf *Config
|
conf *Config
|
||||||
|
|
||||||
|
// done is the channel to signal to stop running filters updates loop.
|
||||||
|
done chan struct{}
|
||||||
|
|
||||||
// Channel for passing data to filters-initializer goroutine
|
// Channel for passing data to filters-initializer goroutine
|
||||||
filtersInitializerChan chan filtersInitializerParams
|
filtersInitializerChan chan filtersInitializerParams
|
||||||
filtersInitializerLock sync.Mutex
|
filtersInitializerLock sync.Mutex
|
||||||
|
@ -424,24 +427,15 @@ func (d *DNSFilter) setFilters(blockFilters, allowFilters []Filter, async bool)
|
||||||
return d.initFiltering(allowFilters, blockFilters)
|
return d.initFiltering(allowFilters, blockFilters)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Starts initializing new filters by signal from channel
|
|
||||||
func (d *DNSFilter) filtersInitializer() {
|
|
||||||
for {
|
|
||||||
params := <-d.filtersInitializerChan
|
|
||||||
err := d.initFiltering(params.allowFilters, params.blockFilters)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("filtering: initializing: %s", err)
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close - close the object
|
// Close - close the object
|
||||||
func (d *DNSFilter) Close() {
|
func (d *DNSFilter) Close() {
|
||||||
d.engineLock.Lock()
|
d.engineLock.Lock()
|
||||||
defer d.engineLock.Unlock()
|
defer d.engineLock.Unlock()
|
||||||
|
|
||||||
|
if d.done != nil {
|
||||||
|
d.done <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
d.reset()
|
d.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1131,19 +1125,64 @@ func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
|
||||||
return d, nil
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start - start the module:
|
// Start registers web handlers and starts filters updates loop.
|
||||||
// . start async filtering initializer goroutine
|
|
||||||
// . register web handlers
|
|
||||||
func (d *DNSFilter) Start() {
|
func (d *DNSFilter) Start() {
|
||||||
d.filtersInitializerChan = make(chan filtersInitializerParams, 1)
|
d.filtersInitializerChan = make(chan filtersInitializerParams, 1)
|
||||||
go d.filtersInitializer()
|
d.done = make(chan struct{}, 1)
|
||||||
|
|
||||||
d.RegisterFilteringHandlers()
|
d.RegisterFilteringHandlers()
|
||||||
|
|
||||||
// Here we should start updating filters,
|
go d.updatesLoop()
|
||||||
// but currently we can't wake up the periodic task to do so.
|
}
|
||||||
// So for now we just start this periodic task from here.
|
|
||||||
go d.periodicallyRefreshFilters()
|
// updatesLoop initializes new filters and checks for filters updates in a loop.
|
||||||
|
func (d *DNSFilter) updatesLoop() {
|
||||||
|
defer log.OnPanic("filtering: updates loop")
|
||||||
|
|
||||||
|
ivl := time.Second * 5
|
||||||
|
t := time.NewTimer(ivl)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case params := <-d.filtersInitializerChan:
|
||||||
|
err := d.initFiltering(params.allowFilters, params.blockFilters)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("filtering: initializing: %s", err)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case <-t.C:
|
||||||
|
ivl = d.periodicallyRefreshFilters(ivl)
|
||||||
|
t.Reset(ivl)
|
||||||
|
case <-d.done:
|
||||||
|
t.Stop()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// periodicallyRefreshFilters checks for filters updates and returns time
|
||||||
|
// interval for the next update.
|
||||||
|
func (d *DNSFilter) periodicallyRefreshFilters(ivl time.Duration) (nextIvl time.Duration) {
|
||||||
|
const maxInterval = time.Hour
|
||||||
|
|
||||||
|
if d.conf.FiltersUpdateIntervalHours == 0 {
|
||||||
|
return ivl
|
||||||
|
}
|
||||||
|
|
||||||
|
isNetErr, ok := false, false
|
||||||
|
_, isNetErr, ok = d.tryRefreshFilters(true, true, false)
|
||||||
|
|
||||||
|
if ok && !isNetErr {
|
||||||
|
ivl = maxInterval
|
||||||
|
} else if isNetErr {
|
||||||
|
ivl *= 2
|
||||||
|
// TODO(s.chzhen): Use built-in function max in Go 1.21.
|
||||||
|
ivl = mathutil.Max(ivl, maxInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ivl
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe browsing and parental control methods.
|
// Safe browsing and parental control methods.
|
||||||
|
|
Loading…
Reference in New Issue