[Minor] Extended LockableObject to log owner of locks on lock timeout

This commit is contained in:
Robert von Burg 2023-02-14 10:10:48 +01:00
parent 2f1923218b
commit 51d8ff6d6c
Signed by: eitch
GPG Key ID: 75DB9C85C74331F7
1 changed files with 35 additions and 17 deletions

View File

@ -20,11 +20,10 @@ import static java.text.MessageFormat.format;
import static li.strolch.utils.helper.StringHelper.formatMillisecondsDuration; import static li.strolch.utils.helper.StringHelper.formatMillisecondsDuration;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.Map; import java.util.Collection;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import li.strolch.utils.helper.StringHelper;
import li.strolch.xmlpers.api.XmlPersistenceException; import li.strolch.xmlpers.api.XmlPersistenceException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -38,12 +37,12 @@ public class LockableObject {
LockableObject.tryLockTime = tryLockTime; LockableObject.tryLockTime = tryLockTime;
} }
private final ReentrantLock lock; private final Lock lock;
protected final String name; protected final String name;
public LockableObject(String name) { public LockableObject(String name) {
this.name = name; this.name = name;
this.lock = new ReentrantLock(true); this.lock = new Lock(true);
} }
public String getName() { public String getName() {
@ -69,19 +68,21 @@ public class LockableObject {
String msg = "Thread {0} failed to acquire lock after {1} for {2}"; //$NON-NLS-1$ String msg = "Thread {0} failed to acquire lock after {1} for {2}"; //$NON-NLS-1$
msg = format(msg, currentThread().getName(), formatMillisecondsDuration(tryLockTime), this); msg = format(msg, currentThread().getName(), formatMillisecondsDuration(tryLockTime), this);
try { Thread owner = lock.getOwner();
logger.error(msg); if (owner == null) {
logger.error("Listing all active threads: "); logger.error(MessageFormat.format("Lock {0} is currently held unknown thread!", this.name));
Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces(); logger.error(lock.toString());
for (Thread thread : allStackTraces.keySet()) { } else {
StackTraceElement[] trace = allStackTraces.get(thread); Exception e = new Exception();
StringBuilder sb = new StringBuilder(); e.setStackTrace(owner.getStackTrace());
for (StackTraceElement traceElement : trace) logger.error(MessageFormat.format("Lock {0} is currently held by {1}", this.name, owner), e);
sb.append("\n\tat ").append(traceElement); }
logger.error("\nThread " + thread.getName() + ":\n" + sb.toString() + "\n");
} logger.error("Threads waiting on this lock are:");
} catch (Exception e) { for (Thread queuedThread : lock.getQueuedThreads()) {
logger.error("Failed to log active threads: " + e.getMessage(), e); Exception e = new Exception();
e.setStackTrace(queuedThread.getStackTrace());
logger.error("\n" + queuedThread.getName(), e);
} }
throw new XmlPersistenceException(msg); throw new XmlPersistenceException(msg);
@ -103,4 +104,21 @@ public class LockableObject {
this.lock.unlock(); this.lock.unlock();
} }
} }
public static class Lock extends ReentrantLock {
public Lock(boolean fair) {
super(fair);
}
@Override
public Thread getOwner() {
return super.getOwner();
}
@Override
public Collection<Thread> getQueuedThreads() {
return super.getQueuedThreads();
}
}
} }