RFR: 8323807: Async UL: Add a stalling mode to async UL [v7]
David Holmes
dholmes at openjdk.org
Wed Jan 22 05:14:35 UTC 2025
On Tue, 21 Jan 2025 13:35:21 GMT, Johan Sjölen <jsjolen at openjdk.org> wrote:
>> Hi,
>>
>> In January of this year I took a stab at implementing a stalling mode for UL, see link: https://github.com/openjdk/jdk/pull/17757 . I also talked about this feature in the mailing lists and seemed to receive positive feedback. With that PR, I also implemented a circular buffer. This PR didn't go through because 1. The stalling mode was broken 2. The complexity was a bit too large imho.
>>
>> This PR does a much smaller change by only focusing on implementing the actual stalling.
>>
>> The addition in terms of command line changes are the same as before, you can now specify the mode of your async logging:
>>
>>
>> $ java -Xlog:async:drop # Dropping mode, same as today
>> $ java -Xlog:async:stall # Stalling mode!
>> $ java -Xlog:async # Dropping mode by default still
>>
>>
>> The change in protocol is quite simple. If a producer thread `P` cannot fit a message into the buffer, it `malloc`s a message and exposes it via a shared pointer. It blocks all other producer threads from writing into the buffer. At the same time, the consumer thread (`AsyncLogWriter`) will perform all writing. When the consumer thread has emptied the write buffer, it writes the stalled message, notifies `P` and releases all locks. `P` then let's all other producer threads continue.
>>
>> We do this by having two locks: `Outer` and `Inner`. In our example above, `P` prevents any other producers from progressing by holding the outer lock, but allows the consumer thread to progress by releasing the inner lock.
>>
>> In pseudo-code we have something like this in the stalling case.
>>
>>
>> void produce() {
>> OuterLock olock;
>> InnerLock ilock;
>> bool out_of_memory = attempt_produce(shared_buffer);
>> if (out_of_memory) {
>> pmsg = new Message();
>> shared_message = pmsg;
>> while (shared_message != nullptr) ilock.wait();
>> free(pmsg);
>> }
>> }
>>
>> void consume() {
>> InnerLock ilock;
>> consume(shared_buffer);
>> if (shared_message != nullptr) {
>> consume(shared_message);
>> ilock.notify();
>> }
>> }
>>
>>
>> *Note!* It is very important that the consumer prints all output found in the buffer before printing the stalled message. This is because logging is output in Program Order. In other words: `print(m0); print(m1);` means that `m0` must appear before `m1` in the log file.
>>
>> *Note!* Yes, we do force *all* threads to stall before the original stalled message has been printed. This isn't optimal, but I still have hope that we can switch to a faster circu...
>
> Johan Sjölen has updated the pull request incrementally with one additional commit since the last revision:
>
> Use some RAM instead
This all seems quite reasonable to me. Thanks for the updates.
src/hotspot/share/logging/logConfiguration.cpp line 646:
> 644: out->print_cr(" A mode, either 'drop' or 'stall', may be provided. If 'drop' is provided then"
> 645: " messages will be dropped if there is no room in the intermediate buffer,"
> 646: " if 'stall' is provided then the log operation will wait for room to be made by the output thread."
Suggestion:
" messages will be dropped if there is no room in the intermediate buffer."
" If 'stall' is provided then the log operation will wait for room to be made by the output thread."
-------------
Marked as reviewed by dholmes (Reviewer).
PR Review: https://git.openjdk.org/jdk/pull/22770#pullrequestreview-2566187605
PR Review Comment: https://git.openjdk.org/jdk/pull/22770#discussion_r1924693168
More information about the hotspot-runtime-dev
mailing list