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 /**
65 * Returns a power of two for the given target capacity.
66 *
67 * @param cap capacity
68 * @return the smallest power of 2 greater or equal to argument
69 */
70 private static int closestPowerOf2(final int cap) {
71 return cap > 1 ? Integer.highestOneBit(cap - 1) << 1 : 1;
72 }
73
74 /**
75 * The sub-maps array.
76 */
77 private final Map<K, V>[] maps;
78
79 /**
80 * The unique simple constructor.
81 *
82 * @param capacity the overall map capacity
83 */
84 SpreadMap(final int capacity) {
85 final int spread = closestPowerOf2(Runtime.getRuntime().availableProcessors());
86 maps = new Map[spread];
87 final int mapCapacity = (capacity + spread + 1) / spread;
88 for (int m = 0; m < spread; ++m) {
89 maps[m] = SoftCache.createSynchronizedLinkedHashMap(mapCapacity);
90 }
91 }
92
93 @Override
94 public void clear() {
95 for (final Map<K, V> map : maps) {
96 map.clear();
97 }
98 }
99
100 @Override
101 public Set<Entry<K, V>> entrySet() {
102 final Set<Map.Entry<K, V>> entries = new LinkedHashSet<>(size());
103 for (final Map<K, V> map : maps) {
104 synchronized (map) {
105 entries.addAll(map.entrySet());
106 }
107 }
108 return entries;
109 }
110
111 @Override
112 public V get(final Object key) {
113 return getMap(key).get(key);
114 }
115
116 /**
117 * Gets the map storing a given key.
118 *
119 * @param key the key
120 * @return the map
121 */
122 private final Map<K, V> getMap(final Object key) {
123 int h = key.hashCode();
124 h ^= h >>> 16;
125 // length is a power of 2, length - 1 is the mask of its modulo:
126 // length = 4, length - 1 = 3 = 11b : x % 4 <=> x & 3
127 return maps[h & maps.length - 1];
128 }
129
130 @Override
131 public V put(final K key, final V value) {
132 return getMap(key).put(key, value);
133 }
134
135 @Override
136 public V remove(final Object key) {
137 return getMap(key).remove(key);
138 }
139
140 @Override
141 public int size() {
142 int size = 0;
143 for (final Map<K, V> map : maps) {
144 size += map.size();
145 }
146 return size;
147 }
148 }