Skip to content

Commit 781ecc5

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 781ecc5

File tree

1 file changed

+14
-155
lines changed

1 file changed

+14
-155
lines changed

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

Lines changed: 14 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ 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.concurrent.ConcurrentHashMap;
2525

2626
/**
27-
* This class represents a doubly indexed hash table, which holds
28-
* soft references to the contained glyph geometry informations.
27+
* This class holds
28+
* soft references to the contained glyph geometry information using a {@link java.util.concurrent.ConcurrentHashMap}.
2929
*
3030
* @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
3131
* @author <a href="mailto:tkormann@ilog.fr">Thierry Kormann</a>
@@ -38,161 +38,58 @@ public class AWTGlyphGeometryCache {
3838
*/
3939
protected static final int INITIAL_CAPACITY = 71;
4040

41-
/**
42-
* The underlying array
43-
*/
44-
protected Entry[] table;
45-
46-
/**
47-
* The number of entries
48-
*/
49-
protected int count;
41+
private static <T> T getIfSet(SoftReference<T> ref) {
42+
return ref == null ? null : ref.get();
43+
}
5044

5145
/**
52-
* The reference queue.
46+
* The underlying map
5347
*/
54-
protected ReferenceQueue referenceQueue = new ReferenceQueue();
48+
ConcurrentHashMap<Character, SoftReference<Value>> cache;
5549

5650
/**
5751
* Creates a new AWTGlyphGeometryCache.
5852
*/
5953
public AWTGlyphGeometryCache() {
60-
table = new Entry[INITIAL_CAPACITY];
54+
this(INITIAL_CAPACITY);
6155
}
6256

6357
/**
6458
* Creates a new AWTGlyphGeometryCache.
6559
* @param c The inital capacity.
6660
*/
6761
public AWTGlyphGeometryCache(int c) {
68-
table = new Entry[c];
62+
cache = new ConcurrentHashMap<>(c);
6963
}
7064

7165
/**
7266
* Returns the size of this table.
7367
*/
7468
public int size() {
75-
return count;
69+
return cache.size();
7670
}
7771

7872
/**
7973
* Gets the value of a variable
8074
* @return the value or null
8175
*/
8276
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;
77+
return getIfSet(cache.get(c));
9278
}
9379

9480
/**
9581
* Sets a new value for the given variable
9682
* @return the old value or null
9783
*/
9884
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;
85+
return getIfSet(cache.put(c, new SoftReference<>(value)));
13686
}
13787

13888
/**
13989
* Clears the table.
14090
*/
14191
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-
}
92+
cache.clear();
19693
}
19794

19895
/**
@@ -234,42 +131,4 @@ public Rectangle2D getOutlineBounds2D() {
234131
return outlineBounds;
235132
}
236133
}
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-
}
275134
}

0 commit comments

Comments
 (0)