Skip to content

Commit

Permalink
Merge pull request #76 from bgilbert/cache
Browse files Browse the repository at this point in the history
Support OpenSlide cache management API
  • Loading branch information
bgilbert authored Apr 14, 2024
2 parents 138ba9a + 12ba0c3 commit 3a84c21
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 17 deletions.
15 changes: 15 additions & 0 deletions org/openslide/OpenSlide.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* OpenSlide, a library for reading whole slide image files
*
* Copyright (c) 2007-2011 Carnegie Mellon University
* Copyright (c) 2024 Benjamin Gilbert
* All rights reserved.
*
* OpenSlide is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -420,6 +421,20 @@ BufferedImage getAssociatedImage(String name) throws IOException {
}
}

public void setCache(OpenSlideCache cache) {
Lock cl = cache.getLock();
Lock rl = lock.readLock();
cl.lock();
rl.lock();
try {
checkDisposed();
OpenSlideFFM.openslide_set_cache(osr, cache.getSegment());
} finally {
rl.unlock();
cl.unlock();
}
}

public static String getLibraryVersion() {
return LIBRARY_VERSION;
}
Expand Down
63 changes: 63 additions & 0 deletions org/openslide/OpenSlideCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* OpenSlide, a library for reading whole slide image files
*
* Copyright (c) 2007-2011 Carnegie Mellon University
* Copyright (c) 2024 Benjamin Gilbert
* All rights reserved.
*
* OpenSlide is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, version 2.1.
*
* OpenSlide is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with OpenSlide. If not, see
* <http://www.gnu.org/licenses/>.
*
*/

package org.openslide;

import java.lang.foreign.MemorySegment;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public final class OpenSlideCache implements AutoCloseable {
private final Lock lock = new ReentrantLock();

private MemorySegment cache;

public OpenSlideCache(long capacity) {
cache = OpenSlideFFM.openslide_cache_create(capacity);
}

Lock getLock() {
return lock;
}

// call, and use result, with lock held
MemorySegment getSegment() {
if (cache == null) {
throw new OpenSlideDisposedException("OpenSlideCache");
}
return cache;
}

// takes the lock
@Override
public void close() {
lock.lock();
try {
if (cache != null) {
OpenSlideFFM.openslide_cache_release(cache);
cache = null;
}
} finally {
lock.unlock();
}
}
}
8 changes: 6 additions & 2 deletions org/openslide/OpenSlideDisposedException.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@
package org.openslide;

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

public OpenSlideDisposedException() {
super(MSG);
this("OpenSlide");
}

public OpenSlideDisposedException(String what) {
super(what + MSG);
}
}
64 changes: 50 additions & 14 deletions org/openslide/OpenSlideFFM.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ class OpenSlideFFM {
private static final AddressLayout C_POINTER = ADDRESS.withTargetLayout(
MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE));

private static final MemoryLayout SIZE_T = Linker.nativeLinker()
.canonicalLayouts().get("size_t");

private OpenSlideFFM() {
}

Expand Down Expand Up @@ -102,7 +105,7 @@ static String openslide_detect_vendor(String filename) {
ret = (MemorySegment) detect_vendor.invokeExact(
arena.allocateFrom(filename));
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
throw new AssertionError("Invalid call", ex);
}
if (ret.equals(MemorySegment.NULL)) {
return null;
Expand All @@ -122,7 +125,7 @@ static MemorySegment openslide_open(String filename) {
ret = (MemorySegment) open.invokeExact(
arena.allocateFrom(filename));
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
throw new AssertionError("Invalid call", ex);
}
if (ret.equals(MemorySegment.NULL)) {
return null;
Expand All @@ -137,7 +140,7 @@ static int openslide_get_level_count(MemorySegment osr) {
try {
return (int) get_level_count.invokeExact(osr);
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
throw new AssertionError("Invalid call", ex);
}
}

Expand All @@ -153,7 +156,7 @@ static void openslide_get_level_dimensions(MemorySegment osr, int level,
try {
get_level_dimensions.invokeExact(osr, level, w, h);
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
throw new AssertionError("Invalid call", ex);
}
dim[0] = w.get(JAVA_LONG, 0);
dim[1] = h.get(JAVA_LONG, 0);
Expand All @@ -167,7 +170,7 @@ static double openslide_get_level_downsample(MemorySegment osr, int level) {
try {
return (double) get_level_downsample.invokeExact(osr, level);
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
throw new AssertionError("Invalid call", ex);
}
}

Expand All @@ -182,7 +185,7 @@ static void openslide_read_region(MemorySegment osr, int dest[],
try {
read_region.invokeExact(osr, buf, x, y, level, w, h);
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
throw new AssertionError("Invalid call", ex);
}
MemorySegment.copy(buf, JAVA_INT, 0, dest, 0, dest.length);
}
Expand All @@ -195,7 +198,7 @@ static void openslide_close(MemorySegment osr) {
try {
close.invokeExact(osr);
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
throw new AssertionError("Invalid call", ex);
}
}

Expand All @@ -207,7 +210,7 @@ static String openslide_get_error(MemorySegment osr) {
try {
ret = (MemorySegment) get_error.invokeExact(osr);
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
throw new AssertionError("Invalid call", ex);
}
if (ret.equals(MemorySegment.NULL)) {
return null;
Expand All @@ -223,7 +226,7 @@ static String[] openslide_get_property_names(MemorySegment osr) {
try {
ret = (MemorySegment) get_property_names.invokeExact(osr);
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
throw new AssertionError("Invalid call", ex);
}
return segment_to_string_array(ret);
}
Expand All @@ -240,7 +243,7 @@ static String openslide_get_property_value(MemorySegment osr, String name) {
ret = (MemorySegment) get_property_value.invokeExact(osr,
arena.allocateFrom(name));
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
throw new AssertionError("Invalid call", ex);
}
if (ret.equals(MemorySegment.NULL)) {
return null;
Expand All @@ -256,7 +259,7 @@ static String[] openslide_get_associated_image_names(MemorySegment osr) {
try {
ret = (MemorySegment) get_associated_image_names.invokeExact(osr);
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
throw new AssertionError("Invalid call", ex);
}
return segment_to_string_array(ret);
}
Expand All @@ -277,7 +280,7 @@ static void openslide_get_associated_image_dimensions(MemorySegment osr,
get_associated_image_dimensions.invokeExact(osr,
arena.allocateFrom(name), w, h);
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
throw new AssertionError("Invalid call", ex);
}
dim[0] = w.get(JAVA_LONG, 0);
dim[1] = h.get(JAVA_LONG, 0);
Expand All @@ -299,12 +302,45 @@ static void openslide_read_associated_image(MemorySegment osr, String name,
read_associated_image.invokeExact(osr, arena.allocateFrom(name),
buf);
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
throw new AssertionError("Invalid call", ex);
}
MemorySegment.copy(buf, JAVA_INT, 0, dest, 0, dest.length);
}
}

private static final MethodHandle cache_create = function(
C_POINTER, "openslide_cache_create", SIZE_T);

static MemorySegment openslide_cache_create(long capacity) {
try {
return (MemorySegment) cache_create.invokeExact(capacity);
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
}
}

private static final MethodHandle set_cache = function(
null, "openslide_set_cache", C_POINTER, C_POINTER);

static void openslide_set_cache(MemorySegment osr, MemorySegment cache) {
try {
set_cache.invokeExact(osr, cache);
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
}
}

private static final MethodHandle cache_release = function(
null, "openslide_cache_release", C_POINTER);

static void openslide_cache_release(MemorySegment cache) {
try {
cache_release.invokeExact(cache);
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
}
}

private static final MethodHandle get_version = function(
C_POINTER, "openslide_get_version");

Expand All @@ -313,7 +349,7 @@ static String openslide_get_version() {
try {
ret = (MemorySegment) get_version.invokeExact();
} catch (Throwable ex) {
throw new AssertionError("Invalid call", ex);
throw new AssertionError("Invalid call", ex);
}
return ret.getString(0);
}
Expand Down
5 changes: 4 additions & 1 deletion org/openslide/TestCLI.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ public static void main(String args[]) throws IOException {

osr.dispose();

osr = new OpenSlide(f);
try (OpenSlideCache cache = new OpenSlideCache(64 << 20)) {
osr = new OpenSlide(f);
osr.setCache(cache);
}

w = osr.getLevel0Width();
h = osr.getLevel0Height();
Expand Down

0 comments on commit 3a84c21

Please sign in to comment.