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 }