/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.codehaus.groovy.util; import java.util.Collection; import java.util.LinkedList; public abstract class AbstractConcurrentMapBase { protected static final int MAXIMUM_CAPACITY = 1 << 30; static final int MAX_SEGMENTS = 1 << 16; static final int RETRIES_BEFORE_LOCK = 2; final int segmentMask; final int segmentShift; protected final Segment[] segments; public AbstractConcurrentMapBase(Object segmentInfo) { int sshift = 0; int ssize = 1; while (ssize < 16) { ++sshift; ssize <<= 1; } segmentShift = 32 - sshift; segmentMask = ssize - 1; this.segments = new Segment[ssize]; int c = 512 / ssize; if (c * ssize < 512) ++c; int cap = 1; while (cap < c) cap <<= 1; for (int i = 0; i < this.segments.length; ++i) this.segments[i] = createSegment(segmentInfo, cap); } protected abstract Segment createSegment(Object segmentInfo, int cap); protected static <K> int hash(K key) { int h = System.identityHashCode(key); h += ~(h << 9); h ^= (h >>> 14); h += (h << 4); h ^= (h >>> 10); return h; } public Segment segmentFor(int hash) { return segments[(hash >>> segmentShift) & segmentMask]; } public int fullSize() { int count = 0; for (int i = 0; i < segments.length; i++) { segments[i].lock(); try { for (int j = 0; j < segments[i].table.length; j++) { Object o = segments[i].table [j]; if (o != null) { if (o instanceof Entry) { count++; } else { Object arr [] = (Object[]) o; count += arr.length; } } } } finally { segments[i].unlock(); } } return count; } public int size() { int count = 0; for (int i = 0; i < segments.length; i++) { segments[i].lock(); try { for (int j = 0; j < segments[i].table.length; j++) { Object o = segments[i].table [j]; if (o != null) { if (o instanceof Entry) { Entry e = (Entry) o; if (e.isValid()) count++; } else { Object arr [] = (Object[]) o; for (int k = 0; k < arr.length; k++) { Entry info = (Entry) arr[k]; if (info != null && info.isValid()) count++; } } } } } finally { segments[i].unlock(); } } return count; } public Collection values() { Collection result = new LinkedList(); for (int i = 0; i < segments.length; i++) { segments[i].lock(); try { for (int j = 0; j < segments[i].table.length; j++) { Object o = segments[i].table [j]; if (o != null) { if (o instanceof Entry) { Entry e = (Entry) o; if (e.isValid()) result.add(e); } else { Object arr [] = (Object[]) o; for (int k = 0; k < arr.length; k++) { Entry info = (Entry) arr[k]; if (info != null && info.isValid()) result.add(info); } } } } } finally { segments[i].unlock(); } } return result; } public static class Segment extends LockableObject { volatile int count; int threshold; protected volatile Object[] table; protected Segment(int initialCapacity) { setTable(new Object[initialCapacity]); } void setTable(Object[] newTable) { threshold = (int) (newTable.length * 0.75f); table = newTable; } void removeEntry (Entry e) { lock (); int newCount = count; try { Object [] tab = table; int index = e.getHash() & (tab.length-1); Object o = tab[index]; if (o != null) { if (o instanceof Entry) { if (o == e) { tab [index] = null; newCount--; } } else { Object arr [] = (Object[]) o; Object res = null; for (int i = 0; i < arr.length; i++) { Entry info = (Entry) arr[i]; if (info != null) { if(info != e) { if (info.isValid()) { res = put(info, res); } else { newCount--; } } else { newCount--; } } } tab [index] = res; } count = newCount; } } finally { unlock(); } } void rehashIfThresholdExceeded() { if(count > threshold) { rehash(); } } void rehash() { Object[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity >= MAXIMUM_CAPACITY) return; int newCount = 0; for (int i = 0; i < oldCapacity ; i++) { Object o = oldTable [i]; if (o != null) { if (o instanceof Entry) { Entry e = (Entry) o; if (e.isValid()) { newCount++; } else { oldTable[i] = null; } } else { Object arr [] = (Object[]) o; int localCount = 0; for (int index = 0; index < arr.length; index++) { Entry e = (Entry) arr[index]; if (e != null && e.isValid()) { localCount++; } else { arr [index] = null; } } if (localCount == 0) oldTable[i] = null; else newCount += localCount; } } } Object[] newTable = new Object[newCount+1 < threshold ? oldCapacity : oldCapacity << 1]; int sizeMask = newTable.length - 1; newCount = 0; for (int i = 0; i < oldCapacity ; i++) { Object o = oldTable[i]; if (o != null) { if (o instanceof Entry) { Entry e = (Entry) o; if (e.isValid()) { int index = e.getHash() & sizeMask; put(e, index, newTable); newCount++; } } else { Object arr [] = (Object[]) o; for (int j = 0; j < arr.length; j++) { Entry e = (Entry) arr[j]; if (e != null && e.isValid()) { int index = e.getHash() & sizeMask; put(e, index, newTable); newCount++; } } } } } threshold = (int)(newTable.length * 0.75f); table = newTable; count = newCount; } private static void put(Entry ee, int index, Object[] tab) { Object o = tab[index]; if (o != null) { if (o instanceof Entry) { Object arr [] = new Object [2]; arr [0] = ee; arr [1] = (Entry) o; tab[index] = arr; return; } else { Object arr [] = (Object[]) o; Object newArr [] = new Object[arr.length+1]; newArr [0] = ee; System.arraycopy(arr, 0, newArr, 1, arr.length); tab [index] = newArr; return; } } tab[index] = ee; } private static Object put(Entry ee, Object o) { if (o != null) { if (o instanceof Entry) { Object arr [] = new Object [2]; arr [0] = ee; arr [1] = (Entry) o; return arr; } else { Object arr [] = (Object[]) o; Object newArr [] = new Object[arr.length+1]; newArr [0] = ee; System.arraycopy(arr, 0, newArr, 1, arr.length); return newArr; } } return ee; } } public interface Entry<V> { V getValue(); void setValue(V value); int getHash(); boolean isValid(); } }