RFR: 8332084: Ensure JdkConsoleImpl.restoreEcho visibility in a shutdown hook [v3]
Pavel Rappo
prappo at openjdk.org
Tue May 14 10:01:02 UTC 2024
On Mon, 13 May 2024 21:14:26 GMT, Stuart Marks <smarks at openjdk.org> wrote:
> Hm, I don't think we want to add any synchronized blocks within a shutdown hook. If a thread is blocked reading from the console, it will hold readLock; if the JVM is then shut down using a signal, it will run shutdown hooks, and this hook will block awaiting readLock. This can deadlock the shutdown completely.
>
> To ensure proper memory visibility, maybe consider making `restoreEcho` be volatile.
It turns out it's not that hard to imagine. Thanks for helping me imagine it, Stuart.
I think that we should add the most straightforward test to demonstrate that the `restoreEcho` logic (or the lack of it) has an observable effect. If we fail to do that, it might mean that the `restoreEcho` logic is not needed and that the rest of this message is probably irrelevant.
The test should be added not in a separate PR for [8332161] -- a bug which Naoto has just filed -- but in this PR, and then we should `/issue add 8332161`.
Since we now have established that [concurrency] is possible, we should find an adequate concurrent solution. I can imagine that the proposed `volatile`-based solution may fall short in the following scenario.
Thread 1 reaches the line marked by `*`. Let's assume that immediately before executing that line `restoreEcho` is `false` and echo is on:
public char[] readPassword(String fmt, Object ... args) {
char[] passwd = null;
synchronized (writeLock) {
synchronized(readLock) {
installShutdownHook();
try {
restoreEcho = echo(false); // *
} catch (IOException x) {
throw new IOError(x);
}
IOError ioe = null;
try {
if (!fmt.isEmpty())
pw.format(fmt, args);
passwd = readline(true);
} catch (IOException x) {
ioe = new IOError(x);
} finally {
try {
if (restoreEcho) // **
restoreEcho = echo(true);
} catch (IOException x) {
...
Thread 1 executes `echo(false)`, which returns `true`. `true` is not yet written into `restoreEcho`.
Meanwhile, thread 2 (the hook thread) is about to execute the line marked by `***`:
public void run() {
try {
if (restoreEcho) { // ***
echo(true);
}
} catch (IOException x) { }
}
Thread 2 sees `restoreEcho` is `false` and skips `echo(true)`. If thread 1 then ceases to exist (remember, we're shutting down) before it reaches the line marked by `**`in the `finally` block, the console echo will remain off.
[8332161]: https://bugs.openjdk.org/browse/JDK-8332161
[concurrency]: https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/Runtime.html#shutdown
-------------
PR Comment: https://git.openjdk.org/jdk/pull/19184#issuecomment-2109779741
More information about the core-libs-dev
mailing list