1 /**
2 * Copyright 2008 Atlassian Pty Ltd
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.atlassian.util.concurrent.atomic;
18
19 import com.google.common.base.Function;
20 import com.google.common.base.Supplier;
21
22 /**
23 * AtomicReferenceArray with richer functionality. This class implements
24 * commonly implemented patterns of use of compareAndSet such as
25 * {@link #getAndSetIf(int, Object, Object)} and {@link #update(int, Function)}.
26 *
27 * @param E the element type of the array.
28 * @since 0.0.12
29 */
30 public class AtomicReferenceArray<E> extends java.util.concurrent.atomic.AtomicReferenceArray<E> {
31
32 private static final long serialVersionUID = 6669693075971189L;
33
34 //
35 // ctors
36 //
37
38 /**
39 * Creates a new AtomicReferenceArray of given length.
40 *
41 * @param length the length of the array
42 */
43 public AtomicReferenceArray(final int length) {
44 super(length);
45 }
46
47 /**
48 * Creates a new AtomicReferenceArray with the same length as, and all
49 * elements copied from, the given array.
50 *
51 * @param array the array to copy elements from
52 * @throws NullPointerException if array is null
53 */
54 public AtomicReferenceArray(final E[] initialValue) {
55 super(initialValue);
56 }
57
58 //
59 // methods
60 //
61
62 /**
63 * Check the current value and if it matches the old value argument, set it
64 * to the one created by the {@link Supplier new value supplier} and return
65 * that instead. If the old value argument does not match, ignore both and
66 * just return the current value.
67 *
68 * @param <T> the object type.
69 * @param oldValue to check the current value against (reference equality
70 * check only).
71 * @param newValue a {@link Supplier} for a new value. May be called more
72 * than once.
73 * @return the current reference value if it doesn't match old value or a
74 * newly created value.
75 */
76 public final E getOrSetAndGetIf(final int index, final E oldValue, final Supplier<E> newValue) {
77 E result = get(index);
78 // loop until invariant is true in case some other thread resets
79 // reference to oldValue (although if they are doing that then we still
80 // cannot guarantee there will be no ABA problem as they could come
81 // back and set it after we return)
82 while (result == oldValue) {
83 final E update = newValue.get();
84 // abort if trying to set the same value, otherwise infinite loop
85 if (update == oldValue) {
86 return oldValue;
87 }
88 compareAndSet(index, oldValue, update);
89 result = get(index);
90 }
91 return result;
92 }
93
94 /**
95 * Check the current value and if it matches the old value argument, set it
96 * to the new value and return that instead. If the old value argument does
97 * not match, ignore both and just return the current value.
98 *
99 * @param <T> the object type.
100 * @param oldValue to check the current value against (reference equality
101 * check only)
102 * @param newValue the new value to set it to
103 * @return the current reference value if it doesn't match oldValue or a
104 * newly created value.
105 */
106 public final E getOrSetAndGetIf(final int index, final E oldValue, final E newValue) {
107 E result = get(index);
108 // loop until invariant is true in case some other thread resets
109 // reference to oldValue (although if they are doing that then we still
110 // cannot guarantee there will be no ABA problem as they could come
111 // back and set it after we return)
112 while (result == oldValue) {
113 // abort if trying to set the same value, otherwise infinite loop
114 if (result == newValue) {
115 return result;
116 }
117 compareAndSet(index, oldValue, newValue);
118 result = get(index);
119 }
120 return result;
121 }
122
123 /**
124 * Do the actual update. Calls the factory method with the old value to do
125 * the update logic, then sets the value to that if it hasn't changed in the
126 * meantime.
127 *
128 * @return the new updated value.
129 */
130 public final E update(final int index, final Function<E, E> newValueFactory) {
131 E oldValue, newValue;
132 do {
133 oldValue = get(index);
134 newValue = newValueFactory.apply(oldValue);
135 // test first to implement TTAS optimisation
136 // then compare and set
137 } while ((get(index) != oldValue) || !compareAndSet(index, oldValue, newValue));
138 return newValue;
139 }
140 }