JDK-8209671 : With dynamic compiler threads the compiler-thread count is not maintained in a thread-safe manner
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 11,12
  • Priority: P3
  • Status: Closed
  • Resolution: Not an Issue
  • Submitted: 2018-08-19
  • Updated: 2018-09-02
  • Resolved: 2018-08-27
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 12
12Resolved
Related Reports
Relates :  
Description
This is the code that updates the compiler thread count:

static bool can_remove(CompilerThread *ct, bool do_it) {
  assert(UseDynamicNumberOfCompilerThreads, "or shouldn't be here");
  if (!ReduceNumberOfCompilerThreads) return false;

  AbstractCompiler *compiler = ct->compiler();
  int compiler_count = compiler->num_compiler_threads();
  bool c1 = compiler->is_c1();

  // Keep at least 1 compiler thread of each type.
  if (compiler_count < 2) return false;

  // Keep thread alive for at least some time.
  if (ct->idle_time_millis() < (c1 ? 500 : 100)) return false;

  // We only allow the last compiler thread of each type to get removed.
  jobject last_compiler = c1 ? CompileBroker::compiler1_object(compiler_count - 1)
                             : CompileBroker::compiler2_object(compiler_count - 1);
  if (oopDesc::equals(ct->threadObj(), JNIHandles::resolve_non_null(last_compiler))) {
    if (do_it) {
      assert_locked_or_safepoint(CompileThread_lock); // Update must be consistent.
      compiler->set_num_compiler_threads(compiler_count - 1);
    }
    return true;
  }
  return false;
}

We read the count initially with no lock held:

  int compiler_count = compiler->num_compiler_threads();

we then later update the count with the lock held:

      assert_locked_or_safepoint(CompileThread_lock); // Update must be consistent.
      compiler->set_num_compiler_threads(compiler_count - 1);

but by this time the local variable "compiler_count" may not contain the current number of compiler threads and so we will set the wrong value. Further the count may also have changed when we use it here:

 jobject last_compiler = c1 ? CompileBroker::compiler1_object(compiler_count - 1)
                             : CompileBroker::compiler2_object(compiler_count - 1);

In addition there is logic to try and ensure we always keep at least one compiler thread but this seems to be executed with no locking so multiple threads can execute:

  if (compiler_count < 2) return false;

and all see a count >=2 but then all continue to exit thus leaving zero compiler threads running.
Comments
Thanks for explanations. It would be much clearer IMHO if the lock-free racy, imprecise code was factored out of the lock-using precise code. Some comments on the tolerance for imprecision would also help.
02-09-2018

Thanks Martin, I agree with your analysis. I've closed this as "Not an Issue". David, please re-open if you still see problems with the current implementation.
27-08-2018

I think this is not an issue. "can_remove" is called by 2 call sites: - CompileQueue::get(): "do_it" is false, so we only access the compiler_count once (without lock). The result doesn't necessarily need to be always precise. If false is returned, the compiler thread will just be kept alive until the next check. If true is returned, we will perform a consistent re-check while holding CompileThread_lock (see below). - CompileBroker::compiler_thread_loop(): Acquires CompileThread_lock before calling "can_remove". If false is returned, the thread will not get removed and it will re-check the compile queue.
27-08-2018

We should add asserts or guarantees that check the upper/lower limit of the number of compiler threads.
23-08-2018

Martin, could you please have a look? Thanks.
20-08-2018

ILW = Compiler thread count is not accessed in thread safe manner (may lead to incorrect computation), never showed up to be a problem, -XX:-UseDynamicNumberOfCompilerThreads = HLM = P3
20-08-2018