001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.modeshape.common.collection.ring; 018 019import static java.util.Arrays.copyOf; 020import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; 021 022/** 023 * Static methods for atomically modifying an array of {@link Pointer} instances. 024 * 025 * @author Randall Hauch (rhauch@redhat.com) 026 */ 027public class Pointers { 028 029 private Pointers() { 030 } 031 032 public static long getMinimum( Pointer[] pointers, 033 long minimum ) { 034 for (int i = 0; i != pointers.length; ++i) { 035 minimum = Math.min(minimum, pointers[i].get()); 036 } 037 return minimum; 038 } 039 040 /** 041 * Atomically add the specified {@link Pointer} instance(s) to an array known to the given updator. 042 * 043 * @param holder the object that holds the array; may not be null 044 * @param updater the updator of the array; may not be null 045 * @param cursor the cursor that the pointers will follow; may be null if none of the pointers are to be changed 046 * @param pointersToAdd the pointer(s) to be added to the existing pointers 047 */ 048 public static <T> void add( T holder, 049 AtomicReferenceFieldUpdater<T, Pointer[]> updater, 050 Cursor cursor, 051 Pointer... pointersToAdd ) { 052 long currentPosition = 0L; 053 Pointer[] updatedPointers; 054 Pointer[] currentPointers; 055 056 do { 057 currentPointers = updater.get(holder); 058 updatedPointers = copyOf(currentPointers, currentPointers.length + pointersToAdd.length); 059 if (cursor != null) currentPosition = cursor.getCurrent(); 060 061 int index = currentPointers.length; 062 for (Pointer pointer : pointersToAdd) { 063 if (cursor != null) pointer.set(currentPosition); 064 updatedPointers[index++] = pointer; 065 } 066 } while (!updater.compareAndSet(holder, currentPointers, updatedPointers)); 067 068 if (cursor != null) { 069 // Set all of the new pointers to the current position ... 070 currentPosition = cursor.getCurrent(); 071 for (Pointer pointer : pointersToAdd) { 072 pointer.set(currentPosition); 073 } 074 } 075 } 076 077 /** 078 * Atomically remove the specified {@link Pointer} instance from an array known to the given updator. 079 * 080 * @param holder the object that holds the array; may not be null 081 * @param updater the updator of the array; may not be null 082 * @param pointer the pointer to be removed from the existing pointers 083 * @return true if the pointer was removed, or false if the array never contained the pointer 084 */ 085 public static <T> boolean remove( T holder, 086 AtomicReferenceFieldUpdater<T, Pointer[]> updater, 087 Pointer pointer ) { 088 int numToRemove; 089 Pointer[] oldPointers; 090 Pointer[] newPointers; 091 092 do { 093 oldPointers = updater.get(holder); 094 numToRemove = countMatching(oldPointers, pointer); 095 if (0 == numToRemove) break; 096 097 final int oldSize = oldPointers.length; 098 newPointers = new Pointer[oldSize - numToRemove]; 099 100 // Copy all but the 'pointer' into the new array ... 101 for (int i = 0, pos = 0; i < oldSize; i++) { 102 final Pointer testPointer = oldPointers[i]; 103 if (pointer != testPointer) { 104 newPointers[pos++] = testPointer; 105 } 106 } 107 } while (!updater.compareAndSet(holder, oldPointers, newPointers)); 108 return numToRemove != 0; 109 } 110 111 private static <T> int countMatching( final T[] values, 112 final T toMatch ) { 113 int numToRemove = 0; 114 for (T value : values) { 115 // Use object identity ... 116 if (value == toMatch) numToRemove++; 117 } 118 return numToRemove; 119 } 120}