8150646: Add support for blocking compiles though whitebox API
Reviewed-by: kvn, ppunegov, simonis, neliasso
Contributed-by: nils.eliasson@oracle.com, volker.simonis@gmail.com
--- a/hotspot/src/share/vm/compiler/compileBroker.cpp Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/compiler/compileBroker.cpp Thu Mar 03 16:21:16 2016 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2016, 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
@@ -838,12 +838,8 @@
const methodHandle& hot_method,
int hot_count,
const char* comment,
+ bool blocking,
Thread* thread) {
- // do nothing if compiler thread(s) is not available
- if (!_initialized) {
- return;
- }
-
guarantee(!method->is_abstract(), "cannot compile abstract methods");
assert(method->method_holder()->is_instance_klass(),
"sanity check");
@@ -916,7 +912,6 @@
// Outputs from the following MutexLocker block:
CompileTask* task = NULL;
- bool blocking = false;
CompileQueue* queue = compile_queue(comp_level);
// Acquire our lock.
@@ -946,9 +941,6 @@
return;
}
- // Should this thread wait for completion of the compile?
- blocking = is_compile_blocking();
-
#if INCLUDE_JVMCI
if (UseJVMCICompiler) {
if (blocking) {
@@ -1034,11 +1026,28 @@
}
}
-
nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci,
int comp_level,
const methodHandle& hot_method, int hot_count,
const char* comment, Thread* THREAD) {
+ // do nothing if compilebroker is not available
+ if (!_initialized) {
+ return NULL;
+ }
+ AbstractCompiler *comp = CompileBroker::compiler(comp_level);
+ assert(comp != NULL, "Ensure we don't compile before compilebroker init");
+ DirectiveSet* directive = DirectivesStack::getMatchingDirective(method, comp);
+ nmethod* nm = CompileBroker::compile_method(method, osr_bci, comp_level, hot_method, hot_count, comment, directive, THREAD);
+ DirectivesStack::release(directive);
+ return nm;
+}
+
+nmethod* CompileBroker::compile_method(const methodHandle& method, int osr_bci,
+ int comp_level,
+ const methodHandle& hot_method, int hot_count,
+ const char* comment, DirectiveSet* directive,
+ Thread* THREAD) {
+
// make sure arguments make sense
assert(method->method_holder()->is_instance_klass(), "not an instance method");
assert(osr_bci == InvocationEntryBci || (0 <= osr_bci && osr_bci < method->code_size()), "bci out of range");
@@ -1051,8 +1060,8 @@
// lock, make sure that the compilation
// isn't prohibited in a straightforward way.
AbstractCompiler *comp = CompileBroker::compiler(comp_level);
- if (comp == NULL || !comp->can_compile_method(method) ||
- compilation_is_prohibited(method, osr_bci, comp_level)) {
+ if (!comp->can_compile_method(method) ||
+ compilation_is_prohibited(method, osr_bci, comp_level, directive->ExcludeOption)) {
return NULL;
}
@@ -1160,7 +1169,7 @@
CompilationPolicy::policy()->delay_compilation(method());
return NULL;
}
- compile_method_base(method, osr_bci, comp_level, hot_method, hot_count, comment, THREAD);
+ compile_method_base(method, osr_bci, comp_level, hot_method, hot_count, comment, !directive->BackgroundCompilationOption, THREAD);
}
// return requested nmethod
@@ -1217,7 +1226,7 @@
// CompileBroker::compilation_is_prohibited
//
// See if this compilation is not allowed.
-bool CompileBroker::compilation_is_prohibited(const methodHandle& method, int osr_bci, int comp_level) {
+bool CompileBroker::compilation_is_prohibited(const methodHandle& method, int osr_bci, int comp_level, bool excluded) {
bool is_native = method->is_native();
// Some compilers may not support the compilation of natives.
AbstractCompiler *comp = compiler(comp_level);
@@ -1235,11 +1244,6 @@
return true;
}
- // Breaking the abstraction - directives are only used inside a compilation otherwise.
- DirectiveSet* directive = DirectivesStack::getMatchingDirective(method, comp);
- bool excluded = directive->ExcludeOption;
- DirectivesStack::release(directive);
-
// The method may be explicitly excluded by the user.
double scale;
if (excluded || (CompilerOracle::has_option_value(method, "CompileThresholdScaling", scale) && scale == 0)) {
@@ -1304,16 +1308,6 @@
return assign_compile_id(method, osr_bci);
}
-/**
- * Should the current thread block until this compilation request
- * has been fulfilled?
- */
-bool CompileBroker::is_compile_blocking() {
- assert(!InstanceRefKlass::owns_pending_list_lock(JavaThread::current()), "possible deadlock");
- return !BackgroundCompilation;
-}
-
-
// ------------------------------------------------------------------
// CompileBroker::preload_classes
void CompileBroker::preload_classes(const methodHandle& method, TRAPS) {
--- a/hotspot/src/share/vm/compiler/compileBroker.hpp Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/compiler/compileBroker.hpp Thu Mar 03 16:21:16 2016 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2016, 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
@@ -222,8 +222,7 @@
static JavaThread* make_thread(const char* name, CompileQueue* queue, CompilerCounters* counters, AbstractCompiler* comp, bool compiler_thread, TRAPS);
static void init_compiler_sweeper_threads(int c1_compiler_count, int c2_compiler_count);
static bool compilation_is_complete (const methodHandle& method, int osr_bci, int comp_level);
- static bool compilation_is_prohibited(const methodHandle& method, int osr_bci, int comp_level);
- static bool is_compile_blocking();
+ static bool compilation_is_prohibited(const methodHandle& method, int osr_bci, int comp_level, bool excluded);
static void preload_classes (const methodHandle& method, TRAPS);
static CompileTask* create_compile_task(CompileQueue* queue,
@@ -253,6 +252,7 @@
const methodHandle& hot_method,
int hot_count,
const char* comment,
+ bool blocking,
Thread* thread);
static CompileQueue* compile_queue(int comp_level);
@@ -291,6 +291,15 @@
int hot_count,
const char* comment, Thread* thread);
+ static nmethod* compile_method(const methodHandle& method,
+ int osr_bci,
+ int comp_level,
+ const methodHandle& hot_method,
+ int hot_count,
+ const char* comment,
+ DirectiveSet* directive,
+ Thread* thread);
+
// Acquire any needed locks and assign a compile id
static uint assign_compile_id_unlocked(Thread* thread, const methodHandle& method, int osr_bci);
--- a/hotspot/src/share/vm/compiler/compilerDirectives.cpp Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/compiler/compilerDirectives.cpp Thu Mar 03 16:21:16 2016 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016, 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
@@ -472,9 +472,12 @@
_depth++;
}
-void DirectivesStack::pop() {
+void DirectivesStack::pop(int count) {
MutexLockerEx locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag);
- pop_inner();
+ assert(count > -1, "No negative values");
+ for (int i = 0; i < count; i++) {
+ pop_inner();
+ }
}
void DirectivesStack::pop_inner() {
--- a/hotspot/src/share/vm/compiler/compilerDirectives.hpp Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/compiler/compilerDirectives.hpp Thu Mar 03 16:21:16 2016 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016, 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
@@ -42,6 +42,7 @@
cflags(PrintAssembly, bool, PrintAssembly, PrintAssembly) \
cflags(PrintInlining, bool, PrintInlining, PrintInlining) \
cflags(PrintNMethods, bool, PrintNMethods, PrintNMethods) \
+ cflags(BackgroundCompilation, bool, BackgroundCompilation, BackgroundCompilation) \
cflags(ReplayInline, bool, false, ReplayInline) \
cflags(DumpReplay, bool, false, DumpReplay) \
cflags(DumpInline, bool, false, DumpInline) \
@@ -87,7 +88,7 @@
static DirectiveSet* getMatchingDirective(methodHandle mh, AbstractCompiler* comp);
static DirectiveSet* getDefaultDirective(AbstractCompiler* comp);
static void push(CompilerDirectives* directive);
- static void pop();
+ static void pop(int count);
static bool check_capacity(int request_size, outputStream* st);
static void clear();
static void print(outputStream* st);
--- a/hotspot/src/share/vm/compiler/directivesParser.cpp Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/compiler/directivesParser.cpp Thu Mar 03 16:21:16 2016 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, 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
@@ -55,7 +55,7 @@
assert(_tmp_depth == 0, "Consistency");
}
-bool DirectivesParser::parse_string(const char* text, outputStream* st) {
+int DirectivesParser::parse_string(const char* text, outputStream* st) {
DirectivesParser cd(text, st);
if (cd.valid()) {
return cd.install_directives();
@@ -63,7 +63,7 @@
cd.clean_tmp();
st->flush();
st->print_cr("Parsing of compiler directives failed");
- return false;
+ return -1;
}
}
@@ -97,17 +97,17 @@
buffer[num_read] = '\0';
// close file
os::close(file_handle);
- return parse_string(buffer, stream);
+ return parse_string(buffer, stream) != -1;
}
}
return false;
}
-bool DirectivesParser::install_directives() {
+int DirectivesParser::install_directives() {
// Check limit
if (!DirectivesStack::check_capacity(_tmp_depth, _st)) {
clean_tmp();
- return false;
+ return 0;
}
// Pop from internal temporary stack and push to compileBroker.
@@ -120,14 +120,14 @@
}
if (i == 0) {
_st->print_cr("No directives in file");
- return false;
+ return 0;
} else {
_st->print_cr("%i compiler directives added", i);
if (CompilerDirectivesPrint) {
// Print entire directives stack after new has been pushed.
DirectivesStack::print(_st);
}
- return true;
+ return i;
}
}
--- a/hotspot/src/share/vm/compiler/directivesParser.hpp Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/compiler/directivesParser.hpp Thu Mar 03 16:21:16 2016 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, 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
@@ -51,8 +51,8 @@
static bool has_file();
static bool parse_from_flag();
static bool parse_from_file(const char* filename, outputStream* st);
- static bool parse_string(const char* string, outputStream* st);
- bool install_directives();
+ static int parse_string(const char* string, outputStream* st);
+ int install_directives();
private:
DirectivesParser(const char* text, outputStream* st);
--- a/hotspot/src/share/vm/prims/whitebox.cpp Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/prims/whitebox.cpp Thu Mar 03 16:21:16 2016 +0100
@@ -30,6 +30,7 @@
#include "classfile/stringTable.hpp"
#include "code/codeCache.hpp"
#include "compiler/methodMatcher.hpp"
+#include "compiler/directivesParser.hpp"
#include "jvmtifiles/jvmtiEnv.hpp"
#include "memory/metadataFactory.hpp"
#include "memory/metaspaceShared.hpp"
@@ -636,6 +637,10 @@
WB_END
WB_ENTRY(jboolean, WB_EnqueueMethodForCompilation(JNIEnv* env, jobject o, jobject method, jint comp_level, jint bci))
+ // Screen for unavailable/bad comp level
+ if (CompileBroker::compiler(comp_level) == NULL) {
+ return false;
+ }
jmethodID jmid = reflected_method_to_jmid(thread, env, method);
CHECK_JNI_EXCEPTION_(env, JNI_FALSE);
methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid));
@@ -1502,6 +1507,27 @@
}
}
+WB_ENTRY(jint, WB_AddCompilerDirective(JNIEnv* env, jobject o, jstring compDirect))
+ // can't be in VM when we call JNI
+ ThreadToNativeFromVM ttnfv(thread);
+ const char* dir = env->GetStringUTFChars(compDirect, NULL);
+ int ret;
+ {
+ ThreadInVMfromNative ttvfn(thread); // back to VM
+ ret = DirectivesParser::parse_string(dir, tty);
+ }
+ env->ReleaseStringUTFChars(compDirect, dir);
+ // -1 for error parsing directive. Return 0 as number of directives added.
+ if (ret == -1) {
+ ret = 0;
+ }
+ return (jint) ret;
+WB_END
+
+WB_ENTRY(void, WB_RemoveCompilerDirective(JNIEnv* env, jobject o, jint count))
+ DirectivesStack::pop(count);
+WB_END
+
#define CC (char*)
static JNINativeMethod methods[] = {
@@ -1677,6 +1703,9 @@
{CC"isShared", CC"(Ljava/lang/Object;)Z", (void*)&WB_IsShared },
{CC"areSharedStringsIgnored", CC"()Z", (void*)&WB_AreSharedStringsIgnored },
{CC"clearInlineCaches", CC"()V", (void*)&WB_ClearInlineCaches },
+ {CC"addCompilerDirective", CC"(Ljava/lang/String;)I",
+ (void*)&WB_AddCompilerDirective },
+ {CC"removeCompilerDirective", CC"(I)V", (void*)&WB_RemoveCompilerDirective },
};
#undef CC
--- a/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp Thu Mar 03 16:21:16 2016 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -177,9 +177,7 @@
// Called with the queue locked and with at least one element
CompileTask* AdvancedThresholdPolicy::select_task(CompileQueue* compile_queue) {
-#if INCLUDE_JVMCI
CompileTask *max_blocking_task = NULL;
-#endif
CompileTask *max_task = NULL;
Method* max_method = NULL;
jlong t = os::javaTimeMillis();
@@ -193,7 +191,8 @@
max_method = method;
} else {
// If a method has been stale for some time, remove it from the queue.
- if (is_stale(t, TieredCompileTaskTimeout, method) && !is_old(method)) {
+ // Blocking tasks don't become stale
+ if (!task->is_blocking() && is_stale(t, TieredCompileTaskTimeout, method) && !is_old(method)) {
if (PrintTieredEvents) {
print_event(REMOVE_FROM_QUEUE, method, method, task->osr_bci(), (CompLevel)task->comp_level());
}
@@ -210,29 +209,25 @@
max_method = method;
}
}
-#if INCLUDE_JVMCI
- if (UseJVMCICompiler && task->is_blocking()) {
+
+ if (task->is_blocking()) {
if (max_blocking_task == NULL || compare_methods(method, max_blocking_task->method())) {
max_blocking_task = task;
}
}
-#endif
+
task = next_task;
}
-#if INCLUDE_JVMCI
- if (UseJVMCICompiler) {
- if (max_blocking_task != NULL) {
- // In blocking compilation mode, the CompileBroker will make
- // compilations submitted by a JVMCI compiler thread non-blocking. These
- // compilations should be scheduled after all blocking compilations
- // to service non-compiler related compilations sooner and reduce the
- // chance of such compilations timing out.
- max_task = max_blocking_task;
- max_method = max_task->method();
- }
+ if (max_blocking_task != NULL) {
+ // In blocking compilation mode, the CompileBroker will make
+ // compilations submitted by a JVMCI compiler thread non-blocking. These
+ // compilations should be scheduled after all blocking compilations
+ // to service non-compiler related compilations sooner and reduce the
+ // chance of such compilations timing out.
+ max_task = max_blocking_task;
+ max_method = max_task->method();
}
-#endif
if (max_task->comp_level() == CompLevel_full_profile && TieredStopAtLevel > CompLevel_full_profile
&& is_method_profiled(max_method)) {
--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp Thu Mar 10 14:53:09 2016 +0000
+++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp Thu Mar 03 16:21:16 2016 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2016, 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
@@ -931,7 +931,7 @@
}
void CompilerDirectivesRemoveDCmd::execute(DCmdSource source, TRAPS) {
- DirectivesStack::pop();
+ DirectivesStack::pop(1);
}
void CompilerDirectivesClearDCmd::execute(DCmdSource source, TRAPS) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/whitebox/BlockingCompilation.java Thu Mar 03 16:21:16 2016 +0100
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2016 SAP SE. 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.
+ */
+
+/*
+ * @test
+ * @bug 8150646
+ * @summary Add support for blocking compiles through whitebox API
+ * @library /testlibrary /test/lib /
+ * @build sun.hotspot.WhiteBox
+ * compiler.testlibrary.CompilerUtils
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * sun.hotspot.WhiteBox$WhiteBoxPermission
+ *
+ * @run main/othervm
+ * -Xbootclasspath/a:.
+ * -Xmixed
+ * -XX:+UnlockDiagnosticVMOptions
+ * -XX:+WhiteBoxAPI
+ * -XX:+PrintCompilation
+ * -XX:CompileCommand=option,BlockingCompilation::foo,PrintInlining
+ * BlockingCompilation
+ */
+
+import java.lang.reflect.Method;
+import java.util.Random;
+
+import sun.hotspot.WhiteBox;
+import compiler.testlibrary.CompilerUtils;
+
+public class BlockingCompilation {
+ private static final WhiteBox WB = WhiteBox.getWhiteBox();
+ private static final Random RANDOM = new Random();
+
+ public static int foo() {
+ return RANDOM.nextInt();
+ }
+
+ public static void main(String[] args) throws Exception {
+ long sum = 0;
+ int level = 0;
+ boolean enqued = false;
+ Method m = BlockingCompilation.class.getMethod("foo");
+ int[] levels = CompilerUtils.getAvailableCompilationLevels();
+
+ // If there are no compilers available these tests don't make any sense.
+ if (levels.length == 0) return;
+ int max_level = levels[levels.length - 1];
+
+ // Normal, non-blocking compilation
+ for (int i = 0; i < 500_000; i++) {
+ sum += foo();
+ if (!enqued && WB.isMethodQueuedForCompilation(m)) {
+ System.out.println("==> " + m + " enqued for compilation in iteration " + i);
+ enqued = true;
+ }
+ if (WB.isMethodCompiled(m)) {
+ if (WB.getMethodCompilationLevel(m) != level) {
+ level = WB.getMethodCompilationLevel(m);
+ System.out.println("==> " + m + " compiled at level " + level + " in iteration " + i);
+ enqued = false;
+ if (level == max_level) break;
+ }
+ }
+ }
+
+ // This is necessarry because WB.deoptimizeMethod doesn't clear the methods
+ // MDO and therefore level 3 compilations will be downgraded to level 2.
+ WB.clearMethodState(m);
+
+ // Blocking compilations on all levels, using the default versions of
+ // WB.enqueueMethodForCompilation() and manually setting compiler directives.
+ String directive = "[{ match: \"BlockingCompilation.foo\", BackgroundCompilation: false }]";
+ WB.addCompilerDirective(directive);
+
+ for (int l : levels) {
+ WB.deoptimizeMethod(m);
+ WB.enqueueMethodForCompilation(m, l);
+
+ if (!WB.isMethodCompiled(m) || WB.getMethodCompilationLevel(m) != l) {
+ String msg = m + " should be compiled at level " + l +
+ "(but is actually compiled at level " +
+ WB.getMethodCompilationLevel(m) + ")";
+ System.out.println("==> " + msg);
+ throw new Exception(msg);
+ }
+ }
+
+ WB.removeCompilerDirective(1);
+
+ WB.deoptimizeMethod(m);
+ WB.clearMethodState(m);
+ level = 0;
+ enqued = false;
+ int iteration = 0;
+
+ // Normal, non-blocking compilation
+ for (int i = 0; i < 500_000; i++) {
+ sum += foo();
+ if (!enqued && WB.isMethodQueuedForCompilation(m)) {
+ System.out.println("==> " + m + " enqued for compilation in iteration " + i);
+ iteration = i;
+ enqued = true;
+ }
+ if (WB.isMethodCompiled(m)) {
+ if (WB.getMethodCompilationLevel(m) != level) {
+ level = WB.getMethodCompilationLevel(m);
+ System.out.println("==> " + m + " compiled at level " + level + " in iteration " + i);
+ if (level == 4 && iteration == i) {
+ throw new Exception("This seems to be a blocking compilation although it shouldn't.");
+ }
+ enqued = false;
+ if (level == max_level) break;
+ }
+ }
+ }
+ }
+}