LogLifecycle.java
package io.jstach.rainbowgum;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* A component that has a start and stop.
*/
public interface LogLifecycle extends AutoCloseable {
/**
* Starts a component.
* @param config log config.
*/
public void start(LogConfig config);
@Override
public void close();
}
interface Shutdownable {
public void shutdown();
}
final class ShutdownManager {
private static final ReentrantReadWriteLock staticLock = new ReentrantReadWriteLock();
private static CopyOnWriteArrayList<AutoCloseable> shutdownHooks = new CopyOnWriteArrayList<>();
// we do not need the newer VarHandle because there is only one of these guys
private static AtomicBoolean shutdownHookRegistered = new AtomicBoolean(false);
static void addShutdownHook(AutoCloseable hook) {
staticLock.writeLock().lock();
try {
if (shutdownHookRegistered.compareAndSet(false, true)) {
var thread = new Thread(() -> {
runShutdownHooks();
});
thread.setName("rainbowgum-shutdown");
Runtime.getRuntime().addShutdownHook(thread);
}
shutdownHooks.add(hook);
}
finally {
staticLock.writeLock().unlock();
}
}
@SuppressWarnings("ReferenceEquality")
static void removeShutdownHook(AutoCloseable hook) {
staticLock.writeLock().lock();
try {
shutdownHooks.removeIf(h -> h == hook);
}
finally {
staticLock.writeLock().unlock();
}
}
/*
* This is for unit testing.
*/
static List<AutoCloseable> shutdownHooks() {
return List.copyOf(shutdownHooks);
}
private static void runShutdownHooks() {
/*
* We do not lock here since we are in the shutdown thread luckily shutdownHooks
* is thread safe
*/
var found = Collections.newSetFromMap(new IdentityHashMap<>());
for (var hook : shutdownHooks) {
try {
if (found.add(hook)) {
if (hook instanceof Shutdownable shut) {
shut.shutdown();
}
else {
hook.close();
}
}
}
catch (Exception e) {
MetaLog.error(ShutdownManager.class, e);
}
}
// Help the GC or whatever final cleanup is going on
shutdownHooks.clear();
}
}