Skip to content

Commit 3a84c21

Browse files
authored
Merge pull request #76 from bgilbert/cache
Support OpenSlide cache management API
2 parents 138ba9a + 12ba0c3 commit 3a84c21

File tree

5 files changed

+138
-17
lines changed

5 files changed

+138
-17
lines changed

org/openslide/OpenSlide.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* OpenSlide, a library for reading whole slide image files
33
*
44
* Copyright (c) 2007-2011 Carnegie Mellon University
5+
* Copyright (c) 2024 Benjamin Gilbert
56
* All rights reserved.
67
*
78
* OpenSlide is free software: you can redistribute it and/or modify
@@ -420,6 +421,20 @@ BufferedImage getAssociatedImage(String name) throws IOException {
420421
}
421422
}
422423

424+
public void setCache(OpenSlideCache cache) {
425+
Lock cl = cache.getLock();
426+
Lock rl = lock.readLock();
427+
cl.lock();
428+
rl.lock();
429+
try {
430+
checkDisposed();
431+
OpenSlideFFM.openslide_set_cache(osr, cache.getSegment());
432+
} finally {
433+
rl.unlock();
434+
cl.unlock();
435+
}
436+
}
437+
423438
public static String getLibraryVersion() {
424439
return LIBRARY_VERSION;
425440
}

org/openslide/OpenSlideCache.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* OpenSlide, a library for reading whole slide image files
3+
*
4+
* Copyright (c) 2007-2011 Carnegie Mellon University
5+
* Copyright (c) 2024 Benjamin Gilbert
6+
* All rights reserved.
7+
*
8+
* OpenSlide is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Lesser General Public License as
10+
* published by the Free Software Foundation, version 2.1.
11+
*
12+
* OpenSlide is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public
18+
* License along with OpenSlide. If not, see
19+
* <http://www.gnu.org/licenses/>.
20+
*
21+
*/
22+
23+
package org.openslide;
24+
25+
import java.lang.foreign.MemorySegment;
26+
import java.util.concurrent.locks.Lock;
27+
import java.util.concurrent.locks.ReentrantLock;
28+
29+
public final class OpenSlideCache implements AutoCloseable {
30+
private final Lock lock = new ReentrantLock();
31+
32+
private MemorySegment cache;
33+
34+
public OpenSlideCache(long capacity) {
35+
cache = OpenSlideFFM.openslide_cache_create(capacity);
36+
}
37+
38+
Lock getLock() {
39+
return lock;
40+
}
41+
42+
// call, and use result, with lock held
43+
MemorySegment getSegment() {
44+
if (cache == null) {
45+
throw new OpenSlideDisposedException("OpenSlideCache");
46+
}
47+
return cache;
48+
}
49+
50+
// takes the lock
51+
@Override
52+
public void close() {
53+
lock.lock();
54+
try {
55+
if (cache != null) {
56+
OpenSlideFFM.openslide_cache_release(cache);
57+
cache = null;
58+
}
59+
} finally {
60+
lock.unlock();
61+
}
62+
}
63+
}

org/openslide/OpenSlideDisposedException.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@
2222
package org.openslide;
2323

2424
public class OpenSlideDisposedException extends RuntimeException {
25-
private static final String MSG = "OpenSlide object has been disposed";
25+
private static final String MSG = " object has been closed";
2626

2727
public OpenSlideDisposedException() {
28-
super(MSG);
28+
this("OpenSlide");
29+
}
30+
31+
public OpenSlideDisposedException(String what) {
32+
super(what + MSG);
2933
}
3034
}

org/openslide/OpenSlideFFM.java

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ class OpenSlideFFM {
3434
private static final AddressLayout C_POINTER = ADDRESS.withTargetLayout(
3535
MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE));
3636

37+
private static final MemoryLayout SIZE_T = Linker.nativeLinker()
38+
.canonicalLayouts().get("size_t");
39+
3740
private OpenSlideFFM() {
3841
}
3942

@@ -102,7 +105,7 @@ static String openslide_detect_vendor(String filename) {
102105
ret = (MemorySegment) detect_vendor.invokeExact(
103106
arena.allocateFrom(filename));
104107
} catch (Throwable ex) {
105-
throw new AssertionError("Invalid call", ex);
108+
throw new AssertionError("Invalid call", ex);
106109
}
107110
if (ret.equals(MemorySegment.NULL)) {
108111
return null;
@@ -122,7 +125,7 @@ static MemorySegment openslide_open(String filename) {
122125
ret = (MemorySegment) open.invokeExact(
123126
arena.allocateFrom(filename));
124127
} catch (Throwable ex) {
125-
throw new AssertionError("Invalid call", ex);
128+
throw new AssertionError("Invalid call", ex);
126129
}
127130
if (ret.equals(MemorySegment.NULL)) {
128131
return null;
@@ -137,7 +140,7 @@ static int openslide_get_level_count(MemorySegment osr) {
137140
try {
138141
return (int) get_level_count.invokeExact(osr);
139142
} catch (Throwable ex) {
140-
throw new AssertionError("Invalid call", ex);
143+
throw new AssertionError("Invalid call", ex);
141144
}
142145
}
143146

@@ -153,7 +156,7 @@ static void openslide_get_level_dimensions(MemorySegment osr, int level,
153156
try {
154157
get_level_dimensions.invokeExact(osr, level, w, h);
155158
} catch (Throwable ex) {
156-
throw new AssertionError("Invalid call", ex);
159+
throw new AssertionError("Invalid call", ex);
157160
}
158161
dim[0] = w.get(JAVA_LONG, 0);
159162
dim[1] = h.get(JAVA_LONG, 0);
@@ -167,7 +170,7 @@ static double openslide_get_level_downsample(MemorySegment osr, int level) {
167170
try {
168171
return (double) get_level_downsample.invokeExact(osr, level);
169172
} catch (Throwable ex) {
170-
throw new AssertionError("Invalid call", ex);
173+
throw new AssertionError("Invalid call", ex);
171174
}
172175
}
173176

@@ -182,7 +185,7 @@ static void openslide_read_region(MemorySegment osr, int dest[],
182185
try {
183186
read_region.invokeExact(osr, buf, x, y, level, w, h);
184187
} catch (Throwable ex) {
185-
throw new AssertionError("Invalid call", ex);
188+
throw new AssertionError("Invalid call", ex);
186189
}
187190
MemorySegment.copy(buf, JAVA_INT, 0, dest, 0, dest.length);
188191
}
@@ -195,7 +198,7 @@ static void openslide_close(MemorySegment osr) {
195198
try {
196199
close.invokeExact(osr);
197200
} catch (Throwable ex) {
198-
throw new AssertionError("Invalid call", ex);
201+
throw new AssertionError("Invalid call", ex);
199202
}
200203
}
201204

@@ -207,7 +210,7 @@ static String openslide_get_error(MemorySegment osr) {
207210
try {
208211
ret = (MemorySegment) get_error.invokeExact(osr);
209212
} catch (Throwable ex) {
210-
throw new AssertionError("Invalid call", ex);
213+
throw new AssertionError("Invalid call", ex);
211214
}
212215
if (ret.equals(MemorySegment.NULL)) {
213216
return null;
@@ -223,7 +226,7 @@ static String[] openslide_get_property_names(MemorySegment osr) {
223226
try {
224227
ret = (MemorySegment) get_property_names.invokeExact(osr);
225228
} catch (Throwable ex) {
226-
throw new AssertionError("Invalid call", ex);
229+
throw new AssertionError("Invalid call", ex);
227230
}
228231
return segment_to_string_array(ret);
229232
}
@@ -240,7 +243,7 @@ static String openslide_get_property_value(MemorySegment osr, String name) {
240243
ret = (MemorySegment) get_property_value.invokeExact(osr,
241244
arena.allocateFrom(name));
242245
} catch (Throwable ex) {
243-
throw new AssertionError("Invalid call", ex);
246+
throw new AssertionError("Invalid call", ex);
244247
}
245248
if (ret.equals(MemorySegment.NULL)) {
246249
return null;
@@ -256,7 +259,7 @@ static String[] openslide_get_associated_image_names(MemorySegment osr) {
256259
try {
257260
ret = (MemorySegment) get_associated_image_names.invokeExact(osr);
258261
} catch (Throwable ex) {
259-
throw new AssertionError("Invalid call", ex);
262+
throw new AssertionError("Invalid call", ex);
260263
}
261264
return segment_to_string_array(ret);
262265
}
@@ -277,7 +280,7 @@ static void openslide_get_associated_image_dimensions(MemorySegment osr,
277280
get_associated_image_dimensions.invokeExact(osr,
278281
arena.allocateFrom(name), w, h);
279282
} catch (Throwable ex) {
280-
throw new AssertionError("Invalid call", ex);
283+
throw new AssertionError("Invalid call", ex);
281284
}
282285
dim[0] = w.get(JAVA_LONG, 0);
283286
dim[1] = h.get(JAVA_LONG, 0);
@@ -299,12 +302,45 @@ static void openslide_read_associated_image(MemorySegment osr, String name,
299302
read_associated_image.invokeExact(osr, arena.allocateFrom(name),
300303
buf);
301304
} catch (Throwable ex) {
302-
throw new AssertionError("Invalid call", ex);
305+
throw new AssertionError("Invalid call", ex);
303306
}
304307
MemorySegment.copy(buf, JAVA_INT, 0, dest, 0, dest.length);
305308
}
306309
}
307310

311+
private static final MethodHandle cache_create = function(
312+
C_POINTER, "openslide_cache_create", SIZE_T);
313+
314+
static MemorySegment openslide_cache_create(long capacity) {
315+
try {
316+
return (MemorySegment) cache_create.invokeExact(capacity);
317+
} catch (Throwable ex) {
318+
throw new AssertionError("Invalid call", ex);
319+
}
320+
}
321+
322+
private static final MethodHandle set_cache = function(
323+
null, "openslide_set_cache", C_POINTER, C_POINTER);
324+
325+
static void openslide_set_cache(MemorySegment osr, MemorySegment cache) {
326+
try {
327+
set_cache.invokeExact(osr, cache);
328+
} catch (Throwable ex) {
329+
throw new AssertionError("Invalid call", ex);
330+
}
331+
}
332+
333+
private static final MethodHandle cache_release = function(
334+
null, "openslide_cache_release", C_POINTER);
335+
336+
static void openslide_cache_release(MemorySegment cache) {
337+
try {
338+
cache_release.invokeExact(cache);
339+
} catch (Throwable ex) {
340+
throw new AssertionError("Invalid call", ex);
341+
}
342+
}
343+
308344
private static final MethodHandle get_version = function(
309345
C_POINTER, "openslide_get_version");
310346

@@ -313,7 +349,7 @@ static String openslide_get_version() {
313349
try {
314350
ret = (MemorySegment) get_version.invokeExact();
315351
} catch (Throwable ex) {
316-
throw new AssertionError("Invalid call", ex);
352+
throw new AssertionError("Invalid call", ex);
317353
}
318354
return ret.getString(0);
319355
}

org/openslide/TestCLI.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@ public static void main(String args[]) throws IOException {
5656

5757
osr.dispose();
5858

59-
osr = new OpenSlide(f);
59+
try (OpenSlideCache cache = new OpenSlideCache(64 << 20)) {
60+
osr = new OpenSlide(f);
61+
osr.setCache(cache);
62+
}
6063

6164
w = osr.getLevel0Width();
6265
h = osr.getLevel0Height();

0 commit comments

Comments
 (0)