Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ public final boolean remove(final Class<? extends IndexGroup<E>> categoryType)

private boolean internalRemoveGroup(final Class<?> categoryType)
{
final IndexGroup.Internal<E> found;
synchronized(this.parentMap())
{
if(this.parent.isReadOnly())
Expand All @@ -644,59 +645,120 @@ private boolean internalRemoveGroup(final Class<?> categoryType)
);
}

IndexGroup.Internal<E> found = null;
IndexGroup.Internal<E> match = null;
// exact class match is checked first, then the general (e.g. interface) type, mirroring #get.
for(final IndexGroup.Internal<E> indexGroup : this.indexGroups)
{
if(indexGroup.getClass() == categoryType)
{
found = indexGroup;
match = indexGroup;
break;
}
}
if(found == null)
if(match == null)
{
for(final IndexGroup.Internal<E> indexGroup : this.indexGroups)
{
if(categoryType.isAssignableFrom(indexGroup.getClass()))
{
found = indexGroup;
match = indexGroup;
break;
}
}
}
if(found == null)
if(match == null)
{
return false;
}
if(found instanceof BitmapIndices)
if(match instanceof BitmapIndices)
{
throw new IllegalArgumentException(
"The bitmap index group cannot be removed (it hosts the unique and custom constraints); "
+ "use BitmapIndices#removeIndex(String) to remove individual bitmap indices."
);
}
found = match;
}

// release resources
if(found instanceof Closeable)
// Release resources outside the parentMap monitor: a group's close() may acquire its own internal
// lock (e.g. a vector index's builder write-lock) while a background task holds that lock and waits
// for the parentMap monitor, so closing under the monitor could dead-lock. Drop the group from the
// registry only after close() succeeds, so a failing close leaves it registered (and re-closeable)
// rather than detached-but-unclosed.
if(found instanceof Closeable)
{
try
{
try
((Closeable)found).close();
}
catch(final IOException e)
{
throw new RuntimeException(
"Failed to close index group \"" + found.getClass().getName() + "\" while removing it.",
e
);
}
}

synchronized(this.parentMap())
{
this.indexGroups.removeOne(found);
this.markStateChangeInstance();
}
return true;
}

/**
* Closes every {@link Closeable} index group, releasing background threads and auxiliary
* resources, <b>without</b> removing the groups or mutating persistent state. Invoked when the
* owning storage is shut down (see {@link GigaMap.Default#releaseOnShutdown()}). Best-effort: a
* failure of one group does not prevent the others from being closed; the first failure is
* rethrown (with the rest suppressed) once all groups have been attempted.
* <p>
* The closeable groups are collected under the {@code parentMap} monitor but closed <b>outside</b>
* of it: a group's {@code close()} may acquire its own internal lock (e.g. a vector index's builder
* write-lock) while a background task holds that lock and waits for the {@code parentMap} monitor,
* so holding the monitor across {@code close()} would dead-lock.
*/
Comment thread
fh-ms marked this conversation as resolved.
final void closeAllGroups()
{
final BulkList<Closeable> closeables = BulkList.New();
synchronized(this.parentMap())
{
for(final IndexGroup.Internal<E> indexGroup : this.indexGroups)
{
if(indexGroup instanceof Closeable)
{
((Closeable)found).close();
closeables.add((Closeable)indexGroup);
}
catch(final IOException e)
}
}

RuntimeException problem = null;
for(final Closeable closeable : closeables)
{
try
{
closeable.close();
}
catch(final Exception e)
{
if(problem == null)
{
throw new RuntimeException(
"Failed to close index group \"" + found.getClass().getName()
+ "\" while removing it.",
problem = new RuntimeException(
"Failed to close one or more index groups on storage shutdown.",
e
);
}
else
{
problem.addSuppressed(e);
}
}

this.indexGroups.removeOne(found);
this.markStateChangeInstance();
return true;
}
if(problem != null)
{
throw problem;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1421,7 +1421,7 @@ public static <E> GigaMap<E> New(

// Unpersistable to force a custom type handler due to required initialization call.
public class Default<E>
implements Internal<E>, EntityResolver<E>, Unpersistable, PersistenceCommitListener
implements Internal<E>, EntityResolver<E>, Unpersistable, PersistenceCommitListener, PersistenceShutdownReleasable
{
static BinaryTypeHandler<GigaMap.Default<?>> provideTypeHandler()
{
Expand Down Expand Up @@ -1615,6 +1615,16 @@ public final GigaIndices<E> index()
{
return this.indices;
}

@Override
public final void releaseOnShutdown()
{
// Closes Closeable index groups (e.g. vector indices: stops their background threads and
// frees off-heap/auxiliary-disk resources) when the owning storage is shut down, so callers
// need not close indices explicitly. Does not mutate the persistent graph; indices are
// transient and rebuilt on the next storage start.
this.indices.closeAllGroups();
}

@Override
public final GigaConstraints<E> constraints()
Expand Down
Loading
Loading