Skip to content

Commit a6592f3

Browse files
author
Elia Trachsel
committed
[GR-65608] Improvement of Strong/WeakHandles
PullRequest: graal/21037
2 parents a9086c9 + f261f4a commit a6592f3

File tree

5 files changed

+235
-268
lines changed

5 files changed

+235
-268
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package com.oracle.truffle.espresso.jni;
24+
25+
import java.util.Arrays;
26+
import java.util.Objects;
27+
28+
import com.oracle.truffle.api.CompilerDirectives;
29+
30+
public abstract class HandleStorage<T, REF> {
31+
protected static final int DEFAULT_INITIAL_CAPACITY = 16;
32+
private volatile int nextHandle = 1;
33+
private REF[] handles;
34+
private HandlesStack stack;
35+
private final boolean implicitFree;
36+
37+
@SuppressWarnings("unchecked")
38+
HandleStorage(boolean implicitFree) {
39+
this.implicitFree = implicitFree;
40+
handles = (REF[]) new Object[DEFAULT_INITIAL_CAPACITY];
41+
stack = new HandlesStack();
42+
}
43+
44+
@CompilerDirectives.TruffleBoundary
45+
public synchronized long handlify(T object) {
46+
Objects.requireNonNull(object);
47+
int handle = findFreeIndex();
48+
handles[handle] = toREF(object);
49+
return handle;
50+
}
51+
52+
private synchronized int findFreeIndex() {
53+
int handle = stack.pop();
54+
if (handle != -1) {
55+
return handle;
56+
}
57+
if (nextHandle < handles.length) {
58+
return nextHandle++;
59+
}
60+
// nextHandle overflows so try to collect
61+
handle = collect();
62+
if (handle != -1) {
63+
return handle;
64+
}
65+
// resize is needed
66+
handles = Arrays.copyOf(handles, handles.length << 1);
67+
return nextHandle++;
68+
}
69+
70+
/**
71+
* Returns the object associated with a handle. This operation is performance-critical,
72+
* shouldn't block.
73+
*
74+
* @param handle handle, must be > 0 and fit in an integer
75+
* @return the object associated with the handle or null
76+
*/
77+
@SuppressWarnings("unchecked")
78+
public final T getObject(long handle) {
79+
if (invalidHandle(handle)) {
80+
return null;
81+
}
82+
return deREF(handles[(int) handle]);
83+
}
84+
85+
/**
86+
* Explicitly frees the resources associated with a handle.
87+
*
88+
* @throws UnsupportedOperationException if the storage is in implicitFree mode
89+
*/
90+
public final void freeHandle(long handle) {
91+
if (implicitFree) {
92+
throw new UnsupportedOperationException("ExplicitFree method should not be called in implicitFree mode!");
93+
}
94+
if (invalidHandle(handle)) {
95+
return;
96+
}
97+
synchronized (this) {
98+
Object object = handles[(int) handle];
99+
if (object != null) {
100+
handles[(int) handle] = null;
101+
stack.push((int) handle);
102+
}
103+
}
104+
}
105+
106+
/**
107+
* Collects all unused handles if in implicitFree mode.
108+
*
109+
* @return the next free handle or -1 if no free handle is available.
110+
*/
111+
private synchronized int collect() {
112+
if (implicitFree) {
113+
for (int i = 1; i < handles.length; i++) {
114+
REF ref = handles[i];
115+
if (ref == null || deREF(ref) == null) {
116+
handles[i] = null;
117+
stack.push(i);
118+
}
119+
}
120+
return stack.pop();
121+
}
122+
return -1;
123+
}
124+
125+
private boolean invalidHandle(long handle) {
126+
return (handle <= 0 || handle >= handles.length);
127+
}
128+
129+
/**
130+
* Abstract method for obtaining a storgae Refrence from the object to store.
131+
*/
132+
abstract REF toREF(T object);
133+
134+
/**
135+
* Abstract method for obtaining the object from the storage reference to store.
136+
*/
137+
abstract T deREF(REF ref);
138+
139+
public static final class HandlesStack {
140+
private static final int MINIMUM_CAPACITY = 8;
141+
142+
private int[] stack;
143+
private volatile int head = 0;
144+
145+
public HandlesStack() {
146+
this(MINIMUM_CAPACITY);
147+
}
148+
149+
private HandlesStack(int capacity) {
150+
if (capacity < MINIMUM_CAPACITY) {
151+
stack = new int[MINIMUM_CAPACITY];
152+
} else {
153+
stack = new int[capacity];
154+
}
155+
}
156+
157+
public synchronized void push(int handle) {
158+
if (head >= stack.length) {
159+
stack = Arrays.copyOf(stack, stack.length << 1);
160+
}
161+
stack[head++] = handle;
162+
}
163+
164+
public synchronized int pop() {
165+
if (head == 0) {
166+
return -1;
167+
}
168+
int halfLength = stack.length >> 1;
169+
if (halfLength >= MINIMUM_CAPACITY && head < (halfLength >> 1)) {
170+
// conservative downsizing
171+
stack = Arrays.copyOf(stack, halfLength);
172+
}
173+
return stack[--head];
174+
}
175+
}
176+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package com.oracle.truffle.espresso.jni;
24+
25+
public final class StrongHandles<T> extends HandleStorage<T, T> {
26+
public StrongHandles() {
27+
super(false);
28+
}
29+
30+
@Override
31+
T toREF(T object) {
32+
return object;
33+
}
34+
35+
@Override
36+
T deREF(T ref) {
37+
return ref;
38+
}
39+
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/WeakHandles.java

Lines changed: 17 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -23,123 +23,42 @@
2323
package com.oracle.truffle.espresso.jni;
2424

2525
import java.lang.ref.WeakReference;
26-
import java.util.Arrays;
27-
import java.util.LinkedList;
2826
import java.util.Objects;
2927
import java.util.WeakHashMap;
3028

31-
import com.oracle.truffle.api.CompilerDirectives;
3229
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
3330

3431
/**
3532
* Manages a collection of weak references associated to handles.
3633
*/
37-
public class WeakHandles<T> {
38-
39-
private static final int DEFAULT_INITIAL_CAPACITY = 16;
40-
34+
public final class WeakHandles<T> extends HandleStorage<T, WeakReference<T>> {
4135
private final WeakHashMap<T, Integer> map;
42-
private final LinkedList<Integer> freeList = new LinkedList<>();
4336

44-
// Non-empty.
45-
private WeakReference<T>[] handles;
37+
@Override
38+
WeakReference<T> toREF(T object) {
39+
return new WeakReference<>(object);
40+
}
4641

47-
/**
48-
* Creates a handle collection pre-allocated capacity.
49-
*
50-
* @param initialCapacity must be > 0
51-
*/
52-
@SuppressWarnings({"unchecked", "rawtypes"})
53-
public WeakHandles(int initialCapacity) {
54-
if (initialCapacity <= 0) {
55-
throw new IllegalArgumentException("initialCapacity must be > 0");
56-
}
57-
map = new WeakHashMap<>(initialCapacity);
58-
handles = new WeakReference[initialCapacity];
42+
@Override
43+
T deREF(WeakReference<T> ref) {
44+
return ref != null ? ref.get() : null;
5945
}
6046

6147
public WeakHandles() {
62-
this(DEFAULT_INITIAL_CAPACITY);
48+
super(true);
49+
map = new WeakHashMap<>(DEFAULT_INITIAL_CAPACITY);
6350
}
6451

65-
/**
66-
* Creates a new handle for the given object or returns an existing handle if the object is
67-
* already in the collection.
68-
*
69-
* @return new or existing handle, provided handles are guanteed to be > 0
70-
*/
7152
@TruffleBoundary
72-
public synchronized int handlify(T object) {
53+
@Override
54+
public synchronized long handlify(T object) {
7355
Objects.requireNonNull(object);
7456
Integer handle = map.get(object);
75-
return handle != null
76-
? handle
77-
: addHandle(object);
78-
}
79-
80-
/**
81-
* Returns the object associated with a handle. This operation is performance-critical,
82-
* shouldn't block.
83-
*
84-
* @param index handle, must be > 0 and fit in an integer
85-
* @return the object associated with the handle or null if was collected
86-
*/
87-
@SuppressWarnings("unchecked")
88-
public T getObject(long index) {
89-
if (index <= 0) {
90-
throw new IllegalArgumentException("index");
91-
}
92-
WeakReference<T> weakRef = CompilerDirectives.castExact(handles[Math.toIntExact(index)], WeakReference.class);
93-
return weakRef != null
94-
? weakRef.get()
95-
: null;
96-
}
97-
98-
/**
99-
* Returns the handle associated with a given object.
100-
*
101-
* @return The handle associated with the given object or -1 if the object doesn't have a handle
102-
* or the object was collected. A valid handle is guaranteed to be != 0.
103-
*/
104-
@TruffleBoundary
105-
public synchronized long getIndex(T object) {
106-
Integer index = map.get(Objects.requireNonNull(object));
107-
return index != null
108-
? index
109-
: -1;
110-
}
111-
112-
@TruffleBoundary
113-
private int getFreeSlot() {
114-
if (!freeList.isEmpty()) {
115-
return freeList.removeFirst();
116-
}
117-
// 0 is a dummy entry, start at 1 to avoid NULL handles.
118-
for (int i = 1; i < handles.length; ++i) {
119-
if (handles[i] == null || handles[i].get() == null) {
120-
freeList.addLast(i);
121-
}
122-
}
123-
return freeList.isEmpty()
124-
? -1
125-
: freeList.removeFirst();
126-
}
127-
128-
@TruffleBoundary
129-
private synchronized int addHandle(T object) {
130-
Objects.requireNonNull(object);
131-
int index = getFreeSlot();
132-
if (index < 0) { // no slot available
133-
WeakReference<T>[] newHandles = Arrays.copyOf(handles, 2 * handles.length);
134-
for (int i = handles.length; i < newHandles.length; ++i) {
135-
freeList.addLast(i);
136-
}
137-
handles = newHandles;
138-
index = freeList.removeFirst();
57+
if (handle != null) {
58+
return handle;
13959
}
140-
assert index >= 0;
141-
handles[index] = new WeakReference<>(object);
142-
map.put(object, index);
143-
return index;
60+
handle = (int) super.handlify(object);
61+
map.put(object, handle);
62+
return handle;
14463
}
14564
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/libs/LibsState.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,20 @@
2525
import java.util.zip.Inflater;
2626

2727
import com.oracle.truffle.api.CompilerDirectives;
28+
import com.oracle.truffle.espresso.jni.StrongHandles;
2829
import com.oracle.truffle.espresso.meta.Meta;
2930
import com.oracle.truffle.espresso.runtime.EspressoContext;
3031
import com.oracle.truffle.espresso.runtime.EspressoException;
3132

3233
public class LibsState {
3334
private final StrongHandles<Inflater> handle2Inflater = new StrongHandles<>();
3435

35-
public int handlifyInflater(Inflater i) {
36+
public long handlifyInflater(Inflater i) {
3637
return handle2Inflater.handlify(i);
3738
}
3839

3940
public void cleanInflater(long handle) {
40-
handle2Inflater.cleanIndex(handle);
41+
handle2Inflater.freeHandle(handle);
4142
}
4243

4344
public Inflater getInflater(long handle) {

0 commit comments

Comments
 (0)