From 24a2a4a4eac25f242cefb424b2dba586fe7b8567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A1chym=20Barv=C3=ADnek?= Date: Fri, 23 Oct 2026 11:02:37 +0200 Subject: [PATCH] Retry atomic_add_double with the value atom_cmpxchg returns The compare-and-swap loops of atomic_add_double and local_atomic_add_double re-read the cell with a plain double load, which races with atom_cmpxchg and on which the loop's termination depends. Read the cell once with an atomic and retry with the value atom_cmpxchg returned, so the cell is accessed only through atomics. --- .../device_functions/atomic_add_double.hpp | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/stan/math/opencl/kernels/device_functions/atomic_add_double.hpp b/stan/math/opencl/kernels/device_functions/atomic_add_double.hpp index b07e1246d24..427b82111ee 100644 --- a/stan/math/opencl/kernels/device_functions/atomic_add_double.hpp +++ b/stan/math/opencl/kernels/device_functions/atomic_add_double.hpp @@ -18,13 +18,14 @@ static constexpr const char *atomic_add_double_device_function /** \ingroup opencl_kernels * Atomically add to a double value. * - * Code is taken from: + * Adapted from: * https://stackoverflow.com/questions/31863587/atomic-operations-with-double-opencl * * @param val pointer to value to add to * @param delta value to add */ void atomic_add_double(__global double *val, double delta) { + volatile __global ulong *p = (volatile __global ulong *)val; union { double f; ulong i; @@ -33,23 +34,25 @@ static constexpr const char *atomic_add_double_device_function double f; ulong i; } new_val; + // atomic read: a compare-and-swap that never changes the cell + ulong current = atom_cmpxchg(p, 0UL, 0UL); do { - old_val.f = *val; + old_val.i = current; new_val.f = old_val.f + delta; - } while (atom_cmpxchg((volatile __global ulong *)val, old_val.i, - new_val.i) - != old_val.i); + current = atom_cmpxchg(p, old_val.i, new_val.i); + } while (current != old_val.i); } /** \ingroup opencl_kernels * Atomically add to a local double value. * - * Code is taken from: + * Adapted from: * https://stackoverflow.com/questions/31863587/atomic-operations-with-double-opencl * * @param val pointer to value to add to * @param delta value to add */ void local_atomic_add_double(__local double *val, double delta) { + volatile __local ulong *p = (volatile __local ulong *)val; union { double f; ulong i; @@ -58,12 +61,13 @@ static constexpr const char *atomic_add_double_device_function double f; ulong i; } new_val; + // atomic read: a compare-and-swap that never changes the cell + ulong current = atom_cmpxchg(p, 0UL, 0UL); do { - old_val.f = *val; + old_val.i = current; new_val.f = old_val.f + delta; - } while (atom_cmpxchg((volatile __local ulong *)val, old_val.i, - new_val.i) - != old_val.i); + current = atom_cmpxchg(p, old_val.i, new_val.i); + } while (current != old_val.i); } // \cond ) "\n#endif\n"; // NOLINT