From 577f318d824d91e5deb8b6b82dd211583cb93cac Mon Sep 17 00:00:00 2001
From: eapen <zhangyipeng7@huawei.com>
Date: Thu, 15 Dec 2022 10:37:31 +0800
Subject: [PATCH 18/33] I68TO2: 8229517: Support for optional asynchronous/buffered
 logging
---
 hotspot/src/os/windows/vm/os_windows.cpp        |   1 +
 hotspot/src/share/vm/runtime/arguments.cpp      |  10 ++
 hotspot/src/share/vm/runtime/globals.hpp        |   9 ++
 hotspot/src/share/vm/runtime/init.cpp           |   2 +
 hotspot/src/share/vm/runtime/logAsyncWriter.cpp | 164 ++++++++++++++++++++++++
 hotspot/src/share/vm/runtime/logAsyncWriter.hpp | 159 +++++++++++++++++++++++
 hotspot/src/share/vm/runtime/os.hpp             |   1 +
 hotspot/src/share/vm/runtime/thread.cpp         |  26 +++-
 hotspot/src/share/vm/runtime/vmStructs.cpp      |   2 +
 hotspot/src/share/vm/utilities/linkedlist.hpp   |  47 +++++--
 hotspot/src/share/vm/utilities/ostream.cpp      |  26 ++++
 hotspot/src/share/vm/utilities/ostream.hpp      |   3 +
 12 files changed, 440 insertions(+), 10 deletions(-)
 create mode 100644 hotspot/src/share/vm/runtime/logAsyncWriter.cpp
 create mode 100644 hotspot/src/share/vm/runtime/logAsyncWriter.hpp

diff --git a/hotspot/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp
index 25122de..cc31126 100644
--- a/hotspot/src/os/windows/vm/os_windows.cpp
+++ b/hotspot/src/os/windows/vm/os_windows.cpp
@@ -562,6 +562,7 @@ bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
     case os::pgc_thread:
     case os::cgc_thread:
     case os::watcher_thread:
+    case os::asynclog_thread:
       if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K);
       break;
     }
diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp
index 91e2ce0..fba3d4b 100644
--- a/hotspot/src/share/vm/runtime/arguments.cpp
+++ b/hotspot/src/share/vm/runtime/arguments.cpp
@@ -2269,6 +2269,16 @@ bool Arguments::verify_percentage(uintx value, const char* name) {
 // no gc log rotation when log file not supplied or
 // NumberOfGCLogFiles is 0
 void check_gclog_consistency() {
+  if (UseAsyncGCLog) {
+    if (Arguments::gc_log_filename() == NULL) {
+      jio_fprintf(defaultStream::output_stream(),
+                  "To enable Async GC log, use -Xloggc:<filename> -XX:UseAsyncGCLog\n"
+                  "Async GC log is turned off\n");
+      UseAsyncGCLog = false;
+
+    }
+  }
+
   if (UseGCLogFileRotation) {
     if ((Arguments::gc_log_filename() == NULL) || (NumberOfGCLogFiles == 0)) {
       jio_fprintf(defaultStream::output_stream(),
diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp
index 41b1392..10e4e7f 100644
--- a/hotspot/src/share/vm/runtime/globals.hpp
+++ b/hotspot/src/share/vm/runtime/globals.hpp
@@ -4104,6 +4104,15 @@ class CommandLineFlags {
                                                                             \
   JFR_ONLY(product(bool, LogJFR, false,                                     \
           "Enable JFR logging (consider +Verbose)"))                        \
+                                                                            \
+  product(bool, UseAsyncGCLog, false,                                       \
+          "Enable asynchronous GC logging")                                 \
+                                                                            \
+  product(uintx, AsyncLogBufferSize, 2*M,                                   \
+          "Memory budget (in bytes) for the buffer of Asynchronous")        \
+                                                                            \
+  diagnostic(bool, PrintAsyncGCLog, false,                                  \
+          "Print some information of Async GC Log")                         \

 /*
  *  Macros for factoring of globals
diff --git a/hotspot/src/share/vm/runtime/init.cpp b/hotspot/src/share/vm/runtime/init.cpp
index d2e0f22..b185409 100644
--- a/hotspot/src/share/vm/runtime/init.cpp
+++ b/hotspot/src/share/vm/runtime/init.cpp
@@ -32,6 +32,7 @@
 #include "runtime/handles.inline.hpp"
 #include "runtime/icache.hpp"
 #include "runtime/init.hpp"
+#include "runtime/logAsyncWriter.hpp"
 #include "runtime/safepoint.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "services/memTracker.hpp"
@@ -106,6 +107,7 @@ jint init_globals() {
   if (status != JNI_OK)
     return status;

+  AsyncLogWriter::initialize();
   interpreter_init(); // before any methods loaded
   invocationCounter_init();  // before any methods loaded
   marksweep_init();
diff --git a/hotspot/src/share/vm/runtime/logAsyncWriter.cpp b/hotspot/src/share/vm/runtime/logAsyncWriter.cpp
new file mode 100644
index 0000000..750a23f
--- /dev/null
+++ b/hotspot/src/share/vm/runtime/logAsyncWriter.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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.
+ *
+ */
+#include "precompiled.hpp"
+#include "runtime/atomic.hpp"
+#include "runtime/logAsyncWriter.hpp"
+#include "utilities/ostream.hpp"
+
+class AsyncLogWriter::AsyncLogLocker : public StackObj {
+ public:
+  AsyncLogLocker() {
+    assert(_instance != NULL, "AsyncLogWriter::_lock is unavailable");
+    _instance->_lock.wait();
+  }
+
+  ~AsyncLogLocker() {
+    _instance->_lock.signal();
+  }
+};
+
+void AsyncLogWriter::enqueue_locked(const AsyncLogMessage& msg) {
+  if (_buffer.size() >= _buffer_max_size)  {
+    // drop the enqueueing message.
+    os::free(msg.message());
+    return;
+  }
+
+  assert(_buffer.size() < _buffer_max_size, "_buffer is over-sized.");
+  _buffer.push_back(msg);
+  _sem.signal();
+}
+
+void AsyncLogWriter::enqueue(const char* msg) {
+  AsyncLogMessage m(os::strdup(msg));
+
+  { // critical area
+    AsyncLogLocker locker;
+    enqueue_locked(m);
+  }
+}
+
+AsyncLogWriter::AsyncLogWriter()
+  : NamedThread(),
+  _lock(1), _sem(0), _io_sem(1),
+  _initialized(false),
+  _buffer_max_size(AsyncLogBufferSize / sizeof(AsyncLogMessage)) {
+  if (os::create_thread(this, os::asynclog_thread)) {
+    _initialized = true;
+    set_name("AsyncLog Thread");
+  } else {
+    if (PrintAsyncGCLog) {
+        tty->print_cr("AsyncLogging failed to create thread. Falling back to synchronous logging.");
+    }
+  }
+
+  if (PrintAsyncGCLog) {
+    tty->print_cr("The maximum entries of AsyncLogBuffer: " SIZE_FORMAT ", estimated memory use: " SIZE_FORMAT " bytes",
+                      _buffer_max_size, AsyncLogBufferSize);
+  }
+}
+
+void AsyncLogWriter::write() {
+  // Use kind of copy-and-swap idiom here.
+  // Empty 'logs' swaps the content with _buffer.
+  // Along with logs destruction, all processed messages are deleted.
+  //
+  // The operation 'pop_all()' is done in O(1). All I/O jobs are then performed without
+  // lock protection. This guarantees I/O jobs don't block logsites.
+  AsyncLogBuffer logs;
+  bool own_io = false;
+
+  { // critical region
+    AsyncLogLocker locker;
+
+    _buffer.pop_all(&logs);
+    own_io = _io_sem.trywait();
+  }
+
+  LinkedListIterator<AsyncLogMessage> it(logs.head());
+  if (!own_io) {
+    _io_sem.wait();
+  }
+
+  bool flush = false;
+  while (!it.is_empty()) {
+    AsyncLogMessage* e = it.next();
+    char* msg = e->message();
+
+    if (msg != NULL) {
+      flush = true;
+      ((gcLogFileStream*)gclog_or_tty)->write_blocking(msg, strlen(msg));
+      os::free(msg);
+    }
+  }
+  if (flush) {
+    ((gcLogFileStream*)gclog_or_tty)->fileStream::flush();
+  }
+  _io_sem.signal();
+}
+
+void AsyncLogWriter::run() {
+  while (true) {
+    // The value of a semphore cannot be negative. Therefore, the current thread falls asleep
+    // when its value is zero. It will be waken up when new messages are enqueued.
+    _sem.wait();
+    write();
+  }
+}
+
+AsyncLogWriter* AsyncLogWriter::_instance = NULL;
+
+void AsyncLogWriter::initialize() {
+  if (!UseAsyncGCLog) return;
+
+  assert(_instance == NULL, "initialize() should only be invoked once.");
+
+  AsyncLogWriter* self = new AsyncLogWriter();
+  if (self->_initialized) {
+    OrderAccess::release_store_ptr(&AsyncLogWriter::_instance, self);
+    os::start_thread(self);
+    if (PrintAsyncGCLog) {
+      tty->print_cr("Async logging thread started.");
+    }
+  }
+}
+
+AsyncLogWriter* AsyncLogWriter::instance() {
+  return _instance;
+}
+
+// write() acquires and releases _io_sem even _buffer is empty.
+// This guarantees all logging I/O of dequeued messages are done when it returns.
+void AsyncLogWriter::flush() {
+  if (_instance != NULL) {
+    _instance->write();
+  }
+}
+
+void AsyncLogWriter::print_on(outputStream* st) const{
+  st->print("\"%s\" ", name());
+  Thread::print_on(st);
+  st->cr();
+}
diff --git a/hotspot/src/share/vm/runtime/logAsyncWriter.hpp b/hotspot/src/share/vm/runtime/logAsyncWriter.hpp
new file mode 100644
index 0000000..5242426
--- /dev/null
+++ b/hotspot/src/share/vm/runtime/logAsyncWriter.hpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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.
+ *
+ */
+#ifndef SHARE_VM_RUNTIME_LOGASYNCWRITER_HPP
+#define SHARE_VM_RUNTIME_LOGASYNCWRITER_HPP
+#include "memory/resourceArea.hpp"
+#include "runtime/semaphore.hpp"
+#include "utilities/linkedlist.hpp"
+
+template <typename E, MEMFLAGS F>
+class LinkedListDeque : private LinkedListImpl<E, ResourceObj::C_HEAP, F> {
+ private:
+  LinkedListNode<E>* _tail;
+  size_t _size;
+
+ public:
+  LinkedListDeque() : _tail(NULL), _size(0) {}
+  void push_back(const E& e) {
+    if (!_tail) {
+      _tail = this->add(e);
+    } else {
+      _tail = this->insert_after(e, _tail);
+    }
+
+    ++_size;
+  }
+
+  // pop all elements to logs.
+  void pop_all(LinkedList<E>* logs) {
+    logs->move(static_cast<LinkedList<E>* >(this));
+    _tail = NULL;
+    _size = 0;
+  }
+
+  void pop_all(LinkedListDeque<E, F>* logs) {
+    logs->_size = _size;
+    logs->_tail = _tail;
+    pop_all(static_cast<LinkedList<E>* >(logs));
+  }
+
+  void pop_front() {
+    LinkedListNode<E>* h = this->unlink_head();
+    if (h == _tail) {
+      _tail = NULL;
+    }
+
+    if (h != NULL) {
+      --_size;
+      this->delete_node(h);
+    }
+  }
+
+  size_t size() const { return _size; }
+
+  const E* front() const {
+    return this->_head == NULL ? NULL : this->_head->peek();
+  }
+
+  const E* back() const {
+    return _tail == NULL ? NULL : _tail->peek();
+  }
+
+  LinkedListNode<E>* head() const {
+    return this->_head;
+  }
+};
+
+class AsyncLogMessage {
+  char* _message;
+
+public:
+  AsyncLogMessage(char* msg)
+    : _message(msg) {}
+
+  // placeholder for LinkedListImpl.
+  bool equals(const AsyncLogMessage& o) const { return false; }
+
+  char* message() const { return _message; }
+};
+
+typedef LinkedListDeque<AsyncLogMessage, mtInternal> AsyncLogBuffer;
+
+//
+// ASYNC LOGGING SUPPORT
+//
+// Summary:
+// Async Logging is working on the basis of singleton AsyncLogWriter, which manages an intermediate buffer and a flushing thread.
+//
+// Interface:
+//
+// initialize() is called once when JVM is initialized. It creates and initializes the singleton instance of AsyncLogWriter.
+// Once async logging is established, there's no way to turn it off.
+//
+// instance() is MT-safe and returns the pointer of the singleton instance if and only if async logging is enabled and has well
+// initialized. Clients can use its return value to determine async logging is established or not.
+//
+// The basic operation of AsyncLogWriter is enqueue(). 2 overloading versions of it are provided to match LogOutput::write().
+// They are both MT-safe and non-blocking. Derived classes of LogOutput can invoke the corresponding enqueue() in write() and
+// return 0. AsyncLogWriter is responsible of copying neccessary data.
+//
+// The static member function flush() is designated to flush out all pending messages when JVM is terminating.
+// In normal JVM termination, flush() is invoked in LogConfiguration::finalize(). flush() is MT-safe and can be invoked arbitrary
+// times. It is no-op if async logging is not established.
+//
+class AsyncLogWriter : public NamedThread {
+  class AsyncLogLocker;
+
+  static AsyncLogWriter* _instance;
+  // _lock(1) denotes a critional region.
+  Semaphore _lock;
+  // _sem is a semaphore whose value denotes how many messages have been enqueued.
+  // It decreases in AsyncLogWriter::run()
+  Semaphore _sem;
+  // A lock of IO
+  Semaphore _io_sem;
+
+  volatile bool _initialized;
+  AsyncLogBuffer _buffer;
+
+  const size_t _buffer_max_size;
+
+  AsyncLogWriter();
+  void enqueue_locked(const AsyncLogMessage& msg);
+  void write();
+  void run();
+
+ public:
+  void enqueue(const char* msg);
+
+  static AsyncLogWriter* instance();
+  static void initialize();
+  static void flush();
+  // Printing
+  void print_on(outputStream* st) const;
+
+};
+
+#endif // SHARE_LOGGING_LOGASYNCWRITER_HPP
diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp
index acc57f4..5f41e96 100644
--- a/hotspot/src/share/vm/runtime/os.hpp
+++ b/hotspot/src/share/vm/runtime/os.hpp
@@ -463,6 +463,7 @@ class os: AllStatic {
     java_thread,
     compiler_thread,
     watcher_thread,
+    asynclog_thread, // dedicated to flushing logs
     os_thread
   };

diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp
index cacab59..61627e4 100644
--- a/hotspot/src/share/vm/runtime/thread.cpp
+++ b/hotspot/src/share/vm/runtime/thread.cpp
@@ -57,6 +57,7 @@
 #include "runtime/java.hpp"
 #include "runtime/javaCalls.hpp"
 #include "runtime/jniPeriodicChecker.hpp"
+#include "runtime/logAsyncWriter.hpp"
 #include "runtime/memprofiler.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "runtime/objectMonitor.hpp"
@@ -881,7 +882,9 @@ void Thread::print_on_error(outputStream* st, char* buf, int buflen) const {
   else if (is_GC_task_thread())             st->print("GCTaskThread");
   else if (is_Watcher_thread())             st->print("WatcherThread");
   else if (is_ConcurrentGC_thread())        st->print("ConcurrentGCThread");
-  else st->print("Thread");
+  else if (this == AsyncLogWriter::instance()) {
+    st->print("%s", this->name());
+  } else st->print("Thread");

   st->print(" [stack: " PTR_FORMAT "," PTR_FORMAT "]",
             _stack_base - _stack_size, _stack_base);
@@ -4387,6 +4390,12 @@ void Threads::print_on(outputStream* st, bool print_stacks, bool internal_format
     st->cr();
   }
   CompileBroker::print_compiler_threads_on(st);
+  if (UseAsyncGCLog) {
+    AsyncLogWriter* aio_writer = AsyncLogWriter::instance();
+    if (aio_writer != NULL) {
+      aio_writer->print_on(st);
+    }
+  }
   st->flush();
 }

@@ -4432,6 +4441,21 @@ void Threads::print_on_error(outputStream* st, Thread* current, char* buf, int b
     wt->print_on_error(st, buf, buflen);
     st->cr();
   }
+
+  if (UseAsyncGCLog) {
+    AsyncLogWriter* aio_writer = AsyncLogWriter::instance();
+    if (aio_writer != NULL) {
+      bool is_current = (current == aio_writer);
+      found_current = found_current || is_current;
+      st->print("%s", is_current ? "=>" : "  ");
+
+      st->print(PTR_FORMAT, aio_writer);
+      st->print(" ");
+      aio_writer->print_on_error(st, buf, buflen);
+      st->cr();
+    }
+  }
+
   if (!found_current) {
     st->cr();
     st->print("=>" PTR_FORMAT " (exited) ", current);
diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp
index ab20f5c..5d1cf2b 100644
--- a/hotspot/src/share/vm/runtime/vmStructs.cpp
+++ b/hotspot/src/share/vm/runtime/vmStructs.cpp
@@ -97,6 +97,7 @@
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/stubRoutines.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/logAsyncWriter.hpp"
 #include "runtime/virtualspace.hpp"
 #include "runtime/vmStructs.hpp"
 #include "utilities/array.hpp"
@@ -1599,6 +1600,7 @@ typedef TwoOopHashtable<Symbol*, mtClass>     SymbolTwoOopHashtable;
            declare_type(Thread, ThreadShadow)                             \
            declare_type(NamedThread, Thread)                              \
            declare_type(WatcherThread, Thread)                            \
+           declare_type(AsyncLogWriter, Thread)                           \
            declare_type(JavaThread, Thread)                               \
            declare_type(JvmtiAgentThread, JavaThread)                     \
            declare_type(ServiceThread, JavaThread)                        \
diff --git a/hotspot/src/share/vm/utilities/linkedlist.hpp b/hotspot/src/share/vm/utilities/linkedlist.hpp
index a76c15c..f4f2a9b 100644
--- a/hotspot/src/share/vm/utilities/linkedlist.hpp
+++ b/hotspot/src/share/vm/utilities/linkedlist.hpp
@@ -40,6 +40,25 @@ template <class E> class LinkedListNode : public ResourceObj {
   E                       _data;  // embedded content
   LinkedListNode<E>*      _next;  // next entry

+  // Select member function 'bool U::equals(const U&) const' if 'U' is of class
+  // type. This works because of the "Substitution Failure Is Not An Error"
+  // (SFINAE) rule. Notice that this version of 'equal' will also be chosen for
+  // class types which don't define a corresponding 'equals()' method (and will
+  // result in a compilation error for them). It is not easily possible to
+  // specialize this 'equal()' function exclusively for class types which define
+  // the correct 'equals()' function because that function can be in a base
+  // class, a dependent base class or have a compatible but slightly different
+  // signature.
+  template <class U>
+  static bool equal(const U& a, const U& b, bool (U::*t)(const U&) const) {
+    return a.equals(b);
+  }
+
+  template <class U>
+  static bool equal(const U& a, const U& b, ...) {
+    return a == b;
+  }
+
  protected:
   LinkedListNode() : _next(NULL) { }

@@ -51,6 +70,10 @@ template <class E> class LinkedListNode : public ResourceObj {

   E*  data() { return &_data; }
   const E* peek() const { return &_data; }
+
+  bool equals(const E& t) const {
+    return equal<E>(_data, t, NULL);
+  }
 };

 // A linked list interface. It does not specify
@@ -62,6 +85,7 @@ template <class E> class LinkedList : public ResourceObj {

  public:
   LinkedList() : _head(NULL) { }
+  virtual ~LinkedList() {}

   inline void set_head(LinkedListNode<E>* h) { _head = h; }
   inline LinkedListNode<E>* head() const     { return _head; }
@@ -182,7 +206,7 @@ template <class E, ResourceObj::allocation_type T = ResourceObj::C_HEAP,

   virtual LinkedListNode<E>* find_node(const E& e) {
     LinkedListNode<E>* p = this->head();
-    while (p != NULL && !p->peek()->equals(e)) {
+    while (p != NULL && !p->equals(e)) {
       p = p->next();
     }
     return p;
@@ -229,7 +253,7 @@ template <class E, ResourceObj::allocation_type T = ResourceObj::C_HEAP,
      LinkedListNode<E>* prev = NULL;

      while (tmp != NULL) {
-       if (tmp->peek()->equals(e)) {
+       if (tmp->equals(e)) {
          return remove_after(prev);
        }
        prev = tmp;
@@ -396,16 +420,21 @@ template <class E, int (*FUNC)(const E&, const E&),
 // Iterates all entries in the list
 template <class E> class LinkedListIterator : public StackObj {
  private:
-  LinkedListNode<E>* _p;
-  bool               _is_empty;
+  mutable LinkedListNode<E>* _p;
+
  public:
-  LinkedListIterator(LinkedListNode<E>* head) : _p(head) {
-    _is_empty = (head == NULL);
-  }
+  LinkedListIterator(LinkedListNode<E>* head) : _p(head) { }
+
+  bool is_empty() const { return _p == NULL; }

-  bool is_empty() const { return _is_empty; }
+  E* next() {
+    if (_p == NULL) return NULL;
+    E* e = _p->data();
+    _p = _p->next();
+    return e;
+  }

-  const E* next() {
+  const E* next() const {
     if (_p == NULL) return NULL;
     const E* e = _p->peek();
     _p = _p->next();
diff --git a/hotspot/src/share/vm/utilities/ostream.cpp b/hotspot/src/share/vm/utilities/ostream.cpp
index 14d82ad..5d40559 100644
--- a/hotspot/src/share/vm/utilities/ostream.cpp
+++ b/hotspot/src/share/vm/utilities/ostream.cpp
@@ -30,6 +30,7 @@
 #include "runtime/mutexLocker.hpp"
 #include "runtime/os.hpp"
 #include "runtime/vmThread.hpp"
+#include "runtime/logAsyncWriter.hpp"
 #include "utilities/defaultStream.hpp"
 #include "utilities/ostream.hpp"
 #include "utilities/top.hpp"
@@ -876,6 +877,17 @@ gcLogFileStream::gcLogFileStream(const char* file_name) : _file_lock(NULL) {
 }

 void gcLogFileStream::write(const char* s, size_t len) {
+  if (UseAsyncGCLog) {
+    AsyncLogWriter* aio_writer = AsyncLogWriter::instance();
+    if (aio_writer != NULL) {
+      aio_writer->enqueue(s);
+      return;
+    }
+  }
+  write_blocking(s, len);
+}
+
+void gcLogFileStream::write_blocking(const char* s, size_t len) {
   if (_file != NULL) {
     // we can't use Thread::current() here because thread may be NULL
     // in early stage(ostream_init_log)
@@ -1047,6 +1059,17 @@ void gcLogFileStream::rotate_log_impl(bool force, outputStream* out) {
   }
 }

+void gcLogFileStream::flush() {
+  if (UseAsyncGCLog) {
+    AsyncLogWriter* aio_writer = AsyncLogWriter::instance();
+    if (aio_writer != NULL) {
+      // do nothing
+      return;
+    }
+  }
+  fileStream::flush();
+}
+
 defaultStream* defaultStream::instance = NULL;
 int defaultStream::_output_fd = 1;
 int defaultStream::_error_fd  = 2;
@@ -1456,6 +1479,9 @@ void ostream_exit() {

 // ostream_abort() is called by os::abort() when VM is about to die.
 void ostream_abort() {
+  if (UseAsyncGCLog) {
+    AsyncLogWriter::flush();
+  }
   // Here we can't delete gclog_or_tty and tty, just flush their output
   if (gclog_or_tty) gclog_or_tty->flush();
   if (tty) tty->flush();
diff --git a/hotspot/src/share/vm/utilities/ostream.hpp b/hotspot/src/share/vm/utilities/ostream.hpp
index d0f9aac..85ff599 100644
--- a/hotspot/src/share/vm/utilities/ostream.hpp
+++ b/hotspot/src/share/vm/utilities/ostream.hpp
@@ -254,6 +254,7 @@ class gcLogFileStream : public fileStream {
   gcLogFileStream(const char* file_name);
   ~gcLogFileStream();
   virtual void write(const char* c, size_t len);
+  void write_blocking(const char* c, size_t len);
   virtual void rotate_log(bool force, outputStream* out = NULL);
   void dump_loggc_header();

@@ -263,6 +264,8 @@ class gcLogFileStream : public fileStream {
              ((GCLogFileSize != 0) && ((uintx)_bytes_written >= GCLogFileSize));
   }

+  virtual void flush();
+
 };

 #ifndef PRODUCT
--
1.8.3.1
