@cryptotaxi247 / netdata-1 / commits / b87f24b22

fix race condition on js.timer in scripts.d scheduler (#21801)

Ilya Mashchenko committed Feb 22, 2026 at 22:31 UTC b87f24b2238487d4f397a3a6e3768585f16d7600
1 file changed +9 -3
src/go/plugin/scripts.d/pkg/runtime/scheduler.go
+9 -3
@@ -276,16 +276,20 @@ func (s *Scheduler) Stop() {
276 if s.cancel != nil {
277 s.cancel()
278 }
279 + s.jobMu.Lock()
280 for _, js := range s.jobs {
281 if js.timer != nil {
282 js.timer.Stop()
283 js.timer = nil
284 }
285 }
286 + s.jobMu.Unlock()
287 s.executor.Stop()
288 s.wg.Wait()
289 + s.jobMu.Lock()
290 s.ctx = nil
291 s.cancel = nil
292 + s.jobMu.Unlock()
293 }
294
295 func (s *Scheduler) run() {
@@ -410,7 +414,10 @@ func (s *Scheduler) handleResult(res ExecutionResult) {
414 }
415
416 func (s *Scheduler) armTimer(js *jobState) {
413 - if s.ctx == nil {
417 + s.jobMu.Lock()
418 + ctx := s.ctx
419 + if ctx == nil {
420 + s.jobMu.Unlock()
421 return
422 }
423 delay := time.Until(js.nextRun)
@@ -418,8 +425,6 @@ func (s *Scheduler) armTimer(js *jobState) {
425 delay = 0
426 }
427 jobID := js.runtime.ID
421 - ctx := s.ctx // capture to avoid racing with Stop() nilling s.ctx
422 -
428 if js.timer == nil {
429 js.timer = time.AfterFunc(delay, func() {
430 select {
@@ -430,6 +435,7 @@ func (s *Scheduler) armTimer(js *jobState) {
435 } else {
436 js.timer.Reset(delay)
437 }
438 + s.jobMu.Unlock()
439 }
440
441 func (s *Scheduler) runJob(ctx context.Context, job JobRuntime) ExecutionResult {