1/*
2 * Written by Doug Lea with assistance from members of JCP JSR-166
3 * Expert Group and released to the public domain, as explained at
4 * http://creativecommons.org/publicdomain/zero/1.0/
5 */
6
7/*
8 * Source:
9 * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/extra/AtomicDoubleArray.java?revision=1.5
10 * (Modified to adapt to guava coding conventions and
11 * to use AtomicLongArray instead of sun.misc.Unsafe)
12 */
13
14package com.google.common.util.concurrent;
15
16import com.google.common.annotations.Beta;
17
18import static java.lang.Double.doubleToRawLongBits;
19import static java.lang.Double.longBitsToDouble;
20import java.util.concurrent.atomic.AtomicLongArray;
21
22/**
23 * A {@code double} array in which elements may be updated atomically.
24 * See the {@link java.util.concurrent.atomic} package specification
25 * for description of the properties of atomic variables.
26 *
27 * <p><a name="bitEquals">This class compares primitive {@code double}
28 * values in methods such as {@link #compareAndSet} by comparing their
29 * bitwise representation using {@link Double#doubleToRawLongBits},
30 * which differs from both the primitive double {@code ==} operator
31 * and from {@link Double#equals}, as if implemented by:
32 *  <pre> {@code
33 * static boolean bitEquals(double x, double y) {
34 *   long xBits = Double.doubleToRawLongBits(x);
35 *   long yBits = Double.doubleToRawLongBits(y);
36 *   return xBits == yBits;
37 * }}</pre>
38 *
39 * @author Doug Lea
40 * @author Martin Buchholz
41 * @since 11.0
42 */
43@Beta
44public class AtomicDoubleArray implements java.io.Serializable {
45  private static final long serialVersionUID = 0L;
46
47  // Making this non-final is the lesser evil according to Effective
48  // Java 2nd Edition Item 76: Write readObject methods defensively.
49  private transient AtomicLongArray longs;
50
51  /**
52   * Creates a new {@code AtomicDoubleArray} of the given length,
53   * with all elements initially zero.
54   *
55   * @param length the length of the array
56   */
57  public AtomicDoubleArray(int length) {
58    this.longs = new AtomicLongArray(length);
59  }
60
61  /**
62   * Creates a new {@code AtomicDoubleArray} with the same length
63   * as, and all elements copied from, the given array.
64   *
65   * @param array the array to copy elements from
66   * @throws NullPointerException if array is null
67   */
68  public AtomicDoubleArray(double[] array) {
69    final int len = array.length;
70    long[] longArray = new long[len];
71    for (int i = 0; i < len; i++) {
72      longArray[i] = doubleToRawLongBits(array[i]);
73    }
74    this.longs = new AtomicLongArray(longArray);
75  }
76
77  /**
78   * Returns the length of the array.
79   *
80   * @return the length of the array
81   */
82  public final int length() {
83    return longs.length();
84  }
85
86  /**
87   * Gets the current value at position {@code i}.
88   *
89   * @param i the index
90   * @return the current value
91   */
92  public final double get(int i) {
93    return longBitsToDouble(longs.get(i));
94  }
95
96  /**
97   * Sets the element at position {@code i} to the given value.
98   *
99   * @param i the index
100   * @param newValue the new value
101   */
102  public final void set(int i, double newValue) {
103    long next = doubleToRawLongBits(newValue);
104    longs.set(i, next);
105  }
106
107  /**
108   * Eventually sets the element at position {@code i} to the given value.
109   *
110   * @param i the index
111   * @param newValue the new value
112   */
113  public final void lazySet(int i, double newValue) {
114    set(i, newValue);
115    // TODO(user): replace with code below when jdk5 support is dropped.
116    // long next = doubleToRawLongBits(newValue);
117    // longs.lazySet(i, next);
118  }
119
120  /**
121   * Atomically sets the element at position {@code i} to the given value
122   * and returns the old value.
123   *
124   * @param i the index
125   * @param newValue the new value
126   * @return the previous value
127   */
128  public final double getAndSet(int i, double newValue) {
129    long next = doubleToRawLongBits(newValue);
130    return longBitsToDouble(longs.getAndSet(i, next));
131  }
132
133  /**
134   * Atomically sets the element at position {@code i} to the given
135   * updated value
136   * if the current value is <a href="#bitEquals">bitwise equal</a>
137   * to the expected value.
138   *
139   * @param i the index
140   * @param expect the expected value
141   * @param update the new value
142   * @return true if successful. False return indicates that
143   * the actual value was not equal to the expected value.
144   */
145  public final boolean compareAndSet(int i, double expect, double update) {
146    return longs.compareAndSet(i,
147                               doubleToRawLongBits(expect),
148                               doubleToRawLongBits(update));
149  }
150
151  /**
152   * Atomically sets the element at position {@code i} to the given
153   * updated value
154   * if the current value is <a href="#bitEquals">bitwise equal</a>
155   * to the expected value.
156   *
157   * <p>May <a
158   * href="http://download.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/package-summary.html#Spurious">
159   * fail spuriously</a>
160   * and does not provide ordering guarantees, so is only rarely an
161   * appropriate alternative to {@code compareAndSet}.
162   *
163   * @param i the index
164   * @param expect the expected value
165   * @param update the new value
166   * @return true if successful
167   */
168  public final boolean weakCompareAndSet(int i, double expect, double update) {
169    return longs.weakCompareAndSet(i,
170                                   doubleToRawLongBits(expect),
171                                   doubleToRawLongBits(update));
172  }
173
174  /**
175   * Atomically adds the given value to the element at index {@code i}.
176   *
177   * @param i the index
178   * @param delta the value to add
179   * @return the previous value
180   */
181  public final double getAndAdd(int i, double delta) {
182    while (true) {
183      long current = longs.get(i);
184      double currentVal = longBitsToDouble(current);
185      double nextVal = currentVal + delta;
186      long next = doubleToRawLongBits(nextVal);
187      if (longs.compareAndSet(i, current, next)) {
188        return currentVal;
189      }
190    }
191  }
192
193  /**
194   * Atomically adds the given value to the element at index {@code i}.
195   *
196   * @param i the index
197   * @param delta the value to add
198   * @return the updated value
199   */
200  public double addAndGet(int i, double delta) {
201    while (true) {
202      long current = longs.get(i);
203      double currentVal = longBitsToDouble(current);
204      double nextVal = currentVal + delta;
205      long next = doubleToRawLongBits(nextVal);
206      if (longs.compareAndSet(i, current, next)) {
207        return nextVal;
208      }
209    }
210  }
211
212  /**
213   * Returns the String representation of the current values of array.
214   * @return the String representation of the current values of array
215   */
216  public String toString() {
217    int iMax = length() - 1;
218    if (iMax == -1) {
219      return "[]";
220    }
221
222    // Double.toString(Math.PI).length() == 17
223    StringBuilder b = new StringBuilder((17 + 2) * (iMax + 1));
224    b.append('[');
225    for (int i = 0;; i++) {
226      b.append(longBitsToDouble(longs.get(i)));
227      if (i == iMax) {
228        return b.append(']').toString();
229      }
230      b.append(',').append(' ');
231    }
232  }
233
234  /**
235   * Saves the state to a stream (that is, serializes it).
236   *
237   * @serialData The length of the array is emitted (int), followed by all
238   *             of its elements (each a {@code double}) in the proper order.
239   */
240  private void writeObject(java.io.ObjectOutputStream s)
241      throws java.io.IOException {
242    s.defaultWriteObject();
243
244    // Write out array length
245    int length = length();
246    s.writeInt(length);
247
248    // Write out all elements in the proper order.
249    for (int i = 0; i < length; i++) {
250      s.writeDouble(get(i));
251    }
252  }
253
254  /**
255   * Reconstitutes the instance from a stream (that is, deserializes it).
256   */
257  private void readObject(java.io.ObjectInputStream s)
258      throws java.io.IOException, ClassNotFoundException {
259    s.defaultReadObject();
260
261    // Read in array length and allocate array
262    int length = s.readInt();
263    this.longs = new AtomicLongArray(length);
264
265    // Read in all elements in the proper order.
266    for (int i = 0; i < length; i++) {
267      set(i, s.readDouble());
268    }
269  }
270}
271