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);