Skip to content

Commit 13ffa1c

Browse files
authored
Apply timeout logging to the task scheduler (#4948)
Activate the logging via log:set DEBUG org.openhab.core.internal.common.WrappedScheduledExecutorService Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
1 parent b8e3dd0 commit 13ffa1c

1 file changed

Lines changed: 103 additions & 0 deletions

File tree

bundles/org.openhab.core/src/main/java/org/openhab/core/internal/common/WrappedScheduledExecutorService.java

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,17 @@
1212
*/
1313
package org.openhab.core.internal.common;
1414

15+
import java.time.Duration;
16+
import java.time.Instant;
17+
import java.util.Objects;
18+
import java.util.concurrent.Callable;
1519
import java.util.concurrent.CancellationException;
1620
import java.util.concurrent.ExecutionException;
1721
import java.util.concurrent.Future;
22+
import java.util.concurrent.ScheduledFuture;
1823
import java.util.concurrent.ScheduledThreadPoolExecutor;
1924
import java.util.concurrent.ThreadFactory;
25+
import java.util.concurrent.TimeUnit;
2026

2127
import org.eclipse.jdt.annotation.NonNullByDefault;
2228
import org.eclipse.jdt.annotation.Nullable;
@@ -38,10 +44,81 @@ public class WrappedScheduledExecutorService extends ScheduledThreadPoolExecutor
3844

3945
final Logger logger = LoggerFactory.getLogger(WrappedScheduledExecutorService.class);
4046

47+
private static final Duration DEFAULT_TIMEOUT = Duration.ofMillis(5000);
48+
4149
public WrappedScheduledExecutorService(int corePoolSize, ThreadFactory threadFactory) {
4250
super(corePoolSize, threadFactory);
4351
}
4452

53+
/**
54+
* A base class that checks the time a scheduled task takes to complete, and if it takes too long,
55+
* it outputs a log message with the stack trace from whence the task was originally scheduled.
56+
*/
57+
private abstract class TimedAbstractTask {
58+
private final Exception stackTraceHolder;
59+
private Instant timeout;
60+
61+
protected TimedAbstractTask() {
62+
this.stackTraceHolder = new Exception();
63+
this.timeout = Instant.MAX;
64+
}
65+
66+
protected void clockStart() {
67+
timeout = Instant.now().plus(DEFAULT_TIMEOUT);
68+
}
69+
70+
protected void clockStop() {
71+
if (Instant.now().isAfter(timeout)) {
72+
logger.debug("Scheduled task took more than {}; it was created here: ", DEFAULT_TIMEOUT,
73+
stackTraceHolder);
74+
}
75+
}
76+
}
77+
78+
/**
79+
* A running time checker for a {@link Runnable}
80+
*/
81+
private class TimedRunnable extends TimedAbstractTask implements Runnable {
82+
private final Runnable runnable;
83+
84+
protected TimedRunnable(@Nullable Runnable runnable) {
85+
super();
86+
this.runnable = Objects.requireNonNull(runnable);
87+
}
88+
89+
@Override
90+
public void run() {
91+
try {
92+
clockStart();
93+
runnable.run();
94+
} finally {
95+
clockStop();
96+
}
97+
}
98+
}
99+
100+
/**
101+
* A running time checker for a {@link Callable}
102+
*/
103+
private class TimedCallable<V> extends TimedAbstractTask implements Callable<V> {
104+
private final Callable<V> callable;
105+
106+
protected TimedCallable(@Nullable Callable<V> callable) {
107+
super();
108+
this.callable = Objects.requireNonNull(callable);
109+
}
110+
111+
@Override
112+
public V call() throws Exception {
113+
try {
114+
clockStart();
115+
return callable.call();
116+
} finally {
117+
clockStop();
118+
}
119+
}
120+
}
121+
45122
@Override
46123
protected void afterExecute(@Nullable Runnable r, @Nullable Throwable t) {
47124
super.afterExecute(r, t);
@@ -68,4 +145,30 @@ protected void afterExecute(@Nullable Runnable r, @Nullable Throwable t) {
68145
logger.warn("Scheduled runnable ended with an exception: ", actualThrowable);
69146
}
70147
}
148+
149+
@Override
150+
public ScheduledFuture<?> schedule(@Nullable Runnable runnable, long delay, @Nullable TimeUnit unit) {
151+
Runnable r = logger.isDebugEnabled() ? new TimedRunnable(runnable) : runnable;
152+
return super.schedule(r, delay, unit);
153+
}
154+
155+
@Override
156+
public ScheduledFuture<?> scheduleAtFixedRate(@Nullable Runnable runnable, long initialDelay, long period,
157+
@Nullable TimeUnit unit) {
158+
Runnable r = logger.isDebugEnabled() ? new TimedRunnable(runnable) : runnable;
159+
return super.scheduleAtFixedRate(r, initialDelay, period, unit);
160+
}
161+
162+
@Override
163+
public ScheduledFuture<?> scheduleWithFixedDelay(@Nullable Runnable runnable, long initialDelay, long delay,
164+
@Nullable TimeUnit unit) {
165+
Runnable r = logger.isDebugEnabled() ? new TimedRunnable(runnable) : runnable;
166+
return super.scheduleWithFixedDelay(r, initialDelay, delay, unit);
167+
}
168+
169+
@Override
170+
public <V> ScheduledFuture<V> schedule(@Nullable Callable<V> callable, long delay, @Nullable TimeUnit unit) {
171+
Callable<V> c = logger.isDebugEnabled() ? new TimedCallable<>(callable) : callable;
172+
return super.schedule(c, delay, unit);
173+
}
71174
}

0 commit comments

Comments
 (0)