[EXTERNAL] A question about memory order on x86_64
David Holmes
david.holmes at oracle.com
Tue Jun 22 23:41:07 UTC 2021
On 23/06/2021 3:11 am, Liu Xin wrote:
> > So they essentially lock out the change while they are writing - which
> means when the writing could block indefinitely then the change is also
> blocked indefinitely.
>
> Yes, this kind of synchronization is biased to readers(cheap). updater
> has to wait(expensive).
> I think it's fair enough, changing log configuration is rare.
>
> > Where is AsyncLogWriter::write participating in this RCU mechanism?
>
> when logsites enqueue messages to the async buffer, they use RCU
> counters as well.
>
> The intention of flush() is to flush all pending messages, either by
> flush() or by async log thread.
> _io_sem is a handshake protocol for two stakeholders. You showed me
> that this so-called "protocol" was broken if one thread invokes multiple
> flush() in a row.
>
> I changed flush() yesterday. It just inserts a special token to the
> buffer and waits for completion. This guarantees to
> serialize multiple flush() calls. I have run overnight, no problem is
> identified.
>
> How about this change?
> https://openjdk.github.io/cr/?repo=jdk&pr=4408&range=05#udiff-0-src/hotspot/share/logging/logAsyncWriter.cpp
> <https://openjdk.github.io/cr/?repo=jdk&pr=4408&range=05#udiff-0-src/hotspot/share/logging/logAsyncWriter.cpp>
>
> > I made the change to hold the io_sem for all of write() and the tests
> passed in tiers 6 and 7.
>
> What did you change?
I did io_sem.wait at the start of write(), so that it is executed
atomically, to completion.
David
-----
> P/V operations perform on io_sem(1) once every write(), but I feel it's
> difficult to control the scheduler.
>
>
>
>
> On Mon, Jun 21, 2021 at 5:39 PM David Holmes <david.holmes at oracle.com
> <mailto:david.holmes at oracle.com>> wrote:
>
> On 22/06/2021 9:56 am, Liu, Xin wrote:
> > Hi, David,
> >
> > LogConfiguration::configure_output() is intriguing. That's why I
> try to
> > explain/comment it in that PR.
> >
> > For sync logging, the RCU reader counter remains > 0 in "writing"
> > period. LogOutputList::Iterator is a RAII class. Its ctor calls
> > increase_readers() and dtor calls decrease_readers().
> >
> https://github.com/openjdk/jdk/blob/master/src/hotspot/share/logging/logTagSet.cpp#L77
> <https://github.com/openjdk/jdk/blob/master/src/hotspot/share/logging/logTagSet.cpp#L77>
> >
> > When the users decide to disable a LogOutput, they need to adjust
> levels
> > first. eg. change tagset 'gc' from DEBUG to OFF. There's a RCU
> > synchronization "wait_until_no_readers()" inside of
> > LogOutputList::update_output_level().
> >
> https://github.com/openjdk/jdk/blob/master/src/hotspot/share/logging/logConfiguration.cpp#L241
> <https://github.com/openjdk/jdk/blob/master/src/hotspot/share/logging/logConfiguration.cpp#L241>
> >
> > When this function returns, it means that all logsites have seen that
> > tagset's level change and all ongoing "writings" have done because
> > _active_readers was once back to zero. There're 200+ tagsets. Each
> > changed tagset needs a RCU synchronization.
> >
> > void LogOutputList::wait_until_no_readers() const {
> > OrderAccess::storeload();
> > while (_active_readers != 0) {
> > // Busy wait
> > }
> > }
> >
> > Of course, logsites are still active after
> wait_until_no_readers(), but
> > they have seen new levels for the tagsets. They won't emit logs
> to the
> > deleting LogOutput. Therefore, it's safe to delete it.
>
> So they essentially lock out the change while they are writing - which
> means when the writing could block indefinitely then the change is also
> blocked indefinitely:
>
> void LogTagSet::log(LogLevelType level, const char* msg) {
> LogDecorations decorations(level, *this, _decorators);
> for (LogOutputList::Iterator it = _output_list.iterator(level);
> it !=
> _output_list.end(); it++) {
> (*it)->write(decorations, msg);
> }
> }
>
> > I would like to make async log a natural extension of current
> lock-free
> > mechanism. The RCU counters only control "enqueuing" periods
> instead of
> > "writing" periods. As long as flush() works, it's still correct.
>
> Where is AsyncLogWriter::write participating in this RCU mechanism? The
> AsyncLogMessages that were enqueued, and have now been dequeued
> ready to
> be written, have already captured their "output", but there is nothing
> to protect those outputs. You can't guarantee that the flush() call
> handles all pre-existing enqueued messages, before that thread deletes
> the output. You would have to ensure that the write() from flush()
> waits
> for the AsyncLogWriter thread to complete any in-progress write() call
> before the flushing write() proceeds. I think you need to hold the
> io_sem across the entire write() to ensure it is executed atomically
> (we
> just need to make sure that can't lead to any deadlock scenarios with
> the other locking mechanisms in use.
>
> David
>
> > thanks,
> > --lx
> >
> >
> >
> > On 6/21/21 4:28 PM, David Holmes wrote:
> >> But we can't do that as we could block indefinitely while writing.
> >>
> >> Backing up a step lets ignore async logging for a moment. How does
> >> synchronous logging deal with this problem? What ensures that a log
> >> request can't use an output that is being concurrently deleted by a
> >> configuration change?
>
More information about the hotspot-runtime-dev
mailing list