1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.jexl3;
19
20 import java.util.AbstractMap;
21 import java.util.LinkedHashSet;
22 import java.util.Map;
23 import java.util.Set;
24
25 import org.apache.commons.jexl3.internal.SoftCache;
26
27 /**
28 * Creates a cache using an array of synchronized LinkedHashMap as backing store to spread contention.
29 * <p>Just meant as a contention reducing mechanism for cache tests.</p>
30 *
31 * @param <K> the cached element"s key
32 * @param <V> the cached element"s value
33 * @return the cache instance
34 */
35 public class SpreadCache<K, V> extends SoftCache<K, V> {
36
37 /**
38 * Creates a new instance of a Spread cache.
39 *
40 * @param theSize the cache size
41 */
42 public SpreadCache(final int theSize) {
43 super(theSize);
44 }
45
46 @Override
47 public <K, V> Map<K, V> createMap(final int cacheSize) {
48 return new SpreadMap<>(cacheSize);
49 }
50 }
51
52 /**
53 * A map backed by an array of LinkedHashMap.
54 * <p>This implementation is really tailored to serve the cache methods and no-other. It foregoes being efficient
55 * for methods that do not serve this purpose.</p>
56 * <p>We spread the map capacity over a number of synchronized sub-maps, the number being the number of
57 * available processors. This is meant to spread the contention at the cost of a relaxed LRU.</p>
58 *
59 * @param <K>
60 * @param <V>
61 */
62 class SpreadMap<K, V> extends AbstractMap<K, V> {
63 /**
64 * Returns a power of two for the given target capacity.
65 *
66 * @param cap capacity
67 * @return the smallest power of 2 greater or equal to argument
68 */
69 private static int closestPowerOf2(final int cap) {
70 return cap > 1 ? Integer.highestOneBit(cap - 1) << 1 : 1;
71 }
72
73 /**
74 * The sub-maps array.
75 */
76 private final Map<K, V>[] maps;
77
78 /**
79 * The unique simple constructor.
80 *
81 * @param capacity the overall map capacity
82 */
83 SpreadMap(final int capacity) {
84 final int spread = closestPowerOf2(Runtime.getRuntime().availableProcessors());
85 maps = new Map[spread];
86 final int mapCapacity = (capacity + spread + 1) / spread;
87 for (int m = 0; m < spread; ++m) {
88 maps[m] = SoftCache.createSynchronizedLinkedHashMap(mapCapacity);
89 }
90 }
91
92 @Override
93 public void clear() {
94 for (final Map<K, V> map : maps) {
95 map.clear();
96 }
97 }
98
99 @Override
100 public Set<Entry<K, V>> entrySet() {
101 final Set<Map.Entry<K, V>> entries = new LinkedHashSet<>(size());
102 for (final Map<K, V> map : maps) {
103 synchronized (map) {
104 entries.addAll(map.entrySet());
105 }
106 }
107 return entries;
108 }
109
110 @Override
111 public V get(final Object key) {
112 return getMap(key).get(key);
113 }
114
115 /**
116 * Gets the map storing a given key.
117 *
118 * @param key the key
119 * @return the map
120 */
121 private final Map<K, V> getMap(final Object key) {
122 int h = key.hashCode();
123 h ^= h >>> 16;
124 // length is a power of 2, length - 1 is the mask of its modulo:
125 // length = 4, length - 1 = 3 = 11b : x % 4 <=> x & 3
126 return maps[h & maps.length - 1];
127 }
128
129 @Override
130 public V put(final K key, final V value) {
131 return getMap(key).put(key, value);
132 }
133
134 @Override
135 public V remove(final Object key) {
136 return getMap(key).remove(key);
137 }
138
139 @Override
140 public int size() {
141 int size = 0;
142 for (final Map<K, V> map : maps) {
143 size += map.size();
144 }
145 return size;
146 }
147 }