+{"files":[{"patch":"@@ -28,0 +28,1 @@\n+#include \"logging\/logFileStreamOutput.hpp\"\n@@ -59,1 +60,1 @@\n-void AsyncLogWriter::enqueue(LogFileOutput& output, const LogDecorations& decorations, const char* msg) {\n+void AsyncLogWriter::enqueue(LogFileStreamOutput& output, const LogDecorations& decorations, const char* msg) {\n@@ -70,1 +71,1 @@\n-void AsyncLogWriter::enqueue(LogFileOutput& output, LogMessageBuffer::Iterator msg_iterator) {\n+void AsyncLogWriter::enqueue(LogFileStreamOutput& output, LogMessageBuffer::Iterator msg_iterator) {\n@@ -98,1 +99,1 @@\n- bool do_entry(LogFileOutput* output, uint32_t& counter) {\n+ bool do_entry(LogFileStreamOutput* output, uint32_t& counter) {\n@@ -152,0 +153,3 @@\n+ \/\/ LogFileOutput::write_block() has called fflush().\n+ \/\/ stderr does not cache.\n+ fflush(stdout);\n","filename":"src\/hotspot\/share\/logging\/logAsyncWriter.cpp","additions":7,"deletions":3,"binary":false,"changes":10,"status":"modified"},{"patch":"@@ -28,1 +28,0 @@\n-#include \"logging\/logFileOutput.hpp\"\n@@ -32,0 +31,1 @@\n+#include \"runtime\/semaphore.hpp\"\n@@ -93,0 +93,3 @@\n+\/\/ Forward declaration\n+class LogFileStreamOutput;\n+\n@@ -94,1 +97,1 @@\n- LogFileOutput* _output;\n+ LogFileStreamOutput* _output;\n@@ -99,1 +102,1 @@\n- AsyncLogMessage(LogFileOutput* output, const LogDecorations& decorations, char* msg)\n+ AsyncLogMessage(LogFileStreamOutput* output, const LogDecorations& decorations, char* msg)\n@@ -105,1 +108,1 @@\n- LogFileOutput* output() const { return _output; }\n+ LogFileStreamOutput* output() const { return _output; }\n@@ -111,1 +114,1 @@\n-typedef ResourceHashtable<LogFileOutput*,\n+typedef ResourceHashtable<LogFileStreamOutput*,\n@@ -171,2 +174,2 @@\n- void enqueue(LogFileOutput& output, const LogDecorations& decorations, const char* msg);\n- void enqueue(LogFileOutput& output, LogMessageBuffer::Iterator msg_iterator);\n+ void enqueue(LogFileStreamOutput& output, const LogDecorations& decorations, const char* msg);\n+ void enqueue(LogFileStreamOutput& output, LogMessageBuffer::Iterator msg_iterator);\n","filename":"src\/hotspot\/share\/logging\/logAsyncWriter.hpp","additions":10,"deletions":7,"binary":false,"changes":17,"status":"modified"},{"patch":"@@ -27,1 +27,0 @@\n-#include \"logging\/logAsyncWriter.hpp\"\n@@ -275,1 +274,3 @@\n- int written = LogFileStreamOutput::write(decorations, msg);\n+ int written = write_internal(decorations, msg);\n+ \/\/ Need to flush to the filesystem before should_rotate()\n+ written = flush() ? written : -1;\n","filename":"src\/hotspot\/share\/logging\/logFileOutput.cpp","additions":3,"deletions":2,"binary":false,"changes":5,"status":"modified"},{"patch":"@@ -86,3 +86,3 @@\n- virtual int write(const LogDecorations& decorations, const char* msg);\n- virtual int write(LogMessageBuffer::Iterator msg_iterator);\n- int write_blocking(const LogDecorations& decorations, const char* msg);\n+ int write(const LogDecorations& decorations, const char* msg) override;\n+ int write(LogMessageBuffer::Iterator msg_iterator) override;\n+ int write_blocking(const LogDecorations& decorations, const char* msg) override;\n","filename":"src\/hotspot\/share\/logging\/logFileOutput.hpp","additions":3,"deletions":3,"binary":false,"changes":6,"status":"modified"},{"patch":"@@ -138,1 +138,1 @@\n-int LogFileStreamOutput::write_internal(const char* msg) {\n+int LogFileStreamOutput::write_internal(const LogDecorations& decorations, const char* msg) {\n@@ -140,0 +140,7 @@\n+ const bool use_decorations = !_decorators.is_empty();\n+\n+ if (use_decorations) {\n+ WRITE_LOG_WITH_RESULT_CHECK(write_decorations(decorations), written);\n+ WRITE_LOG_WITH_RESULT_CHECK(jio_fprintf(_stream, \" \"), written);\n+ }\n+\n@@ -162,0 +169,4 @@\n+int LogFileStreamOutput::write_blocking(const LogDecorations& decorations, const char* msg) {\n+ return write_internal(decorations, msg);\n+}\n+\n@@ -163,1 +174,5 @@\n- const bool use_decorations = !_decorators.is_empty();\n+ AsyncLogWriter* aio_writer = AsyncLogWriter::instance();\n+ if (aio_writer != nullptr) {\n+ aio_writer->enqueue(*this, decorations, msg);\n+ return 0;\n+ }\n@@ -165,1 +180,0 @@\n- int written = 0;\n@@ -167,5 +181,1 @@\n- if (use_decorations) {\n- WRITE_LOG_WITH_RESULT_CHECK(write_decorations(decorations), written);\n- WRITE_LOG_WITH_RESULT_CHECK(jio_fprintf(_stream, \" \"), written);\n- }\n- written += write_internal(msg);\n+ int written = write_internal(decorations, msg);\n@@ -177,1 +187,5 @@\n- const bool use_decorations = !_decorators.is_empty();\n+ AsyncLogWriter* aio_writer = AsyncLogWriter::instance();\n+ if (aio_writer != nullptr) {\n+ aio_writer->enqueue(*this, msg_iterator);\n+ return 0;\n+ }\n@@ -182,5 +196,1 @@\n- if (use_decorations) {\n- WRITE_LOG_WITH_RESULT_CHECK(write_decorations(msg_iterator.decorations()), written);\n- WRITE_LOG_WITH_RESULT_CHECK(jio_fprintf(_stream, \" \"), written);\n- }\n- written += write_internal(msg_iterator.message());\n+ written += write_internal(msg_iterator.decorations(), msg_iterator.message());\n","filename":"src\/hotspot\/share\/logging\/logFileStreamOutput.cpp","additions":24,"deletions":14,"binary":false,"changes":38,"status":"modified"},{"patch":"@@ -27,0 +27,1 @@\n+#include \"logging\/logAsyncWriter.hpp\"\n@@ -49,1 +50,0 @@\n- int write_internal(const char* msg);\n@@ -61,0 +61,1 @@\n+ int write_internal(const LogDecorations& decorations, const char* msg);\n@@ -67,0 +68,2 @@\n+ \/\/ Write API used by AsyncLogWriter\n+ virtual int write_blocking(const LogDecorations& decorations, const char* msg);\n","filename":"src\/hotspot\/share\/logging\/logFileStreamOutput.hpp","additions":4,"deletions":1,"binary":false,"changes":5,"status":"modified"},{"patch":"@@ -0,0 +1,164 @@\n+\/*\n+ * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.\n+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n+ *\n+ * This code is free software; you can redistribute it and\/or modify it\n+ * under the terms of the GNU General Public License version 2 only, as\n+ * published by the Free Software Foundation.\n+ *\n+ * This code is distributed in the hope that it will be useful, but WITHOUT\n+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License\n+ * version 2 for more details (a copy is included in the LICENSE file that\n+ * accompanied this code).\n+ *\n+ * You should have received a copy of the GNU General Public License version\n+ * 2 along with this work; if not, write to the Free Software Foundation,\n+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n+ *\n+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA\n+ * or visit www.oracle.com if you need additional information or have any\n+ * questions.\n+ *\/\n+\n+\/*\n+ * @test\n+ * @bug 8267517\n+ * @summary Test the JVM process with unified logging with -Xlog:async will not be\n+ * frozen even when stdout is blocked.\n+ *\n+ * @library \/test\/lib\n+ * @modules java.base\/jdk.internal.misc\n+ * @run driver BlockedLoggingTest\n+ *\/\n+\n+import java.io.BufferedReader;\n+import java.io.InputStreamReader;\n+import java.io.IOException;\n+import java.util.AbstractQueue;\n+import java.util.Arrays;\n+import java.util.List;\n+import java.util.concurrent.ArrayBlockingQueue;\n+\n+import jdk.test.lib.Asserts;\n+import jdk.test.lib.process.ProcessTools;\n+\n+public class BlockedLoggingTest {\n+ static String BANNER = \"User-defined Java Program has started.\";\n+ static int ThreadNum = 1;\n+\n+ public static class UserDefinedJavaProgram {\n+ public static void main(String[] args) {\n+ System.out.println(BANNER);\n+ System.out.flush();\n+\n+ Thread[] threads = new Thread[ThreadNum];\n+ \/\/ The size of pipe buffer is indeterminate. It is presumably 64k on many Linux distros.\n+ \/\/ We just churn many gc-related logs in ChurnThread.Duration seconds.\n+ for(int i = 0; i < ThreadNum; ++i) {\n+ threads[i] = new ChurnThread();\n+ threads[i].start();\n+ }\n+\n+ try {\n+ for (int i = 0; i < ThreadNum; ++i) {\n+ threads[i].join();\n+ }\n+ } catch (InterruptedException ie) {\n+ \/\/ ignore\n+ }\n+\n+ \/\/ If the control reaches here, we have demonstrated that the current process isn't\n+ \/\/ blocked by StdinBlocker because of -Xlog:async.\n+ \/\/\n+ \/\/ the reason we throw a RuntimeException because the normal exit of JVM still needs\n+ \/\/ to call AsyncLogWriter::flush(), stdout is still blocked. AbortVMOnException will\n+ \/\/ abort JVM and avoid the final flushing.\n+ throw new RuntimeException(\"we succeed if we each here.\");\n+ }\n+ }\n+\n+ static class ChurnThread extends Thread {\n+ static long Duration = 3; \/\/ seconds; Program will exit after Duration of seconds.\n+ static int ReferenceSize = 1024 * 10; \/\/ each reference object size;\n+ static int CountDownSize = 1000 * 100;\n+ static int EachRemoveSize = 1000 * 50; \/\/ remove # of elements each time.\n+\n+ long timeZero = System.currentTimeMillis();\n+\n+ public ChurnThread() {}\n+\n+ public void run() {\n+ AbstractQueue<String> q = new ArrayBlockingQueue<String>(CountDownSize);\n+ char[] srcArray = new char[ReferenceSize];\n+ String emptystr = new String(srcArray);\n+\n+ while (true) {\n+ \/\/ Simulate object use to force promotion into OldGen and then GC\n+ if (q.size() >= CountDownSize) {\n+ for (int j = 0; j < EachRemoveSize; j++) {\n+ q.remove();\n+ }\n+\n+ \/\/ every 1000 removal is counted as 1 unit.\n+ long curTime = System.currentTimeMillis();\n+ long totalTime = curTime - timeZero;\n+\n+ if (Duration != -1 && totalTime > Duration * 1000) {\n+ return;\n+ }\n+ }\n+\n+ srcArray = new char[ReferenceSize];\n+ emptystr = new String(srcArray);\n+ String str = emptystr.replace('\\0', 'a');\n+ q.add(str);\n+ }\n+ }\n+ }\n+\n+ \/\/ StdinBlocker echoes whatever it sees from stdin until it encounters BANNER.\n+ \/\/ It will hang and leave stdin alone.\n+ public static class StdinBlocker {\n+ public static void main(String[] args) throws IOException {\n+ BufferedReader in = new BufferedReader(new InputStreamReader(System.in));\n+ String line = in.readLine();\n+\n+ while (line != null) {\n+ \/\/ block stdin once we have seen the banner.\n+ if (line.contains(BANNER)) {\n+ while (true) {\n+ try {\n+ Thread.sleep(Long.MAX_VALUE);\n+ } catch (InterruptedException ie) {\/* skip on purpose *\/}\n+ }\n+ }\n+ line = in.readLine();\n+ }\n+ }\n+ }\n+\n+ public static void main(String[] args) throws Exception {\n+ \/\/ The simplest test is to use tty with software flow control. AsyncUL should not suspend JVM\n+ \/\/ with XOFF(Ctrl^s) to stdout. We can not assume tty is in use in the testing environments. It is also\n+ \/\/ not portable. Therefore, the test uses pipe to simulate suspending stdout.\n+ ProcessBuilder[] builders = {\n+ \/\/ Process 0 has to carefully avoid any output to stdout except Unified Logging.\n+ \/\/ We expect to demonstrate that process 0 with -Xlog:async can still terminate even though its stdout\n+ \/\/ is blocked.\n+ ProcessTools.createJavaProcessBuilder(\"-XX:+UnlockDiagnosticVMOptions\", \"-XX:AbortVMOnException=java.lang.RuntimeException\",\n+ \"-XX:+DisplayVMOutputToStderr\", \"-XX:+SuppressFatalErrorMessage\", \"-XX:-UsePerfData\", \"-Xlog:all=debug\",\n+ \"-Xlog:async\", \/\/ should hang without this!\n+ UserDefinedJavaProgram.class.getName()),\n+ ProcessTools.createJavaProcessBuilder(StdinBlocker.class.getName())\n+ };\n+\n+ List<Process> processes = ProcessBuilder.startPipeline(Arrays.asList(builders));\n+ \/\/ Process 0 should abort from Exceptions::debug_check_abort()\n+ int exitcode = processes.get(0).waitFor();\n+ \/\/ Exitcode may be 1 or 134.\n+ Asserts.assertNE(exitcode, Integer.valueOf(0));\n+ \/\/ Terminate StdinBlocker by force\n+ processes.get(1).destroyForcibly();\n+ }\n+}\n","filename":"test\/hotspot\/jtreg\/runtime\/logging\/BlockedLoggingTest.java","additions":164,"deletions":0,"binary":false,"changes":164,"status":"added"}]}
0 commit comments