ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/jsr166e/extra/AtomicDoubleArray.java
Revision: 1.11
Committed: Mon Jul 22 16:47:46 2013 UTC (10 years, 9 months ago) by jsr166
Branch: MAIN
CVS Tags: HEAD
Changes since 1.10: +6 -0 lines
Log Message:
fix javadoc warnings for serialization methods

File Contents

# Content
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 package jsr166e.extra;
8
9 import static java.lang.Double.doubleToRawLongBits;
10 import static java.lang.Double.longBitsToDouble;
11
12 /**
13 * A {@code double} array in which elements may be updated atomically.
14 * See the {@link java.util.concurrent.atomic} package specification
15 * for description of the properties of atomic variables.
16 *
17 * <p id="bitEquals">This class compares primitive {@code double}
18 * values in methods such as {@link #compareAndSet} by comparing their
19 * bitwise representation using {@link Double#doubleToRawLongBits},
20 * which differs from both the primitive double {@code ==} operator
21 * and from {@link Double#equals}, as if implemented by:
22 * <pre> {@code
23 * static boolean bitEquals(double x, double y) {
24 * long xBits = Double.doubleToRawLongBits(x);
25 * long yBits = Double.doubleToRawLongBits(y);
26 * return xBits == yBits;
27 * }}</pre>
28 *
29 * @author Doug Lea
30 * @author Martin Buchholz
31 */
32 public class AtomicDoubleArray implements java.io.Serializable {
33 private static final long serialVersionUID = -2308431214976778248L;
34
35 private final transient long[] array;
36
37 private long checkedByteOffset(int i) {
38 if (i < 0 || i >= array.length)
39 throw new IndexOutOfBoundsException("index " + i);
40
41 return byteOffset(i);
42 }
43
44 private static long byteOffset(int i) {
45 return ((long) i << shift) + base;
46 }
47
48 /**
49 * Creates a new {@code AtomicDoubleArray} of the given length,
50 * with all elements initially zero.
51 *
52 * @param length the length of the array
53 */
54 public AtomicDoubleArray(int length) {
55 array = new long[length];
56 }
57
58 /**
59 * Creates a new {@code AtomicDoubleArray} with the same length
60 * as, and all elements copied from, the given array.
61 *
62 * @param array the array to copy elements from
63 * @throws NullPointerException if array is null
64 */
65 public AtomicDoubleArray(double[] array) {
66 // Visibility guaranteed by final field guarantees
67 final int len = array.length;
68 final long[] a = new long[len];
69 for (int i = 0; i < len; i++)
70 a[i] = doubleToRawLongBits(array[i]);
71 this.array = a;
72 }
73
74 /**
75 * Returns the length of the array.
76 *
77 * @return the length of the array
78 */
79 public final int length() {
80 return array.length;
81 }
82
83 /**
84 * Gets the current value at position {@code i}.
85 *
86 * @param i the index
87 * @return the current value
88 */
89 public final double get(int i) {
90 return longBitsToDouble(getRaw(checkedByteOffset(i)));
91 }
92
93 private long getRaw(long offset) {
94 return unsafe.getLongVolatile(array, offset);
95 }
96
97 /**
98 * Sets the element at position {@code i} to the given value.
99 *
100 * @param i the index
101 * @param newValue the new value
102 */
103 public final void set(int i, double newValue) {
104 long next = doubleToRawLongBits(newValue);
105 unsafe.putLongVolatile(array, checkedByteOffset(i), next);
106 }
107
108 /**
109 * Eventually sets the element at position {@code i} to the given value.
110 *
111 * @param i the index
112 * @param newValue the new value
113 */
114 public final void lazySet(int i, double newValue) {
115 long next = doubleToRawLongBits(newValue);
116 unsafe.putOrderedLong(array, checkedByteOffset(i), next);
117 }
118
119 /**
120 * Atomically sets the element at position {@code i} to the given value
121 * and returns the old value.
122 *
123 * @param i the index
124 * @param newValue the new value
125 * @return the previous value
126 */
127 public final double getAndSet(int i, double newValue) {
128 long next = doubleToRawLongBits(newValue);
129 long offset = checkedByteOffset(i);
130 while (true) {
131 long current = getRaw(offset);
132 if (compareAndSetRaw(offset, current, next))
133 return longBitsToDouble(current);
134 }
135 }
136
137 /**
138 * Atomically sets the element at position {@code i} to the given
139 * updated value
140 * if the current value is <a href="#bitEquals">bitwise equal</a>
141 * to the expected value.
142 *
143 * @param i the index
144 * @param expect the expected value
145 * @param update the new value
146 * @return true if successful. False return indicates that
147 * the actual value was not equal to the expected value.
148 */
149 public final boolean compareAndSet(int i, double expect, double update) {
150 return compareAndSetRaw(checkedByteOffset(i),
151 doubleToRawLongBits(expect),
152 doubleToRawLongBits(update));
153 }
154
155 private boolean compareAndSetRaw(long offset, long expect, long update) {
156 return unsafe.compareAndSwapLong(array, offset, expect, update);
157 }
158
159 /**
160 * Atomically sets the element at position {@code i} to the given
161 * updated value
162 * if the current value is <a href="#bitEquals">bitwise equal</a>
163 * to the expected value.
164 *
165 * <p><a
166 * href="http://download.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/package-summary.html#Spurious">
167 * May fail spuriously and does not provide ordering guarantees</a>,
168 * so is only rarely an appropriate alternative to {@code compareAndSet}.
169 *
170 * @param i the index
171 * @param expect the expected value
172 * @param update the new value
173 * @return true if successful
174 */
175 public final boolean weakCompareAndSet(int i, double expect, double update) {
176 return compareAndSet(i, expect, update);
177 }
178
179 /**
180 * Atomically adds the given value to the element at index {@code i}.
181 *
182 * @param i the index
183 * @param delta the value to add
184 * @return the previous value
185 */
186 public final double getAndAdd(int i, double delta) {
187 long offset = checkedByteOffset(i);
188 while (true) {
189 long current = getRaw(offset);
190 double currentVal = longBitsToDouble(current);
191 double nextVal = currentVal + delta;
192 long next = doubleToRawLongBits(nextVal);
193 if (compareAndSetRaw(offset, current, next))
194 return currentVal;
195 }
196 }
197
198 /**
199 * Atomically adds the given value to the element at index {@code i}.
200 *
201 * @param i the index
202 * @param delta the value to add
203 * @return the updated value
204 */
205 public double addAndGet(int i, double delta) {
206 long offset = checkedByteOffset(i);
207 while (true) {
208 long current = getRaw(offset);
209 double currentVal = longBitsToDouble(current);
210 double nextVal = currentVal + delta;
211 long next = doubleToRawLongBits(nextVal);
212 if (compareAndSetRaw(offset, current, next))
213 return nextVal;
214 }
215 }
216
217 /**
218 * Returns the String representation of the current values of array.
219 * @return the String representation of the current values of array
220 */
221 public String toString() {
222 int iMax = array.length - 1;
223 if (iMax == -1)
224 return "[]";
225
226 // Double.toString(Math.PI).length() == 17
227 StringBuilder b = new StringBuilder((17 + 2) * (iMax + 1));
228 b.append('[');
229 for (int i = 0;; i++) {
230 b.append(longBitsToDouble(getRaw(byteOffset(i))));
231 if (i == iMax)
232 return b.append(']').toString();
233 b.append(',').append(' ');
234 }
235 }
236
237 /**
238 * Saves the state to a stream (that is, serializes it).
239 *
240 * @param s the stream
241 * @throws java.io.IOException if an I/O error occurs
242 * @serialData The length of the array is emitted (int), followed by all
243 * of its elements (each a {@code double}) in the proper order.
244 */
245 private void writeObject(java.io.ObjectOutputStream s)
246 throws java.io.IOException {
247 s.defaultWriteObject();
248
249 // Write out array length
250 int length = length();
251 s.writeInt(length);
252
253 // Write out all elements in the proper order.
254 for (int i = 0; i < length; i++)
255 s.writeDouble(get(i));
256 }
257
258 /**
259 * Reconstitutes the instance from a stream (that is, deserializes it).
260 * @param s the stream
261 * @throws ClassNotFoundException if the class of a serialized object
262 * could not be found
263 * @throws java.io.IOException if an I/O error occurs
264 */
265 private void readObject(java.io.ObjectInputStream s)
266 throws java.io.IOException, ClassNotFoundException {
267 s.defaultReadObject();
268
269 // Read in array length and allocate array
270 int length = s.readInt();
271 unsafe.putObjectVolatile(this, arrayOffset, new long[length]);
272
273 // Read in all elements in the proper order.
274 for (int i = 0; i < length; i++)
275 set(i, s.readDouble());
276 }
277
278 // Unsafe mechanics
279 private static final sun.misc.Unsafe unsafe = getUnsafe();
280 private static final long arrayOffset;
281 private static final int base = unsafe.arrayBaseOffset(long[].class);
282 private static final int shift;
283
284 static {
285 try {
286 Class<?> k = AtomicDoubleArray.class;
287 arrayOffset = unsafe.objectFieldOffset
288 (k.getDeclaredField("array"));
289 int scale = unsafe.arrayIndexScale(long[].class);
290 if ((scale & (scale - 1)) != 0)
291 throw new Error("data type scale not a power of two");
292 shift = 31 - Integer.numberOfLeadingZeros(scale);
293 } catch (Exception e) {
294 throw new Error(e);
295 }
296 }
297
298 /**
299 * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package.
300 * Replace with a simple call to Unsafe.getUnsafe when integrating
301 * into a jdk.
302 *
303 * @return a sun.misc.Unsafe
304 */
305 private static sun.misc.Unsafe getUnsafe() {
306 try {
307 return sun.misc.Unsafe.getUnsafe();
308 } catch (SecurityException tryReflectionInstead) {}
309 try {
310 return java.security.AccessController.doPrivileged
311 (new java.security.PrivilegedExceptionAction<sun.misc.Unsafe>() {
312 public sun.misc.Unsafe run() throws Exception {
313 Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
314 for (java.lang.reflect.Field f : k.getDeclaredFields()) {
315 f.setAccessible(true);
316 Object x = f.get(null);
317 if (k.isInstance(x))
318 return k.cast(x);
319 }
320 throw new NoSuchFieldError("the Unsafe");
321 }});
322 } catch (java.security.PrivilegedActionException e) {
323 throw new RuntimeException("Could not initialize intrinsics",
324 e.getCause());
325 }
326 }
327 }