diff --git a/src/hotspot/share/jfr/support/jfrIntrinsics.hpp b/src/hotspot/share/jfr/support/jfrIntrinsics.hpp index 6520f3cb00ca0..77affc69926f0 100644 --- a/src/hotspot/share/jfr/support/jfrIntrinsics.hpp +++ b/src/hotspot/share/jfr/support/jfrIntrinsics.hpp @@ -47,7 +47,7 @@ class JfrIntrinsicSupport : AllStatic { #define JFR_HAVE_INTRINSICS #define JFR_TEMPLATES(template) \ - template(jdk_jfr_internal_HiddenWait, "jdk/jfr/internal/HiddenWait") \ + template(jdk_jfr_internal_management_HiddenWait, "jdk/jfr/internal/management/HiddenWait") \ template(jdk_jfr_internal_JVM, "jdk/jfr/internal/JVM") \ template(jdk_jfr_internal_event_EventWriterFactory, "jdk/jfr/internal/event/EventWriterFactory") \ template(jdk_jfr_internal_event_EventConfiguration_signature, "Ljdk/jfr/internal/event/EventConfiguration;") \ diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index ba463231592c5..c509ed691cdb8 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -1442,7 +1442,7 @@ bool ObjectMonitor::check_owner(TRAPS) { static inline bool is_excluded(const Klass* monitor_klass) { assert(monitor_klass != nullptr, "invariant"); NOT_JFR_RETURN_(false); - JFR_ONLY(return vmSymbols::jdk_jfr_internal_HiddenWait() == monitor_klass->name();) + JFR_ONLY(return vmSymbols::jdk_jfr_internal_management_HiddenWait() == monitor_klass->name();) } static void post_monitor_wait_event(EventJavaMonitorWait* event, diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java index 2e600c8c02974..cc0ee05a948a5 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java @@ -31,6 +31,7 @@ import jdk.jfr.Event; import jdk.jfr.internal.event.EventConfiguration; import jdk.jfr.internal.event.EventWriter; +import jdk.jfr.internal.management.HiddenWait; /** * Interface against the JVM. diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVMSupport.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVMSupport.java index 114052ecea250..7fde28a3d21fc 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVMSupport.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVMSupport.java @@ -30,6 +30,7 @@ import jdk.jfr.Recording; import jdk.jfr.internal.event.EventConfiguration; +import jdk.jfr.internal.management.HiddenWait; import jdk.jfr.internal.util.Utils; import jdk.jfr.internal.util.ValueFormatter; @@ -118,7 +119,8 @@ private static void awaitUniqueTimestamp() { lastTimestamp = time; return; } - Utils.takeNap(1); + HiddenWait hiddenWait = new HiddenWait(); + hiddenWait.takeNap(1); } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java index 75be70a0d1dae..1ddae489917b6 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java @@ -49,6 +49,7 @@ import jdk.jfr.ValueDescriptor; import jdk.jfr.internal.consumer.RepositoryFiles; import jdk.jfr.internal.event.EventConfiguration; +import jdk.jfr.internal.management.HiddenWait; import jdk.jfr.internal.periodic.PeriodicEvents; import jdk.jfr.internal.util.Utils; @@ -59,6 +60,7 @@ public final class MetadataRepository { private final Map<String, EventType> nativeEventTypes = LinkedHashMap.newHashMap(150); private final Map<String, EventControl> nativeControls = LinkedHashMap.newHashMap(150); private final SettingsManager settingsManager = new SettingsManager(); + private final HiddenWait threadSleeper = new HiddenWait(); private Constructor<EventConfiguration> cachedEventConfigurationConstructor; private boolean staleMetadata = true; private boolean unregistered; @@ -343,7 +345,7 @@ private void awaitEpochMilliShift() { lastMillis = millis; return; } - Utils.takeNap(1); + threadSleeper.takeNap(1); } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java index 1816d00f345a7..e0dd7fa2ad8d5 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ import jdk.jfr.internal.SecuritySupport; import jdk.jfr.internal.SecuritySupport.SafePath; import jdk.jfr.internal.management.EventByteStream; +import jdk.jfr.internal.management.HiddenWait; import jdk.jfr.internal.management.ManagementSupport; public final class OngoingStream extends EventByteStream { @@ -44,6 +45,7 @@ public final class OngoingStream extends EventByteStream { private final RepositoryFiles repositoryFiles; private final Recording recording; + private final HiddenWait threadSleeper = new HiddenWait(); private final int blockSize; private final long endTimeNanos; private final byte[] headerBytes = new byte[HEADER_SIZE]; @@ -195,19 +197,13 @@ private byte[] readWithHeader(int size) throws IOException { return bytes; } } - takeNap(); + if (!threadSleeper.takeNap(10)) { + throw new IOException("Read operation interrupted"); + } } return EMPTY_ARRAY; } - private void takeNap() throws IOException { - try { - Thread.sleep(10); - } catch (InterruptedException ie) { - throw new IOException("Read operation interrupted", ie); - } - } - private boolean ensureInput() throws IOException { if (input == null) { if (SecuritySupport.getFileSize(new SafePath(path)) < HEADER_SIZE) { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java index 90a03130f4990..69ec73f57fd6f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,8 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.nio.file.Path; + +import jdk.jfr.internal.management.HiddenWait; import jdk.jfr.internal.util.Utils; public final class RecordingInput implements DataInput, AutoCloseable { @@ -67,6 +69,7 @@ public void reset() { } private final int blockSize; private final FileAccess fileAccess; + private final HiddenWait threadSleeper = new HiddenWait(); private long pollCount = 1000; private RandomAccessFile file; private String filename; @@ -453,6 +456,6 @@ public void pollWait() throws IOException { if (pollCount < 0) { throw new IOException("Recording file is stuck in locked stream state."); } - Utils.takeNap(1); + threadSleeper.takeNap(1); } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java index 6ef88ecad3750..3f0611f998173 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java @@ -46,9 +46,10 @@ import jdk.jfr.internal.Logger; import jdk.jfr.internal.Repository; import jdk.jfr.internal.SecuritySupport.SafePath; +import jdk.jfr.internal.management.HiddenWait;; public final class RepositoryFiles { - private static final Object WAIT_OBJECT = new Object(); + private static final HiddenWait WAIT_OBJECT = new HiddenWait(); private static final String DIRECTORY_PATTERN = "DDDD_DD_DD_DD_DD_DD_"; public static void notifyNewFile() { synchronized (WAIT_OBJECT) { @@ -59,7 +60,7 @@ public static void notifyNewFile() { private final FileAccess fileAccess; private final NavigableMap<Long, Path> pathSet = new TreeMap<>(); private final Map<Path, Long> pathLookup = new HashMap<>(); - private final Object waitObject; + private final HiddenWait waitObject; private boolean allowSubDirectory; private volatile boolean closed; private Path repository; @@ -67,7 +68,7 @@ public static void notifyNewFile() { public RepositoryFiles(FileAccess fileAccess, Path repository, boolean allowSubDirectory) { this.repository = repository; this.fileAccess = fileAccess; - this.waitObject = repository == null ? WAIT_OBJECT : new Object(); + this.waitObject = repository == null ? WAIT_OBJECT : new HiddenWait(); this.allowSubDirectory = allowSubDirectory; } @@ -108,7 +109,7 @@ private boolean updatePaths(boolean wait) { // was accessed. Just ignore, and retry later. } if (wait) { - nap(); + waitObject.takeNap(1000); } else { return pathLookup.size() > beforeSize; } @@ -157,16 +158,6 @@ private Path path(long timestamp, boolean wait) { } } - private void nap() { - try { - synchronized (waitObject) { - waitObject.wait(1000); - } - } catch (InterruptedException e) { - // ignore - } - } - private boolean updatePaths() throws IOException, DirectoryIteratorException { boolean foundNew = false; Path repoPath = repository; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/HiddenWait.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/management/HiddenWait.java similarity index 82% rename from src/jdk.jfr/share/classes/jdk/jfr/internal/HiddenWait.java rename to src/jdk.jfr/share/classes/jdk/jfr/internal/management/HiddenWait.java index 26505990332c9..9070faad5a658 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/HiddenWait.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/management/HiddenWait.java @@ -22,11 +22,21 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.jfr.internal; +package jdk.jfr.internal.management; /** * The HiddenWait class is used to exclude jdk.JavaMonitorWait events * from being generated when Object.wait() is called on an object of this type. */ public final class HiddenWait { + + public synchronized boolean takeNap(long timeoutMillis) { + try { + this.wait(timeoutMillis); + return true; + } catch (InterruptedException e) { + // Ok, ignore + return false; + } + } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/management/StreamBarrier.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/management/StreamBarrier.java index 0b3132da2178b..7f1db163b4e3d 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/management/StreamBarrier.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/management/StreamBarrier.java @@ -39,45 +39,57 @@ * processing should not continue. */ public final class StreamBarrier implements Closeable { - + private final HiddenWait lock = new HiddenWait(); private boolean activated = false; private boolean used = false; private long end = Long.MAX_VALUE; // Blocks thread until barrier is deactivated - public synchronized void check() { - while (activated) { - try { - this.wait(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); + public void check() { + synchronized (lock) { + while (activated) { + try { + lock.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } } } } - public synchronized void setStreamEnd(long timestamp) { - end = timestamp; + public void setStreamEnd(long timestamp) { + synchronized(lock) { + end = timestamp; + } } - public synchronized long getStreamEnd() { - return end; + public long getStreamEnd() { + synchronized(lock) { + return end; + } } - public synchronized void activate() { - activated = true; - used = true; + public void activate() { + synchronized (lock) { + activated = true; + used = true; + } } @Override public synchronized void close() throws IOException { - activated = false; - this.notifyAll(); + synchronized (lock) { + activated = false; + lock.notifyAll(); + } } /** * Returns {@code true) if barrier is, or has been, in active state, {@code false) otherwise. */ - public synchronized boolean used() { - return used; + public boolean used() { + synchronized (lock) { + return used; + } } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java index 5e04a25fe7ddf..ae83727096a2a 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java @@ -48,19 +48,19 @@ import jdk.jfr.Event; import jdk.jfr.EventType; import jdk.jfr.RecordingState; -import jdk.jfr.internal.HiddenWait; import jdk.jfr.internal.LogLevel; import jdk.jfr.internal.LogTag; import jdk.jfr.internal.Logger; import jdk.jfr.internal.MirrorEvent; import jdk.jfr.internal.SecuritySupport; import jdk.jfr.internal.Type; +import jdk.jfr.internal.management.HiddenWait; import jdk.jfr.internal.settings.PeriodSetting; import jdk.jfr.internal.settings.StackTraceSetting; import jdk.jfr.internal.settings.ThresholdSetting; public final class Utils { - private static final Object flushObject = new Object(); + private static final HiddenWait flushObject = new HiddenWait(); private static final String LEGACY_EVENT_NAME_PREFIX = "com.oracle.jdk."; /** @@ -351,17 +351,6 @@ private static boolean isSupportedType(Class<?> type) { return Type.isValidJavaFieldType(type.getName()); } - public static void takeNap(long millis) { - HiddenWait hiddenWait = new HiddenWait(); - try { - synchronized(hiddenWait) { - hiddenWait.wait(millis); - } - } catch (InterruptedException e) { - // ok - } - } - public static void notifyFlush() { synchronized (flushObject) { flushObject.notifyAll(); @@ -369,13 +358,7 @@ public static void notifyFlush() { } public static void waitFlush(long timeOut) { - synchronized (flushObject) { - try { - flushObject.wait(timeOut); - } catch (InterruptedException e) { - // OK - } - } + flushObject.takeNap(timeOut); } public static Instant epochNanosToInstant(long epochNanos) { diff --git a/src/jdk.management.jfr/share/classes/jdk/management/jfr/DownLoadThread.java b/src/jdk.management.jfr/share/classes/jdk/management/jfr/DownLoadThread.java index 5ce66692537cb..05895f0f4a99b 100644 --- a/src/jdk.management.jfr/share/classes/jdk/management/jfr/DownLoadThread.java +++ b/src/jdk.management.jfr/share/classes/jdk/management/jfr/DownLoadThread.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,12 +30,14 @@ import java.util.Map; import jdk.jfr.internal.management.ManagementSupport; +import jdk.jfr.internal.management.HiddenWait; final class DownLoadThread extends Thread { private final RemoteRecordingStream stream; private final Instant startTime; private final Instant endTime; private final DiskRepository diskRepository; + private final HiddenWait threadSleeper = new HiddenWait(); DownLoadThread(RemoteRecordingStream stream, String name) { super(name); @@ -64,7 +66,7 @@ public void run() { if (bytes.length != 0) { diskRepository.write(bytes); } else { - takeNap(); + threadSleeper.takeNap(1000); } } } catch (IOException ioe) { @@ -73,12 +75,4 @@ public void run() { diskRepository.complete(); } } - - private void takeNap() { - try { - Thread.sleep(1000); - } catch (InterruptedException ie) { - // ignore - } - } } diff --git a/src/jdk.management.jfr/share/classes/jdk/management/jfr/FileDump.java b/src/jdk.management.jfr/share/classes/jdk/management/jfr/FileDump.java index 7da0fe351c450..37ba296732635 100644 --- a/src/jdk.management.jfr/share/classes/jdk/management/jfr/FileDump.java +++ b/src/jdk.management.jfr/share/classes/jdk/management/jfr/FileDump.java @@ -32,8 +32,10 @@ import java.util.Deque; import jdk.management.jfr.DiskRepository.DiskChunk; +import jdk.jfr.internal.management.HiddenWait; final class FileDump { + private final HiddenWait lock = new HiddenWait(); private final Deque<DiskChunk> chunks = new ArrayDeque<>(); private final long stopTimeMillis; private boolean complete; @@ -42,45 +44,53 @@ final class FileDump { this.stopTimeMillis = stopTimeMillis; } - public synchronized void add(DiskChunk dc) { - if (isComplete()) { - return; - } - dc.acquire(); - chunks.addFirst(dc); - long endMillis = dc.endTimeNanos / 1_000_000; - if (endMillis >= stopTimeMillis) { - setComplete(); + public void add(DiskChunk dc) { + synchronized (lock) { + if (isComplete()) { + return; + } + dc.acquire(); + chunks.addFirst(dc); + long endMillis = dc.endTimeNanos / 1_000_000; + if (endMillis >= stopTimeMillis) { + setComplete(); + } } } - public synchronized boolean isComplete() { - return complete; + public boolean isComplete() { + synchronized (lock) { + return complete; + } } - public synchronized void setComplete() { - complete = true; - this.notifyAll(); + public void setComplete() { + synchronized (lock) { + complete = true; + lock.notifyAll(); + } } - public synchronized void close() { - for (DiskChunk dc : chunks) { - dc.release(); + public void close() { + synchronized (lock) { + for (DiskChunk dc : chunks) { + dc.release(); + } + chunks.clear(); + complete = true; } - chunks.clear(); - complete = true; } private DiskChunk oldestChunk() throws InterruptedException { while (true) { - synchronized (this) { + synchronized (lock) { if (!chunks.isEmpty()) { return chunks.pollLast(); } if (complete) { return null; } - this.wait(); + lock.wait(); } } } diff --git a/test/jdk/jdk/jfr/jvm/TestHiddenWait.java b/test/jdk/jdk/jfr/jvm/TestHiddenWait.java new file mode 100644 index 0000000000000..f4b810f192e48 --- /dev/null +++ b/test/jdk/jdk/jfr/jvm/TestHiddenWait.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jfr.jvm; + +import java.nio.file.Path; +import java.time.Duration; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +import jdk.jfr.Recording; +import jdk.jfr.Name; +import jdk.jfr.Event; +import jdk.jfr.FlightRecorder; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingStream; +import jdk.test.lib.jfr.Events; + +/** + * @test TestHiddenWait + * @key jfr + * @summary Checks that JFR code don't emit noise in the form of ThreadSleep and JavaMonitorWait events. + * @requires vm.hasJFR + * @library /test/lib + * @run main/othervm jdk.jfr.jvm.TestHiddenWait + */ +public class TestHiddenWait { + static final String PERIODIC_EVENT_NAME = "test.Periodic"; + + @Name(PERIODIC_EVENT_NAME) + public static class PeriodicEvent extends Event { + } + + public static void main(String... args) throws Exception { + FlightRecorder.addPeriodicEvent(PeriodicEvent.class, () -> { + PeriodicEvent event = new PeriodicEvent(); + event.commit(); + }); + try (Recording r = new Recording()) { + AtomicLong counter = new AtomicLong(); + r.enable("jdk.ThreadSleep").withoutThreshold(); + r.enable("jdk.JavaMonitorWait").withoutThreshold(); + r.enable(PERIODIC_EVENT_NAME).withPeriod(Duration.ofMillis(100)); + r.start(); + // Triggers Object.wait() in stream barrier + try (RecordingStream b = new RecordingStream()) { + b.startAsync(); + b.stop(); + } + // Wait for for periodic events + try (RecordingStream s = new RecordingStream()) { + s.onEvent(PERIODIC_EVENT_NAME, e -> { + if (counter.incrementAndGet() >= 2) { + s.close(); + } + }); + s.start(); + } + List<RecordedEvent> events = Events.fromRecording(r); + for (RecordedEvent event : events) { + if (!event.getEventType().getName().equals(PERIODIC_EVENT_NAME)) { + System.out.println(event); + throw new Exception("Didn't expect ThreadSleep or JavaMonitorWait events"); + } + } + } + } +}