Skip to content

Commit

Permalink
WIP: Split common Shm logic to base class
Browse files Browse the repository at this point in the history
There is a lot of duplicate code between ShmLinux, ShmMacOS,
and ShmWindows, which can be put into an internal base class.

This will make it easier to have consistent logic around the close
vs. unlink behavior across platforms, without copy/pasted code.
  • Loading branch information
ctrueden committed Jul 18, 2024
1 parent 7f2f57b commit 3b4c83c
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 176 deletions.
2 changes: 1 addition & 1 deletion src/main/java/org/apposed/appose/NDArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
import java.util.Arrays;

/**
* Represents a multi-dimensional array similar to NumPy ndarray.
* Represents a multidimensional array similar to a NumPy ndarray.
* <p>
* The array contains elements of a {@link DType data type}, arranged in a
* particular {@link Shape}, and flattened into {@link SharedMemory},
Expand Down
22 changes: 16 additions & 6 deletions src/main/java/org/apposed/appose/SharedMemory.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ static SharedMemory createOrAttach(String name, boolean create, int size) {
*
* @return The length in bytes of the shared memory.
*/
long size();
int size();

/**
* JNA pointer to the shared memory segment.
Expand All @@ -115,19 +115,29 @@ static SharedMemory createOrAttach(String name, boolean create, int size) {
*/
Pointer pointer();

/**
* Sets whether the {@link #unlink()} method should be invoked to destroy
* the shared memory block when the {@link #close()} method is called.
* <p>
* By default, shared memory objects constructed with {@link #create} will
* behave this way, whereas shared memory objects constructed with
* {@link #attach} will not. But this method allows to override the behavior.
* </p>
*/
void unlinkOnClose(boolean unlinkOnClose);

/**
* Requests that the underlying shared memory block be destroyed.
* In order to ensure proper cleanup of resources, unlink should be
* called once (and only once) across all processes which have access
* to the shared memory block.
*/
default void unlink() {
throw new UnsupportedOperationException();
}
void unlink();

/**
* Closes access to the shared memory from this instance but does
* not destroy the shared memory block.
* Closes access to the shared memory from this instance,
* but does not necessarily destroy the shared memory block.
* See also {@link #unlinkOnClose(boolean)}.
*/
@Override
void close();
Expand Down
13 changes: 12 additions & 1 deletion src/main/java/org/apposed/appose/ShmFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@
*/
package org.apposed.appose;

/**
* Interface for platform-specific creation of {@link SharedMemory} instances.
* <p>
* Each platform (e.g. Linux, macOS, Windows) provides its own implementation
* of this interface, which knows how to manufacture {@link SharedMemory} blocks
* for that platform. These implementations are declared as implementations in
* {@code META-INF/services/org.apposed.appose.ShmFactory}, so that Java's
* {@code ServiceLoader} can discover them in an extensible way, and then use
* the one best suited for the platform at hand.
* </p>
*/
public interface ShmFactory {
SharedMemory create(String name, boolean create, int size);
SharedMemory create(String name, boolean create, int size);
}
107 changes: 107 additions & 0 deletions src/main/java/org/apposed/appose/shm/ShmBase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*-
* #%L
* Appose: multi-language interprocess cooperation with shared memory.
* %%
* Copyright (C) 2023 - 2024 Appose developers.
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/

package org.apposed.appose.shm;

import com.sun.jna.Pointer;
import org.apposed.appose.SharedMemory;

/**
* Base class for platform-specific shared memory implementations.
*
* @author Carlos Garcia Lopez de Haro
* @author Tobias Pietzsch
* @author Curtis Rueden
*/
abstract class ShmBase implements SharedMemory {

/** Unique name that identifies the shared memory segment. */
private final String name;

/** Size in bytes. */
private final int size;

/** Pointer referencing the shared memory. */
private final Pointer pointer;

/** Whether to destroy the shared memory block when {@link #close()} is called. */
private boolean unlinkOnClose;

/** Whether the memory block has been closed. */
private boolean closed;

/** Whether the memory block has been unlinked. */
private boolean unlinked;

protected ShmBase(final String name, final boolean create, final int size, final Pointer pointer) {
this.name = name;
this.size = size;
this.pointer = pointer;
unlinkOnClose = create;
}

protected abstract void doClose();
protected abstract void doUnlink();

@Override
public String name() {
return name;
}

@Override
public int size() {
return size;
}

@Override
public Pointer pointer() {
return pointer;
}

@Override
public void unlinkOnClose(boolean unlinkOnClose) {
this.unlinkOnClose = unlinkOnClose;
}

@Override
public synchronized void unlink() {
if (unlinked) throw new IllegalStateException("Shared memory '" + name + "' is already unlinked.");
doClose();
doUnlink();
unlinked = true;
}

@Override
public synchronized void close() {
if (closed) return;
doClose();
if (unlinkOnClose) doUnlink();
closed = true;
}
}
68 changes: 12 additions & 56 deletions src/main/java/org/apposed/appose/shm/ShmLinux.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,10 @@

/**
* Linux-specific shared memory implementation.
* <p>
* TODO separate unlink and close
* </p>
*
* @author Carlos Garcia Lopez de Haro
* @author Tobias Pietzsch
* @author Curtis Rueden
*/
public class ShmLinux implements ShmFactory {

Expand All @@ -62,35 +60,15 @@ public SharedMemory create(final String name, final boolean create, final int si
return new SharedMemoryLinux(name, create, size);
}

private static class SharedMemoryLinux implements SharedMemory {
private static class SharedMemoryLinux extends ShmBase {

/**
* File descriptor
*/
/** File descriptor. */
private final int fd;

/**
* Size in bytes
*/
private final int size;

/**
* Pointer referencing the shared memory
*/
private final Pointer pointer;

/**
* Unique name that identifies the shared memory segment.
*/
private final String name;

/**
* Whether the memory block has been closed and unlinked
*/
private boolean unlinked = false;

// name without leading slash
private SharedMemoryLinux(final String name, final boolean create, final int size) {
super(name, create, size, pointer);

String shm_name;
long prevSize;
if (name == null) {
Expand Down Expand Up @@ -130,42 +108,21 @@ private SharedMemoryLinux(final String name, final boolean create, final int siz
}

@Override
public String name() {
return name;
}

@Override
public Pointer pointer() {
return pointer;
}

@Override
public long size() {
return size;
protected void doUnlink() {
LibRtOrC.shm_unlink(name());
}

/**
* Unmap and close the shared memory. Necessary to eliminate the shared memory block
*/
@Override
public synchronized void close() {
if (unlinked) {
return;
}

protected void doClose() {
// Unmap the shared memory
if (this.pointer != Pointer.NULL && LibRtOrC.munmap(this.pointer, size) == -1) {
if (pointer() != Pointer.NULL && LibRtOrC.munmap(pointer(), size()) == -1) {
throw new RuntimeException("munmap failed. Errno: " + Native.getLastError());
}

// Close the file descriptor
if (LibRtOrC.close(this.fd) == -1) {
throw new RuntimeException("close failed. Errno: " + Native.getLastError());
}

// Unlink the shared memory object
LibRtOrC.shm_unlink(this.name);
unlinked = true;
}

/**
Expand Down Expand Up @@ -206,14 +163,13 @@ private static long getSHMSize(final int shmFd) {
public String toString() {
return "ShmLinux{" +
"fd=" + fd +
", size=" + size +
", pointer=" + pointer +
", name='" + name + '\'' +
", size=" + size() +
", pointer=" + pointer() +
", name='" + name() + '\'' +
", unlinked=" + unlinked +
'}';
}


private static class LibRtOrC {

/**
Expand Down
Loading

0 comments on commit 3b4c83c

Please sign in to comment.