--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/RequestEngine.java Tue May 15 20:24:34 2018 +0200
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2016, 2018, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.internal;
+
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Predicate;
+
+public final class RequestEngine {
+
+ private final static JVM jvm = JVM.getJVM();
+
+ final static class RequestHook {
+ private final Runnable hook;
+ private final PlatformEventType type;
+ private final AccessControlContext accessControllerContext;
+ private long delta;
+
+ // Java events
+ private RequestHook(AccessControlContext acc, PlatformEventType eventType, Runnable hook) {
+ this.hook = hook;
+ this.type = eventType;
+ this.accessControllerContext = acc;
+ }
+
+ // native events
+ RequestHook(PlatformEventType eventType) {
+ this(null, eventType, null);
+ }
+
+ private void execute() {
+ try {
+ if (accessControllerContext == null) { // native
+ jvm.emitEvent(type.getId(), JVM.counterTime(), 0);
+ Logger.log(LogTag.JFR_SYSTEM_EVENT, LogLevel.DEBUG, ()-> "Executed periodic hook for " + type.getLogName());
+ } else {
+ executeSecure();
+ }
+ } catch (Throwable e) {
+ // Prevent malicious user to propagate exception callback in the wrong context
+ Logger.log(LogTag.JFR_SYSTEM_EVENT, LogLevel.WARN, "Exception occured during execution of period hook for " + type.getLogName());
+ }
+ }
+
+ private void executeSecure() {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
+ public Void run() {
+ try {
+ hook.run();
+ Logger.log(LogTag.JFR_EVENT, LogLevel.DEBUG, ()-> "Executed periodic hook for " + type.getLogName());
+ } catch (Throwable t) {
+ // Prevent malicious user to propagate exception callback in the wrong context
+ Logger.log(LogTag.JFR_EVENT, LogLevel.WARN, "Exception occured during execution of period hook for " + type.getLogName());
+ }
+ return null;
+ }
+ }, accessControllerContext);
+ }
+ }
+
+ private final static List<RequestHook> entries = new CopyOnWriteArrayList<>();
+ private static long lastTimeMillis;
+
+ // Insertion takes O(2*n), could be O(1) with HashMap, but
+ // thinking is that CopyOnWriteArrayList is faster
+ // to iterate over, which will happen more over time.
+ public static void addHook(AccessControlContext acc, PlatformEventType type, Runnable hook) {
+ Objects.requireNonNull(acc);
+ RequestHook he = new RequestHook(acc, type, hook);
+ for (RequestHook e : entries) {
+ if (e.hook == hook) {
+ throw new IllegalArgumentException("Hook has already been added");
+ }
+ }
+ he.type.setEventHook(true);
+ entries.add(he);
+ logHook("Added", type);
+ }
+
+
+ private static void logHook(String action, PlatformEventType type) {
+ if (type.isJDK() || type.isJVM()) {
+ Logger.log(LogTag.JFR_SYSTEM_EVENT, LogLevel.INFO, action + " periodic hook for " + type.getLogName());
+ } else {
+ Logger.log(LogTag.JFR_EVENT, LogLevel.INFO, action + " periodic hook for " + type.getLogName());
+ }
+ }
+
+ // Takes O(2*n), see addHook.
+ public static boolean removeHook(Runnable hook) {
+ for (RequestHook rh : entries) {
+ if (rh.hook == hook) {
+ entries.remove(rh);
+ rh.type.setEventHook(false);
+ logHook("Removed", rh.type);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Only to be used for JVM events. No access control contest
+ // or check if hook already exists
+ static void addHooks(List<RequestHook> newEntries) {
+ List<RequestHook> addEntries = new ArrayList<>();
+ for (RequestHook rh : newEntries) {
+ rh.type.setEventHook(true);
+ addEntries.add(rh);
+ logHook("Added", rh.type);
+ }
+ entries.addAll(newEntries);
+ }
+
+ static void doChunkEnd() {
+ doChunk(x -> x.isEndChunk());
+ }
+
+ static void doChunkBegin() {
+ doChunk(x -> x.isBeginChunk());
+ }
+
+ private static void doChunk(Predicate<PlatformEventType> predicate) {
+ for (RequestHook requestHook : entries) {
+ PlatformEventType s = requestHook.type;
+ if (s.isEnabled() && predicate.test(s)) {
+ requestHook.execute();
+ }
+ }
+ }
+
+ static long doPeriodic() {
+ return run_requests(entries);
+ }
+
+ // code copied from native impl.
+ private static long run_requests(Collection<RequestHook> entries) {
+ long last = lastTimeMillis;
+ // Bug 9000556 - current time millis has rather lame resolution
+ // The use of os::elapsed_counter() is deliberate here, we don't
+ // want it exchanged for os::ft_elapsed_counter().
+ // Keeping direct call os::elapsed_counter() here for reliable
+ // real time values in order to decide when registered requestable
+ // events are due.
+ long now = System.currentTimeMillis();
+ long min = 0;
+ long delta = 0;
+
+ if (last == 0) {
+ last = now;
+ }
+
+ // time from then to now
+ delta = now - last;
+
+ if (delta < 0) {
+ // to handle time adjustments
+ // for example Daylight Savings
+ lastTimeMillis = now;
+ return 0;
+ }
+ for (RequestHook he : entries) {
+ long left = 0;
+ PlatformEventType es = he.type;
+ // Not enabled, skip.
+ if (!es.isEnabled() || es.isEveryChunk()) {
+ continue;
+ }
+ long r_period = es.getPeriod();
+ long r_delta = he.delta;
+
+ // add time elapsed.
+ r_delta += delta;
+
+ // above threshold?
+ if (r_delta >= r_period) {
+ // Bug 9000556 - don't try to compensate
+ // for wait > period
+ r_delta = 0;
+ he.execute();
+ ;
+ }
+
+ // calculate time left
+ left = (r_period - r_delta);
+
+ /**
+ * nothing outside checks that a period is >= 0, so left can end up
+ * negative here. ex. (r_period =(-1)) - (r_delta = 0) if it is,
+ * handle it.
+ */
+ if (left < 0) {
+ left = 0;
+ }
+
+ // assign delta back
+ he.delta = r_delta;
+
+ if (min == 0 || left < min) {
+ min = left;
+ }
+ }
+ lastTimeMillis = now;
+ return min;
+ }
+}