fix(shadow): repair field routing and harden evidence collection
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
package com.ruoyi.shadow.kernel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.LongSupplier;
|
||||
|
||||
/** One bounded diagnostic snapshot per poll, including polls with no task evidence. */
|
||||
final class ShadowCycleDiagnostics {
|
||||
private final LongSupplier ticker;
|
||||
private final long started;
|
||||
private long phaseStarted;
|
||||
private String phase = "QUEUED";
|
||||
private final Map<String, Long> phaseNanos = new LinkedHashMap<String, Long>();
|
||||
private Map<String, Object> completed;
|
||||
private boolean cancelled;
|
||||
|
||||
ShadowCycleDiagnostics() { this(System::nanoTime); }
|
||||
|
||||
ShadowCycleDiagnostics(LongSupplier ticker) {
|
||||
this.ticker = ticker;
|
||||
this.started = ticker.getAsLong();
|
||||
this.phaseStarted = started;
|
||||
}
|
||||
|
||||
synchronized void enter(String next) {
|
||||
if (cancelled || Thread.currentThread().isInterrupted()) {
|
||||
throw new CancellationException("SHADOW_CYCLE_CANCELLED");
|
||||
}
|
||||
if (completed != null) return;
|
||||
long now = ticker.getAsLong();
|
||||
accumulate(now);
|
||||
phase = next;
|
||||
phaseStarted = now;
|
||||
}
|
||||
|
||||
synchronized Map<String, Object> finish(String outcome, Thread worker) {
|
||||
if (completed != null) return completed;
|
||||
long now = ticker.getAsLong();
|
||||
accumulate(now);
|
||||
Map<String, Object> snapshot = new LinkedHashMap<String, Object>();
|
||||
snapshot.put("outcome", outcome);
|
||||
snapshot.put("phase", phase);
|
||||
snapshot.put("elapsedMillis", TimeUnit.NANOSECONDS.toMillis(now - started));
|
||||
Map<String, Long> timings = new LinkedHashMap<String, Long>();
|
||||
phaseNanos.forEach((key, value) -> timings.put(key, TimeUnit.NANOSECONDS.toMillis(value)));
|
||||
snapshot.put("phaseMillis", Collections.unmodifiableMap(timings));
|
||||
if (worker != null) {
|
||||
snapshot.put("workerState", worker.getState().name());
|
||||
List<String> stack = new ArrayList<String>();
|
||||
StackTraceElement[] frames = worker.getStackTrace();
|
||||
for (int i = 0; i < Math.min(16, frames.length); i++) stack.add(frames[i].toString());
|
||||
snapshot.put("workerStack", Collections.unmodifiableList(stack));
|
||||
}
|
||||
completed = Collections.unmodifiableMap(snapshot);
|
||||
return completed;
|
||||
}
|
||||
|
||||
synchronized Map<String, Object> cancel(String outcome, Thread worker) {
|
||||
cancelled = true;
|
||||
return finish(outcome, worker);
|
||||
}
|
||||
|
||||
private void accumulate(long now) {
|
||||
phaseNanos.merge(phase, now - phaseStarted, Long::sum);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user