JDK-8230611 : infinite loop in LogOutputList::wait_until_no_readers()
  • Type: Bug
  • Component: hotspot
  • Sub-Component: svc
  • Affected Version: 11.0.3,13,14
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2019-09-04
  • Updated: 2022-06-27
  • Resolved: 2019-11-22
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 11 JDK 13 JDK 14
11.0.7-oracleFixed 13.0.4Fixed 14 b25Fixed
Related Reports
Relates :  
Relates :  
Description
While observing a loop while using -verbose option it would seem that a destructor call 
in the iterator constructor causes wait_until_no_readers to go into an infinite loop.

On analysis the issue seems to be in file src/hotspot/share/logging/logOutputList.cpp

The issue is due to invoking of destructor in iterator constructor due to creation of 
an object in the following code:

 Iterator iterator(LogLevelType level = LogLevel::Last) {
    increase_readers();
    return Iterator(this, _level_start[level]);
  }

Again, when the object is destroyed, the destructor is called again.

Due to this the _active_readers count becomes negative and wait_until_no_readers 
goes into infinite loop.

void LogOutputList::wait_until_no_readers() const {
  OrderAccess::storeload();
  while (_active_readers != 0) {
    // Busy wait
  }
}


Comments
Fix request (13u): The original change applies cleanly, passes tier1 and tier2 tests.
03-06-2020

Fix Request (11u) This fixes the corner case that livelocks the VM, and keeps codebases in sync (I see 11.0.7-oracle). Patch applies cleanly to 11u, passes tier1 and tier2 tests.
15-01-2020

URL: https://hg.openjdk.java.net/jdk/jdk/rev/fcd74557a9cc User: dbuck Date: 2019-11-22 04:30:05 +0000
22-11-2019

Testing requires building the JDK without RVO (-fno-elide-constructors flag on clang or gcc). Disabling RVO exposes a number of other similar breakages (which I will soon file additional bug reports for). Most of these additional bugs must be fixed (or at least worked around) before trying to test this particular fix. (It is not known if any tool chain out there hits these additional issues by default.) With considerable effort, I have been able to test the efficacy of this fix, but I do not believe it is practical to try to automate the testing.
20-11-2019

Yet another example of what happens when we break the Rule of Three. When we call the iterator() function, some compilers may use the copy ctor to return the newly created Iterator object. This extra Iterator object does not increment the readers count during construction (because the default copy ctor does not call increase_readers()), but it *does* decrement the readers count when its destructor is called. As a result, the readers count will eventually reach a negative number. This problem has not shown up on any of our own builds because all of the compilers we use (gcc / clang / VS) do RVO by default (usually while inlining the entire iterator() call), negating the need to make a temp copy. I am able to reproduce the issue on linux/gcc and macos/clang by using the -fno-elide-constructors flag to disable RVO. We never noticed this before because even our slowdebug builds (-O0) do RVO by default. The solution is to add a proper copy constructor that calls increase_readers(). Even though it will be dead code for now, a copy assignment operator override should also be added in case it is needed in the future (as is recommended by the Rule of Three).
20-11-2019