8253505: JFR: onFlush invoked out of order with a sorted event stream
Reviewed-by: mgronlun
This commit is contained in:
parent
0148adf20e
commit
4307fa68b7
@ -103,6 +103,9 @@ public final class RecordingFile implements Closeable {
|
||||
isLastEventInChunk = false;
|
||||
RecordedEvent event = nextEvent;
|
||||
nextEvent = chunkParser.readEvent();
|
||||
while (nextEvent == ChunkParser.FLUSH_MARKER) {
|
||||
nextEvent = chunkParser.readEvent();
|
||||
}
|
||||
if (nextEvent == null) {
|
||||
isLastEventInChunk = true;
|
||||
findNext();
|
||||
@ -251,6 +254,9 @@ public final class RecordingFile implements Closeable {
|
||||
return;
|
||||
}
|
||||
nextEvent = chunkParser.readEvent();
|
||||
while (nextEvent == ChunkParser.FLUSH_MARKER) {
|
||||
nextEvent = chunkParser.readEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,6 +233,14 @@ public abstract class AbstractEventStream implements EventStream {
|
||||
return flushOperation;
|
||||
}
|
||||
|
||||
|
||||
final protected void onFlush() {
|
||||
Runnable r = getFlushOperation();
|
||||
if (r != null) {
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
|
||||
private void startInternal(long startNanos) {
|
||||
synchronized (streamConfiguration) {
|
||||
if (streamConfiguration.started) {
|
||||
@ -291,7 +299,7 @@ public abstract class AbstractEventStream implements EventStream {
|
||||
streamConfiguration.addMetadataAction(action);
|
||||
}
|
||||
|
||||
protected final void emitMetadataEvent(ChunkParser parser) {
|
||||
protected final void onMetadata(ChunkParser parser) {
|
||||
if (parser.hasStaleMetadata()) {
|
||||
if (dispatcher.hasMetadataHandler()) {
|
||||
List<EventType> ce = parser.getEventTypes();
|
||||
|
@ -91,6 +91,7 @@ public final class ChunkParser {
|
||||
return (mask & flags) != 0;
|
||||
}
|
||||
}
|
||||
public final static RecordedEvent FLUSH_MARKER = JdkJfrConsumer.instance().newRecordedEvent(null, null, 0L, 0L);
|
||||
|
||||
private static final long CONSTANT_POOL_TYPE_ID = 1;
|
||||
private static final String CHUNKHEADER = "jdk.types.ChunkHeader";
|
||||
@ -104,7 +105,6 @@ public final class ChunkParser {
|
||||
private LongMap<Parser> parsers;
|
||||
private boolean chunkFinished;
|
||||
|
||||
private Runnable flushOperation;
|
||||
private ParserConfiguration configuration;
|
||||
private volatile boolean closed;
|
||||
private MetadataDescriptor previousMetadata;
|
||||
@ -194,6 +194,9 @@ public final class ChunkParser {
|
||||
RecordedEvent readStreamingEvent() throws IOException {
|
||||
long absoluteChunkEnd = chunkHeader.getEnd();
|
||||
RecordedEvent event = readEvent();
|
||||
if (event == ChunkParser.FLUSH_MARKER) {
|
||||
return null;
|
||||
}
|
||||
if (event != null) {
|
||||
return event;
|
||||
}
|
||||
@ -253,8 +256,9 @@ public final class ChunkParser {
|
||||
// Not accepted by filter
|
||||
} else {
|
||||
if (typeId == 1) { // checkpoint event
|
||||
if (flushOperation != null) {
|
||||
parseCheckpoint();
|
||||
if (CheckPointType.FLUSH.is(parseCheckpointType())) {
|
||||
input.position(pos + size);
|
||||
return FLUSH_MARKER;
|
||||
}
|
||||
} else {
|
||||
if (typeId != 0) { // Not metadata event
|
||||
@ -267,16 +271,11 @@ public final class ChunkParser {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void parseCheckpoint() throws IOException {
|
||||
// Content has been parsed previously. This
|
||||
// is to trigger flush
|
||||
private byte parseCheckpointType() throws IOException {
|
||||
input.readLong(); // timestamp
|
||||
input.readLong(); // duration
|
||||
input.readLong(); // delta
|
||||
byte typeFlags = input.readByte();
|
||||
if (CheckPointType.FLUSH.is(typeFlags)) {
|
||||
flushOperation.run();
|
||||
}
|
||||
return input.readByte();
|
||||
}
|
||||
|
||||
private boolean awaitUpdatedHeader(long absoluteChunkEnd, long filterEnd) throws IOException {
|
||||
@ -451,10 +450,6 @@ public final class ChunkParser {
|
||||
return chunkFinished;
|
||||
}
|
||||
|
||||
public void setFlushOperation(Runnable flushOperation) {
|
||||
this.flushOperation = flushOperation;
|
||||
}
|
||||
|
||||
public long getChunkDuration() {
|
||||
return chunkHeader.getDurationNanos();
|
||||
}
|
||||
|
@ -38,8 +38,6 @@ import jdk.jfr.internal.consumer.ChunkParser.ParserConfiguration;
|
||||
|
||||
final class Dispatcher {
|
||||
|
||||
public final static RecordedEvent FLUSH_MARKER = JdkJfrConsumer.instance().newRecordedEvent(null, null, 0L, 0L);
|
||||
|
||||
final static class EventDispatcher {
|
||||
private final static EventDispatcher[] NO_DISPATCHERS = new EventDispatcher[0];
|
||||
|
||||
|
@ -143,7 +143,7 @@ public class EventDirectoryStream extends AbstractEventStream {
|
||||
long filterEnd = disp.endTime != null ? disp.endNanos : Long.MAX_VALUE;
|
||||
|
||||
while (!isClosed()) {
|
||||
emitMetadataEvent(currentParser);
|
||||
onMetadata(currentParser);
|
||||
while (!isClosed() && !currentParser.isChunkFinished()) {
|
||||
disp = dispatcher();
|
||||
if (disp != lastDisp) {
|
||||
@ -151,7 +151,6 @@ public class EventDirectoryStream extends AbstractEventStream {
|
||||
pc.filterStart = filterStart;
|
||||
pc.filterEnd = filterEnd;
|
||||
currentParser.updateConfiguration(pc, true);
|
||||
currentParser.setFlushOperation(getFlushOperation());
|
||||
lastDisp = disp;
|
||||
}
|
||||
if (disp.parserConfiguration.isOrdered()) {
|
||||
@ -221,9 +220,10 @@ public class EventDirectoryStream extends AbstractEventStream {
|
||||
}
|
||||
sortedCache[index++] = e;
|
||||
}
|
||||
emitMetadataEvent(currentParser);
|
||||
onMetadata(currentParser);
|
||||
// no events found
|
||||
if (index == 0 && currentParser.isChunkFinished()) {
|
||||
onFlush();
|
||||
return;
|
||||
}
|
||||
// at least 2 events, sort them
|
||||
@ -233,6 +233,7 @@ public class EventDirectoryStream extends AbstractEventStream {
|
||||
for (int i = 0; i < index; i++) {
|
||||
c.dispatch(sortedCache[i]);
|
||||
}
|
||||
onFlush();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -240,11 +241,11 @@ public class EventDirectoryStream extends AbstractEventStream {
|
||||
while (true) {
|
||||
RecordedEvent e = currentParser.readStreamingEvent();
|
||||
if (e == null) {
|
||||
emitMetadataEvent(currentParser);
|
||||
onFlush();
|
||||
return true;
|
||||
} else {
|
||||
c.dispatch(e);
|
||||
}
|
||||
onMetadata(currentParser);
|
||||
c.dispatch(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ package jdk.jfr.internal.consumer;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.AccessControlContext;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@ -87,7 +88,7 @@ public final class EventFileStream extends AbstractEventStream {
|
||||
|
||||
currentParser = new ChunkParser(input, disp.parserConfiguration);
|
||||
while (!isClosed()) {
|
||||
emitMetadataEvent(currentParser);
|
||||
onMetadata(currentParser);
|
||||
if (currentParser.getStartNanos() > end) {
|
||||
close();
|
||||
return;
|
||||
@ -96,7 +97,6 @@ public final class EventFileStream extends AbstractEventStream {
|
||||
disp.parserConfiguration.filterStart = start;
|
||||
disp.parserConfiguration.filterEnd = end;
|
||||
currentParser.updateConfiguration(disp.parserConfiguration, true);
|
||||
currentParser.setFlushOperation(getFlushOperation());
|
||||
if (disp.parserConfiguration.isOrdered()) {
|
||||
processOrdered(disp);
|
||||
} else {
|
||||
@ -116,46 +116,42 @@ public final class EventFileStream extends AbstractEventStream {
|
||||
}
|
||||
RecordedEvent event;
|
||||
int index = 0;
|
||||
while (true) {
|
||||
event = currentParser.readEvent();
|
||||
if (event == Dispatcher.FLUSH_MARKER) {
|
||||
emitMetadataEvent(currentParser);
|
||||
dispatchOrdered(c, index);
|
||||
index = 0;
|
||||
continue;
|
||||
while (!currentParser.isChunkFinished()) {
|
||||
while ((event = currentParser.readStreamingEvent()) != null) {
|
||||
if (index == cacheSorted.length) {
|
||||
RecordedEvent[] tmp = cacheSorted;
|
||||
cacheSorted = new RecordedEvent[2 * tmp.length];
|
||||
System.arraycopy(tmp, 0, cacheSorted, 0, tmp.length);
|
||||
}
|
||||
cacheSorted[index++] = event;
|
||||
}
|
||||
|
||||
if (event == null) {
|
||||
emitMetadataEvent(currentParser);
|
||||
dispatchOrdered(c, index);
|
||||
return;
|
||||
}
|
||||
if (index == cacheSorted.length) {
|
||||
RecordedEvent[] tmp = cacheSorted;
|
||||
cacheSorted = new RecordedEvent[2 * tmp.length];
|
||||
System.arraycopy(tmp, 0, cacheSorted, 0, tmp.length);
|
||||
}
|
||||
cacheSorted[index++] = event;
|
||||
dispatchOrdered(c, index);
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchOrdered(Dispatcher c, int index) {
|
||||
onMetadata(currentParser);
|
||||
Arrays.sort(cacheSorted, 0, index, EVENT_COMPARATOR);
|
||||
for (int i = 0; i < index; i++) {
|
||||
c.dispatch(cacheSorted[i]);
|
||||
}
|
||||
onFlush();
|
||||
}
|
||||
|
||||
private void processUnordered(Dispatcher c) throws IOException {
|
||||
onMetadata(currentParser);
|
||||
while (!isClosed()) {
|
||||
RecordedEvent event = currentParser.readEvent();
|
||||
RecordedEvent event = currentParser.readStreamingEvent();
|
||||
if (event == null) {
|
||||
emitMetadataEvent(currentParser);
|
||||
return;
|
||||
}
|
||||
if (event != Dispatcher.FLUSH_MARKER) {
|
||||
c.dispatch(event);
|
||||
onFlush();
|
||||
if (currentParser.isChunkFinished()) {
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
onMetadata(currentParser);
|
||||
c.dispatch(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user