[EXTERNAL] A question about memory order on x86_64
David Holmes
david.holmes at oracle.com
Tue Jun 22 11:32:07 UTC 2021
On 22/06/2021 10:39 am, David Holmes 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
>>
>>
>> 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
>>
>>
>> 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.
I made the change to hold the io_sem for all of write() and the tests
passed in tiers 6 and 7.
David
-----
> 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