@cryptotaxi247 / netdata / commits / 0c3e3da85

Improve ML chart access (#22530)

* Ensure atomic operations for safe access and updates to `ml_chart` fields in ML code path. * Simplify `__atomic_store_n` calls by removing unnecessary type casting

Stelios Fragkakis committed May 23, 2026 at 15:24 UTC 0c3e3da85efef8199295fa06976fff6fac325493
2 files changed +24 -10
src/ml/ml.cc
+1 -1
@@ -1080,7 +1080,7 @@ ml_host_detect_once(ml_host_t *host, ONEWAYALLOC *owa)
1080 rrdset_foreach_read(rsp, host->rh) {
1081 RRDSET *rs = static_cast<RRDSET *>(rsp);
1082
1083 - ml_chart_t *chart = (ml_chart_t *) rs->ml_chart;
1083 + ml_chart_t *chart = (ml_chart_t *) __atomic_load_n(&rs->ml_chart, __ATOMIC_ACQUIRE);
1084 if (!chart)
1085 continue;
1086
src/ml/ml_public.cc
+23 -9
@@ -169,7 +169,7 @@ void ml_host_stop(RRDHOST *rh) {
169 rrdset_foreach_read(rsp, host->rh) {
170 RRDSET *rs = static_cast<RRDSET *>(rsp);
171
172 - ml_chart_t *chart = (ml_chart_t *) rs->ml_chart;
172 + ml_chart_t *chart = (ml_chart_t *) __atomic_load_n(&rs->ml_chart, __ATOMIC_ACQUIRE);
173 if (!chart)
174 continue;
175
@@ -308,7 +308,14 @@ void ml_chart_new(RRDSET *rs)
308 chart->rs = rs;
309 chart->mls = ml_machine_learning_stats_t();
310
311 - rs->ml_chart = (rrd_ml_chart_t *) chart;
311 + // Publish with release semantics so readers that load rs->ml_chart with
312 + // acquire semantics observe the chart's `rs` and `mls` fields as fully
313 + // initialized. Without this, the C++ compiler may reorder the plain
314 + // `chart->rs = rs` store after the publish store of rs->ml_chart, and a
315 + // concurrent reader would see chart != NULL with chart->rs still NULL
316 + // (from value-init in `new ml_chart_t()`), producing the SIGSEGV /
317 + // MAPERR / 0x80 fault inside ml_chart_is_available_for_ml.
318 + __atomic_store_n(&rs->ml_chart, (rrd_ml_chart_t *)chart, __ATOMIC_RELEASE);
319 }
320
321 void ml_chart_delete(RRDSET *rs)
@@ -317,15 +324,18 @@ void ml_chart_delete(RRDSET *rs)
324 if (!host)
325 return;
326
320 - ml_chart_t *chart = (ml_chart_t *) rs->ml_chart;
327 + ml_chart_t *chart = (ml_chart_t *) __atomic_load_n(&rs->ml_chart, __ATOMIC_ACQUIRE);
328
329 + // Unpublish BEFORE freeing so a concurrent reader that loads rs->ml_chart
330 + // observes either the live chart (with chart->rs set) or NULL -- never
331 + // the freed chart memory.
332 + __atomic_store_n(&rs->ml_chart, (rrd_ml_chart_t *)NULL, __ATOMIC_RELEASE);
333 delete chart;
323 - rs->ml_chart = NULL;
334 }
335
336 ALWAYS_INLINE_ONLY bool ml_chart_update_begin(RRDSET *rs)
337 {
328 - ml_chart_t *chart = (ml_chart_t *)rs->ml_chart;
338 + ml_chart_t *chart = (ml_chart_t *) __atomic_load_n(&rs->ml_chart, __ATOMIC_ACQUIRE);
339 if (!chart)
340 return false;
341
@@ -335,14 +345,14 @@ ALWAYS_INLINE_ONLY bool ml_chart_update_begin(RRDSET *rs)
345
346 void ml_chart_update_end(RRDSET *rs)
347 {
338 - ml_chart_t *chart = (ml_chart_t *) rs->ml_chart;
348 + ml_chart_t *chart = (ml_chart_t *) __atomic_load_n(&rs->ml_chart, __ATOMIC_ACQUIRE);
349 if (!chart)
350 return;
351 }
352
353 void ml_dimension_new(RRDDIM *rd)
354 {
345 - ml_chart_t *chart = (ml_chart_t *) rd->rrdset->ml_chart;
355 + ml_chart_t *chart = (ml_chart_t *) __atomic_load_n(&rd->rrdset->ml_chart, __ATOMIC_ACQUIRE);
356 if (!chart)
357 return;
358
@@ -425,7 +435,9 @@ ALWAYS_INLINE_ONLY void ml_dimension_received_anomaly(RRDDIM *rd, bool is_anomal
435 if (!host->ml_running)
436 return;
437
428 - ml_chart_t *chart = (ml_chart_t *) rd->rrdset->ml_chart;
438 + ml_chart_t *chart = (ml_chart_t *) __atomic_load_n(&rd->rrdset->ml_chart, __ATOMIC_ACQUIRE);
439 + if (!chart)
440 + return;
441
442 ml_chart_update_dimension(chart, dim, is_anomalous);
443 }
@@ -442,7 +454,9 @@ bool ml_dimension_is_anomalous(RRDDIM *rd, time_t curr_time, double value, bool
454 if (!host->ml_running)
455 return false;
456
445 - ml_chart_t *chart = (ml_chart_t *) rd->rrdset->ml_chart;
457 + ml_chart_t *chart = (ml_chart_t *) __atomic_load_n(&rd->rrdset->ml_chart, __ATOMIC_ACQUIRE);
458 + if (!chart)
459 + return false;
460
461 bool is_anomalous = ml_dimension_predict(dim, value, exists);
462 ml_chart_update_dimension(chart, dim, is_anomalous);