Add foundation for event object reuse JEP-349-branch
authoregahlin
Mon, 27 May 2019 18:33:13 +0200
branchJEP-349-branch
changeset 57376 8e8a06a3059c
parent 57375 3efa9b992c4d
child 57377 528d85d2fce5
Add foundation for event object reuse
src/jdk.jfr/share/classes/jdk/jfr/consumer/ChunkParser.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/EventDirectoryStream.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/EventFileStream.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/EventParser.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/EventSet.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ChunkParser.java	Fri May 24 20:51:28 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/ChunkParser.java	Mon May 27 18:33:13 2019 +0200
@@ -61,9 +61,15 @@
     private LongMap<Parser> parsers;
     private boolean chunkFinished;
     private InternalEventFilter eventFilter = InternalEventFilter.ACCEPT_ALL;
+    private boolean reuse;
 
-    public ChunkParser(RecordingInput input) throws IOException {
+    public ChunkParser(RecordingInput input, boolean reuse) throws IOException {
         this(new ChunkHeader(input), null, 500);
+       this.reuse = reuse;
+    }
+
+    public void setReuse(boolean resue) {
+        this.reuse = resue;
     }
 
     private ChunkParser(ChunkHeader header, ChunkParser previous, long pollInterval) throws IOException {
@@ -108,10 +114,13 @@
         return this.eventFilter;
     }
 
-    private void updateParserFilters() {
+    private void updateParsers() {
         parsers.forEach(p -> {
             if (p instanceof EventParser) {
                 EventParser ep = (EventParser) p;
+                if (reuse) {
+                    ep.setReuse(true);
+                }
                 long threshold = eventFilter.getThreshold(ep.getEventType().getName());
                 if (threshold >= 0) {
                     ep.setEnabled(true);
@@ -152,7 +161,7 @@
                 ParserFactory factory = new ParserFactory(metadata, constantLookups, timeConverter);
                 parsers = factory.getParsers();
                 typeMap = factory.getTypeMap();
-                updateParserFilters();
+                updateParsers();
             }
             if (contantPosition != chunkHeader.getConstantPoolPosition()) {
                 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found new constant pool data. Filling up pools with new values");
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventDirectoryStream.java	Fri May 24 20:51:28 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventDirectoryStream.java	Mon May 27 18:33:13 2019 +0200
@@ -102,6 +102,10 @@
             }
             runCloseActions();
         }
+
+        public void setReuse(boolean reuse) {
+            // ignore hint
+        }
     }
 
     private final EventSetConsumer eventConsumer;
@@ -172,4 +176,9 @@
     public void awaitTermination() {
         eventConsumer.awaitTermination(Duration.ofMillis(0));
     }
+
+    @Override
+    public void setReuse(boolean reuse) {
+        eventConsumer.setReuse(reuse);
+    }
 }
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventFileStream.java	Fri May 24 20:51:28 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventFileStream.java	Mon May 27 18:33:13 2019 +0200
@@ -45,8 +45,7 @@
     private final static class FileEventConsumer extends EventConsumer {
         private final RecordingInput input;
         private ChunkParser chunkParser;
-        private RecordedEvent event;
-        private boolean moreEvents = true;
+        private boolean reuse = true;
 
         public FileEventConsumer(AccessControlContext acc, RecordingInput input) throws IOException {
             super(acc);
@@ -55,12 +54,14 @@
 
         @Override
         public void process() throws Exception {
-            chunkParser = new ChunkParser(input);
-            while (moreEvents) {
+            chunkParser = new ChunkParser(input, reuse);
+            chunkParser.setReuse(reuse);
+            RecordedEvent event;
+            while (true) {
                 event = chunkParser.readEvent();
                 if (event == null) {
-                    findNext();
-                    if (!moreEvents) {
+                    event = findNext();
+                    if (event == null) {
                         return;
                     }
                 }
@@ -68,18 +69,24 @@
             }
         }
 
-        private void findNext() throws IOException {
+        private RecordedEvent findNext() throws IOException {
+            RecordedEvent event = null;
             while (event == null) {
-                if (chunkParser == null) {
-                    chunkParser = new ChunkParser(input);
-                } else if (!chunkParser.isLastChunk()) {
-                    chunkParser = chunkParser.nextChunkParser();
-                } else {
-                    moreEvents = false;
-                    return;
+                if (chunkParser.isLastChunk()) {
+                    return null;
                 }
+                chunkParser = chunkParser.nextChunkParser();
                 event = chunkParser.readEvent();
             }
+            return event;
+        }
+
+        public void setReuse(boolean reuse) {
+            if (chunkParser == null) {
+                this.reuse = reuse;
+            } else {
+                chunkParser.setReuse(reuse);
+            }
         }
     }
 
@@ -139,6 +146,10 @@
         eventConsumer.start(0);
     }
 
+    public void setReuse(boolean reuse) {
+        eventConsumer.setReuse(reuse);
+    }
+
     @Override
     public void startAsync() {
         eventConsumer.startAsync(0);
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventParser.java	Fri May 24 20:51:28 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventParser.java	Mon May 27 18:33:13 2019 +0200
@@ -46,8 +46,10 @@
     private final boolean hasDuration;
     private final List<ValueDescriptor> valueDescriptors;
     private final int startIndex;
+    private final RecordedEvent event;
     private long thresholdTicks = -1;
     private boolean enabled = true;
+    private boolean reuse;
 
     EventParser(TimeConverter timeConverter, EventType type, Parser[] parsers) {
         this.timeConverter = timeConverter;
@@ -56,6 +58,7 @@
         this.hasDuration = type.getField(FIELD_DURATION) != null;
         this.startIndex = hasDuration ? 2 : 1;
         this.valueDescriptors = type.getFields();
+        this.event = new RecordedEvent(type, valueDescriptors, new Object[parsers.length], 0L, 0L, timeConverter);
     }
 
     public EventType getEventType() {
@@ -84,24 +87,44 @@
                     return null;
                 }
             }
-            Object[] values = new Object[parsers.length];
-            for (int i = startIndex; i < parsers.length; i++) {
-                values[i] = parsers[i].parse(input);
-            }
-            values[0] = startTicks;
-            if (hasDuration) {
-                values[1] = Long.valueOf(durationTicks);
-            }
-            long startTime = timeConverter.convertTimestamp(startTicks);
-            if (hasDuration) {
-                long endTime = timeConverter.convertTimestamp(startTicks + durationTicks);
-                return new RecordedEvent(eventType, valueDescriptors, values, startTime, endTime, timeConverter);
+            if (reuse) {
+                Object[] values = event.objects;
+                for (int i = startIndex; i < parsers.length; i++) {
+                    values[i] = parsers[i].parse(input);
+                }
+                values[0] = startTicks;
+                if (hasDuration) {
+                    values[1] = Long.valueOf(durationTicks);
+                }
+                long startTime = timeConverter.convertTimestamp(startTicks);
+                if (hasDuration) {
+                    event.startTime = startTime;
+                    event.endTime = timeConverter.convertTimestamp(startTicks + durationTicks);
+                    return event;
+                } else {
+                    event.startTime = startTime;
+                    event.endTime = startTime;
+                    return event;
+                }
             } else {
-                return new RecordedEvent(eventType, valueDescriptors, values, startTime, startTime, timeConverter);
+                Object[] values = new Object[parsers.length];
+                for (int i = startIndex; i < parsers.length; i++) {
+                    values[i] = parsers[i].parse(input);
+                }
+                values[0] = startTicks;
+                if (hasDuration) {
+                    values[1] = Long.valueOf(durationTicks);
+                }
+                long startTime = timeConverter.convertTimestamp(startTicks);
+                if (hasDuration) {
+                    long endTime = timeConverter.convertTimestamp(startTicks + durationTicks);
+                    return new RecordedEvent(eventType, valueDescriptors, values, startTime, endTime, timeConverter);
+                } else {
+                    return new RecordedEvent(eventType, valueDescriptors, values, startTime, startTime, timeConverter);
+                }
             }
         }
-        return null;
-
+        return event;
     }
 
     @Override
@@ -109,4 +132,8 @@
         throw new InternalError("Should not call this method. More efficent to read event size and skip ahead");
     }
 
+    public void setReuse(boolean reuse) {
+        this.reuse = reuse;
+    }
+
 }
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventSet.java	Fri May 24 20:51:28 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventSet.java	Mon May 27 18:33:13 2019 +0200
@@ -159,7 +159,7 @@
     // held with lock
     private void addSegment(int index) throws IOException {
         if (chunkParser == null) {
-            chunkParser = new ChunkParser(new RecordingInput(path.toFile()));
+            chunkParser = new ChunkParser(new RecordingInput(path.toFile()), false);
         }
         if (dirtyFilter) {
             chunkParser.setParserFilter(globalFilter);
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java	Fri May 24 20:51:28 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java	Mon May 27 18:33:13 2019 +0200
@@ -45,8 +45,8 @@
      */
     public static EventStream openRepository(Path directory) throws IOException {
         throw new UnsupportedOperationException("Not yet implemented");
-//       AccessControlContext acc = AccessController.getContext();
-//       return new EventDirectoryStream(acc);
+        // AccessControlContext acc = AccessController.getContext();
+        // return new EventDirectoryStream(acc);
     }
 
     /**
@@ -55,11 +55,12 @@
      * @param file location of the file, not {@code null}
      * @return an event stream, not {@code null}
      *
-     * @throws IOException if a stream can't be opened,or an I/O error occurs during reading
+     * @throws IOException if a stream can't be opened,or an I/O error occurs
+     *         during reading
      */
     public static EventStream openFile(Path file) throws IOException {
         throw new UnsupportedOperationException("Not yet implemented");
-//      return new EventFileStream(file);
+        // return new EventFileStream(file);
     }
 
     /**
@@ -67,24 +68,23 @@
      *
      * @param file location of the file, not {@code null}
      *
-     * @param the start start time for the stream, or {@code null} to get data from
-     *        the beginning of the
+     * @param the start start time for the stream, or {@code null} to get data
+     *        from the beginning of the
      *
-     * @param the end end time for the stream, or {@code null} to get data until the
-     *        end.
+     * @param the end end time for the stream, or {@code null} to get data until
+     *        the end.
      *
      * @throws IllegalArgumentException if {@code end} happens before
      *         {@code start}
      *
-     * @throws IOException if a stream can't be opened,or an I/O error occurs during reading
+     * @throws IOException if a stream can't be opened,or an I/O error occurs
+     *         during reading
      */
     public static EventStream openFile(Path file, Instant from, Instant to) throws IOException {
         throw new UnsupportedOperationException("Not yet implemented");
-//      return new EventFileStream(file);
+        // return new EventFileStream(file);
     }
 
-
-
     /**
      * Performs an action on all events in the stream.
      *
@@ -140,6 +140,19 @@
     boolean remove(Object action);
 
     /**
+     * Hint that the the event object in an {@link #onEvent(Consumer)} action
+     * may be reused.
+     * <p>
+     * If reuse is set to
+     * {@code true), a callback should not keep a reference to the event object
+     * after the callback from {@code onEvent} has returned.
+     * <p>
+     * By default reuse is {@code true}
+     *
+     */
+    public void setReuse(boolean reuse);
+
+    /**
      * Starts processing events in the stream.
      * <p>
      * All actions will performed on this stream will happen in the current
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java	Fri May 24 20:51:28 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java	Mon May 27 18:33:13 2019 +0200
@@ -40,9 +40,8 @@
  */
 public final class RecordedEvent extends RecordedObject {
     private final EventType eventType;
-    private final long startTime;
-    // package private needed for efficient sorting
-    final long endTime;
+    long startTime;
+    long endTime;
 
     // package private
     RecordedEvent(EventType type, List<ValueDescriptor> vds, Object[] values, long startTime, long endTime, TimeConverter timeConverter) {
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java	Fri May 24 20:51:28 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java	Mon May 27 18:33:13 2019 +0200
@@ -95,7 +95,7 @@
         }
     }
 
-    private final Object[] objects;
+    final Object[] objects;
     private final List<ValueDescriptor> descriptors;
     private final TimeConverter timeConverter;
 
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java	Fri May 24 20:51:28 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java	Mon May 27 18:33:13 2019 +0200
@@ -237,7 +237,7 @@
     private void findNext() throws IOException {
         while (nextEvent == null) {
             if (chunkParser == null) {
-                chunkParser = new ChunkParser(input);
+                chunkParser = new ChunkParser(input, false);
             } else if (!chunkParser.isLastChunk()) {
                 chunkParser = chunkParser.nextChunkParser();
             } else {
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java	Fri May 24 20:51:28 2019 +0200
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java	Mon May 27 18:33:13 2019 +0200
@@ -296,4 +296,9 @@
     public void awaitTermination() {
         stream.awaitTermination();
     }
+
+    @Override
+    public void setReuse(boolean reuse) {
+     // hint is ignored
+    }
 }