1 /*
   2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.core.common.util;
  24 
  25 import org.graalvm.compiler.options.Option;
  26 import org.graalvm.compiler.options.OptionType;
  27 import org.graalvm.compiler.options.OptionValue;
  28 
  29 /**
  30  * Utility class that allows the compiler to monitor compilations that take a very long time.
  31  */
  32 public final class CompilationAlarm implements AutoCloseable {
  33 
  34     public static class Options {
  35         // @formatter:off
  36         @Option(help = "Time limit in seconds before a compilation expires (0 to disable the limit).", type = OptionType.Debug)
  37         public static final OptionValue<Integer> CompilationExpirationPeriod = new OptionValue<>(300);
  38         // @formatter:on
  39     }
  40 
  41     private CompilationAlarm() {
  42     }
  43 
  44     private static boolean enabled() {
  45         return Options.CompilationExpirationPeriod.getValue() > 0;
  46     }
  47 
  48     /**
  49      * Thread local storage for compilation start timestamps. Everytime a compiler thread calls
  50      * {@link #trackCompilationPeriod()} it will save the start timestamp of the compilation.
  51      */
  52     private static final ThreadLocal<Long> compilationStartedTimeStamps = new ThreadLocal<>();
  53 
  54     private static boolean compilationStarted() {
  55         if (enabled()) {
  56             Long start = compilationStartedTimeStamps.get();
  57             if (start == null) {
  58                 compilationStartedTimeStamps.set(System.currentTimeMillis());
  59                 return true;
  60             }
  61         }
  62         return false;
  63     }
  64 
  65     private static void compilationFinished() {
  66         if (enabled()) {
  67             assert compilationStartedTimeStamps.get() != null;
  68             compilationStartedTimeStamps.set(null);
  69         }
  70     }
  71 
  72     /**
  73      * Determines if the current compilation is expired. A compilation expires if it takes longer
  74      * than {@linkplain CompilationAlarm.Options#CompilationExpirationPeriod}.
  75      *
  76      * @return {@code true} if the current compilation already takes longer than
  77      *         {@linkplain CompilationAlarm.Options#CompilationExpirationPeriod}, {@code false}
  78      *         otherwise
  79      */
  80     public static boolean hasExpired() {
  81         if (enabled()) {
  82             Long start = compilationStartedTimeStamps.get();
  83             if (start != null) {
  84                 long time = System.currentTimeMillis();
  85                 assert time >= start;
  86                 return time - start > Options.CompilationExpirationPeriod.getValue() * 1000;
  87             }
  88         }
  89         return false;
  90     }
  91 
  92     @Override
  93     public void close() {
  94         compilationFinished();
  95     }
  96 
  97     private static final CompilationAlarm INSTANCE = enabled() ? new CompilationAlarm() : null;
  98 
  99     /**
 100      * Gets an object that can be used in a try-with-resource statement to set an time limit based
 101      * alarm for a compilation.
 102      *
 103      * @return a {@link CompilationAlarm} instance if there is no current alarm for the calling
 104      *         thread otherwise {@code null}
 105      */
 106     public static CompilationAlarm trackCompilationPeriod() {
 107         if (compilationStarted()) {
 108             return INSTANCE;
 109         }
 110         return null;
 111     }
 112 
 113 }