Skip to content

Commit efb024b

Browse files
Christoph SuhreneagleMcMoeBuZZ-dEE
committed
BATIK-1271: makes AWTGlyphGeometryCache thread-safe by using ConcurrentHashMap
Co-authored-by: Michael Möller <michael.moeller@cewe.de> Co-authored-by: Sebastian Schlatow <sebastian.schlatow@cewe.de>
1 parent dc3f248 commit efb024b

1 file changed

Lines changed: 14 additions & 156 deletions

File tree

batik-gvt/src/main/java/org/apache/batik/gvt/font/AWTGlyphGeometryCache.java

Lines changed: 14 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@ Licensed to the Apache Software Foundation (ASF) under one or more
2020

2121
import java.awt.Shape;
2222
import java.awt.geom.Rectangle2D;
23-
import java.lang.ref.ReferenceQueue;
2423
import java.lang.ref.SoftReference;
24+
import java.util.Optional;
25+
import java.util.concurrent.ConcurrentHashMap;
2526

2627
/**
27-
* This class represents a doubly indexed hash table, which holds
28-
* soft references to the contained glyph geometry informations.
28+
* This class holds
29+
* soft references to the contained glyph geometry information using a {@link java.util.concurrent.ConcurrentHashMap}.
2930
*
3031
* @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
3132
* @author <a href="mailto:tkormann@ilog.fr">Thierry Kormann</a>
@@ -39,160 +40,55 @@ public class AWTGlyphGeometryCache {
3940
protected static final int INITIAL_CAPACITY = 71;
4041

4142
/**
42-
* The underlying array
43+
* The underlying map
4344
*/
44-
protected Entry[] table;
45-
46-
/**
47-
* The number of entries
48-
*/
49-
protected int count;
50-
51-
/**
52-
* The reference queue.
53-
*/
54-
protected ReferenceQueue referenceQueue = new ReferenceQueue();
45+
ConcurrentHashMap<Character, SoftReference<Value>> cache;
5546

5647
/**
5748
* Creates a new AWTGlyphGeometryCache.
5849
*/
5950
public AWTGlyphGeometryCache() {
60-
table = new Entry[INITIAL_CAPACITY];
51+
this(INITIAL_CAPACITY);
6152
}
6253

6354
/**
6455
* Creates a new AWTGlyphGeometryCache.
6556
* @param c The inital capacity.
6657
*/
6758
public AWTGlyphGeometryCache(int c) {
68-
table = new Entry[c];
59+
cache = new ConcurrentHashMap<>(c);
6960
}
7061

7162
/**
7263
* Returns the size of this table.
7364
*/
7465
public int size() {
75-
return count;
66+
return cache.size();
7667
}
7768

7869
/**
7970
* Gets the value of a variable
8071
* @return the value or null
8172
*/
8273
public Value get(char c) {
83-
int hash = hashCode(c) & 0x7FFFFFFF;
84-
int index = hash % table.length;
85-
86-
for (Entry e = table[index]; e != null; e = e.next) {
87-
if ((e.hash == hash) && e.match(c)) {
88-
return (Value)e.get();
89-
}
90-
}
91-
return null;
74+
return Optional.ofNullable(cache.get(c)).map(SoftReference::get).orElse(null);
9275
}
9376

9477
/**
9578
* Sets a new value for the given variable
9679
* @return the old value or null
9780
*/
9881
public Value put(char c, Value value) {
99-
removeClearedEntries();
100-
101-
int hash = hashCode(c) & 0x7FFFFFFF;
102-
int index = hash % table.length;
103-
104-
Entry e = table[index];
105-
if (e != null) {
106-
if ((e.hash == hash) && e.match(c)) {
107-
Object old = e.get();
108-
table[index] = new Entry(hash, c, value, e.next);
109-
return (Value)old;
110-
}
111-
Entry o = e;
112-
e = e.next;
113-
while (e != null) {
114-
if ((e.hash == hash) && e.match(c)) {
115-
Object old = e.get();
116-
e = new Entry(hash, c, value, e.next);
117-
o.next = e;
118-
return (Value)old;
119-
}
120-
121-
o = e;
122-
e = e.next;
123-
}
124-
}
125-
126-
// The key is not in the hash table
127-
int len = table.length;
128-
if (count++ >= (len - (len >> 2))) {
129-
// more than 75% loaded: grow
130-
rehash();
131-
index = hash % table.length;
132-
}
133-
134-
table[index] = new Entry(hash, c, value, table[index]);
135-
return null;
82+
Value oldValue = get(c);
83+
cache.put(c, new SoftReference<>(value));
84+
return oldValue;
13685
}
13786

13887
/**
13988
* Clears the table.
14089
*/
14190
public void clear() {
142-
table = new Entry[INITIAL_CAPACITY];
143-
count = 0;
144-
referenceQueue = new ReferenceQueue();
145-
}
146-
147-
/**
148-
* Rehash the table
149-
*/
150-
protected void rehash () {
151-
Entry[] oldTable = table;
152-
153-
table = new Entry[oldTable.length * 2 + 1];
154-
155-
for (int i = oldTable.length-1; i >= 0; i--) {
156-
for (Entry old = oldTable[i]; old != null;) {
157-
Entry e = old;
158-
old = old.next;
159-
160-
int index = e.hash % table.length;
161-
e.next = table[index];
162-
table[index] = e;
163-
}
164-
}
165-
}
166-
167-
/**
168-
* Computes a hash code corresponding to the given objects.
169-
*/
170-
protected int hashCode(char c) {
171-
return c;
172-
}
173-
174-
/**
175-
* Removes the cleared entries.
176-
*/
177-
protected void removeClearedEntries() {
178-
Entry e;
179-
while ((e = (Entry)referenceQueue.poll()) != null) {
180-
int index = e.hash % table.length;
181-
Entry t = table[index];
182-
if (t == e) {
183-
table[index] = e.next;
184-
} else {
185-
loop: for (;t!=null;) {
186-
Entry c = t.next;
187-
if (c == e) {
188-
t.next = e.next;
189-
break loop;
190-
}
191-
t = c;
192-
}
193-
}
194-
count--;
195-
}
91+
cache.clear();
19692
}
19793

19894
/**
@@ -234,42 +130,4 @@ public Rectangle2D getOutlineBounds2D() {
234130
return outlineBounds;
235131
}
236132
}
237-
238-
/**
239-
* To manage collisions
240-
*/
241-
protected class Entry extends SoftReference {
242-
243-
/**
244-
* The hash code
245-
*/
246-
public int hash;
247-
248-
/**
249-
* The character
250-
*/
251-
public char c;
252-
253-
/**
254-
* The next entry
255-
*/
256-
public Entry next;
257-
258-
/**
259-
* Creates a new entry
260-
*/
261-
public Entry(int hash, char c, Value value, Entry next) {
262-
super(value, referenceQueue);
263-
this.hash = hash;
264-
this.c = c;
265-
this.next = next;
266-
}
267-
268-
/**
269-
* Whether this entry match the given keys.
270-
*/
271-
public boolean match(char o2) {
272-
return (c == o2);
273-
}
274-
}
275133
}

0 commit comments

Comments
 (0)