@@ -94,8 +94,37 @@ public void add(double x) {
9494 * @return the sum
9595 */
9696 public double sum () {
97- Cell [] as = cells ;
98- double sum = Double .longBitsToDouble (base );
97+ // On concurrent `sum` and `set`, it is acceptable to `get` an outdated `value`.
98+ // On concurrent `sum` and `add`, it is acceptable to `get` an outdated `value`.
99+ // On concurrent `sum` and `set` and `add`, it is possible to `get` an outdated `value`.
100+
101+ // Correctness is guaranteed by `volatile` memory access ordering and visibility semantics.
102+ // Program order:
103+ // - writes in `set` - `busy` (CAS), `cells` (Wc), `base` (Wb), `busy`
104+ // - reads in `sum` - `cells` (Rc), `base` (Rb), `busy`, `cells` (Cc), `base` (Cb)
105+ // Note that:
106+ // - `busy` is written after `cells` and `base`
107+ // - `busy` is read after `cells` and `base`, then `cells` and `base` is re-read after `busy`
108+ // In other words:
109+ // - if we see the write to `busy`, then we must see the write to `cells` and `busy` on re-read
110+ // - if we don't see the write to `busy`, then we must retry as we have no guarantees
111+ // Execution order (in the former case):
112+ // - serial
113+ // - old result - Rc, Rb, Cc, Cb, Wc, Wb
114+ // - new result - Wc, Wb, Rc, Rb, Cc, Cb
115+ // - concurrent
116+ // - old result - Rc, Wc, Rb, Wb, Cc, Cb - retry (superfluous)
117+ // - new result - Wc, Rc, Wb, Rb, Cc, Cb
118+ // - invalid result - Rc, Wc, Wb, Rb, Cc, Cb - retry
119+ // - invalid result - Wc, Rc, Rb, Wb, Cc, Cb - retry
120+ Cell [] as = cells ; long b = base ;
121+ while (as != null && !(busy == 0 && cells == as && base == b )) {
122+ // busy waiting, retry loop
123+ Thread .yield ();
124+ as = cells ; b = base ;
125+ }
126+
127+ double sum = Double .longBitsToDouble (b );
99128 if (as != null ) {
100129 int n = as .length ;
101130 for (int i = 0 ; i < n ; ++i ) {
@@ -118,6 +147,41 @@ public void reset() {
118147 internalReset (0L );
119148 }
120149
150+ public void set (double x ) {
151+ // On concurrent `set` and `set`, it should be acceptable to lose one `set` measurement.
152+ // On concurrent `set` and `add`, it should be acceptable to lose the `add` measurement.
153+
154+ // Correctness is ensured by different techniques:
155+ // - `set` waits on contention (blocking)
156+ // - `add` avoids contention (non-blocking)
157+ // - `sum` retries on conflicts (non-blocking)
158+ // Performance characteristics by use cases:
159+ // - only `set` - `cells` is always `null` - no allocations
160+ // - only `add` - `cells` allocated on contention
161+ // - mixed `set` and `add` - `cells` allocated on contention, `cells` deallocated on `set`
162+ for (;;) {
163+ Cell [] as ;
164+ if ((as = cells ) != null ) { // have cells
165+ if (busy == 0 && casBusy ()) {
166+ try {
167+ if (cells == as ) { // recheck under lock
168+ // update cells and base (not atomic)
169+ cells = null ;
170+ base = Double .doubleToLongBits (x );
171+ break ;
172+ }
173+ } finally {
174+ busy = 0 ;
175+ }
176+ }
177+ } else { // no cells
178+ // update base (atomic)
179+ base = Double .doubleToLongBits (x );
180+ break ;
181+ }
182+ }
183+ }
184+
121185 /**
122186 * Equivalent in effect to {@link #sum} followed by {@link
123187 * #reset}. This method may apply for example during quiescent
0 commit comments