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 
  24 
  25 package org.graalvm.compiler.core.common.util;
  26 
  27 import org.graalvm.compiler.debug.Assertions;
  28 import org.graalvm.compiler.options.Option;
  29 import org.graalvm.compiler.options.OptionKey;
  30 import org.graalvm.compiler.options.OptionType;
  31 import org.graalvm.compiler.options.OptionValues;
  32 
  33 /**
  34  * Utility class that allows the compiler to monitor compilations that take a very long time.
  35  */
  36 public final class CompilationAlarm implements AutoCloseable {
  37 
  38     public static class Options {
  39         // @formatter:off
  40         @Option(help = "Time limit in seconds before a compilation expires (0 to disable the limit). " +
  41                        "The compilation alarm will be implicitly disabled if assertions are enabled.", type = OptionType.Debug)
  42         public static final OptionKey<Integer> CompilationExpirationPeriod = new OptionKey<>(300);
  43         // @formatter:on
  44     }
  45 
  46     private CompilationAlarm(long expiration) {
  47         this.expiration = expiration;
  48     }
  49 
  50     /**
  51      * Thread local storage for the active compilation alarm.
  52      */
  53     private static final ThreadLocal<CompilationAlarm> currentAlarm = new ThreadLocal<>();
  54 
  55     private static final CompilationAlarm NEVER_EXPIRES = new CompilationAlarm(0);
  56 
  57     /**
  58      * Gets the current compilation alarm. If there is no current alarm, a non-null value is
  59      * returned that will always return {@code false} for {@link #hasExpired()}.
  60      */
  61     public static CompilationAlarm current() {
  62         CompilationAlarm alarm = currentAlarm.get();
  63         return alarm == null ? NEVER_EXPIRES : alarm;
  64     }
  65 
  66     /**
  67      * Determines if this alarm has expired. A compilation expires if it takes longer than
  68      * {@linkplain CompilationAlarm.Options#CompilationExpirationPeriod}.
  69      *
  70      * @return {@code true} if the current compilation already takes longer than
  71      *         {@linkplain CompilationAlarm.Options#CompilationExpirationPeriod}, {@code false}
  72      *         otherwise
  73      */
  74     public boolean hasExpired() {
  75         return this != NEVER_EXPIRES && System.currentTimeMillis() > expiration;
  76     }
  77 
  78     @Override
  79     public void close() {
  80         if (this != NEVER_EXPIRES) {
  81             currentAlarm.set(null);
  82         }
  83     }
  84 
  85     /**
  86      * The time at which this alarm expires.
  87      */
  88     private final long expiration;
  89 
  90     /**
  91      * Starts an alarm for setting a time limit on a compilation if there isn't already an active
  92      * alarm, if assertions are disabled and
  93      * {@link CompilationAlarm.Options#CompilationExpirationPeriod}{@code > 0}. The returned value
  94      * can be used in a try-with-resource statement to disable the alarm once the compilation is
  95      * finished.
  96      *
  97      * @return a {@link CompilationAlarm} if there was no current alarm for the calling thread
  98      *         before this call otherwise {@code null}
  99      */
 100     public static CompilationAlarm trackCompilationPeriod(OptionValues options) {
 101         int period = Assertions.assertionsEnabled() ? 0 : Options.CompilationExpirationPeriod.getValue(options);
 102         if (period > 0) {
 103             CompilationAlarm current = currentAlarm.get();
 104             if (current == null) {
 105                 long expiration = System.currentTimeMillis() + period * 1000;
 106                 current = new CompilationAlarm(expiration);
 107                 currentAlarm.set(current);
 108                 return current;
 109             }
 110         }
 111         return null;
 112     }
 113 
 114 }