diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLiteDB.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLiteDB.java index 7db8118d3..3d105ec3d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLiteDB.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLiteDB.java @@ -59,7 +59,7 @@ public class SQLiteDB extends SQLDB { * one thread closing the connection while another is executing a statement as * that might lead to a SIGSEGV signal JVM crash. */ - private final SemaphoreAccessCounter connectionLock = new SemaphoreAccessCounter(); + private final SemaphoreAccessCounter connectionLock; private Constructor connectionConstructor; @@ -76,6 +76,7 @@ public class SQLiteDB extends SQLDB { super(() -> serverInfo.get().getServerUUID(), locale, config, files, runnableFactory, logger, errorLogger); dbName = databaseFile.getName(); this.databaseFile = databaseFile; + connectionLock = new SemaphoreAccessCounter(config); } @Override diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/SemaphoreAccessCounter.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/SemaphoreAccessCounter.java index 0840354d6..27aef4111 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/SemaphoreAccessCounter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/SemaphoreAccessCounter.java @@ -16,24 +16,65 @@ */ package com.djrapitops.plan.utilities; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.PluginSettings; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; public class SemaphoreAccessCounter { + private final PlanConfig config; + private final AtomicInteger accessCounter; private final Object lockObject; + private final Collection holds = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private AtomicBoolean logHolds = null; - public SemaphoreAccessCounter() { + public SemaphoreAccessCounter(PlanConfig config) { + this.config = config; accessCounter = new AtomicInteger(0); lockObject = new Object(); } + @NotNull + private static String getAccessingThing() { + boolean previousWasAccess = false; + List accessors = new ArrayList<>(); + for (StackTraceElement e : Thread.currentThread().getStackTrace()) { + if (previousWasAccess) { + accessors.add(e); + previousWasAccess = false; + } + String call = e.getClassName() + "." + e.getMethodName(); + if ("com.djrapitops.plan.storage.database.SQLDB.query".equals(call) + || "com.djrapitops.plan.storage.database.SQLDB.executeTransaction".equals(call)) { + previousWasAccess = true; + } + } + return accessors.toString(); + } + public void enter() { accessCounter.incrementAndGet(); + + if (logHolds == null) logHolds = new AtomicBoolean(config.isTrue(PluginSettings.DEV_MODE)); + if (logHolds.get()) holds.add(getAccessingThing()); } public void exit() { synchronized (lockObject) { + if (logHolds == null) logHolds = new AtomicBoolean(config.isTrue(PluginSettings.DEV_MODE)); + if (logHolds.get()) holds.remove(getAccessingThing()); + int value = accessCounter.decrementAndGet(); if (value == 0) { lockObject.notifyAll(); @@ -45,6 +86,7 @@ public class SemaphoreAccessCounter { while (accessCounter.get() > 0) { synchronized (lockObject) { try { + logAccess(); lockObject.wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); @@ -52,4 +94,18 @@ public class SemaphoreAccessCounter { } } } + + private void logAccess() { + if (logHolds != null && logHolds.get()) { + Logger logger = Logger.getLogger("Plan"); + if (logger == null) logger = Logger.getGlobal(); + + if (logger.isLoggable(Level.INFO) && !holds.isEmpty()) { + logger.log(Level.INFO, "DEBUG - Following call sites are holding connection:"); + for (String hold : holds) { + logger.log(Level.INFO, String.format("DEBUG - %s", hold)); + } + } + } + } }