RFR: 8315559: Delay TempSymbol cleanup to avoid symbol table churn [v4]
Coleen Phillimore
coleenp at openjdk.org
Tue Nov 7 20:03:26 UTC 2023
On Wed, 1 Nov 2023 16:34:23 GMT, Oli Gillespie <ogillespie at openjdk.org> wrote:
>> Coleen -
>>
>> I think NBQ is a reasonable choice for use here. But it's not a complete
>> solution on its own. It imposes documented requirements on clients. I don't
>> think we have a different data structure for this purpose (thread-safe FIFO
>> without locks), so any alternative would need to be invented, and would be
>> solving the same problem as NBQ and the surrounding client-provided code.
>>
>> Oliver -
>>
>> The current usage is not safe. The reuse can occur through the allocator. For
>> example, one thread starts a pop. Another thread steals that pop, then deletes
>> the object. Later, an allocation gets a new node at the same address as the
>> deleted node. That newly allocated node makes its way through the queue to
>> eventually become visible to that first thread's still in-progress pop. (So
>> this is an SMR bug. You generally can't delete an object while some other
>> thread might be looking at it.)
>>
>> GlobalCounter is not a locking mechanism. It is an RCU-style synchronization
>> mechanism, so related to but different from RWLocks. In particular, readers
>> (threads in a critical section) never block due to this mechanism - only
>> write_synchronize blocks.
>>
>> A problem with using GlobalCounter in that simplistic way is that once the
>> queue is "full", the one-in-one-out policy is going to have every allocation
>> hit GlobalCounter::write_synchronize (a potentially somewhat expensive
>> operation, since it needs to iterate over all threads), at least until the
>> queue is bulk drained. Switching over to a one-in-N-out policy could ameliate
>> that by batching the synchronizes over several nodes, and also remove the need
>> for complete bulk draining. Have min/max queue size and switching between
>> insert-only and one-in-N-out policies depending on the current size seems like
>> a possible solution.
>
> Thanks for all the details, I hadn't considered the SMR angle. I'll think about alternatives.
In a brief conversation with Kim, where I bemoaned that there should be a simpler solution, he suggested maybe a fixed ring buffer with a xchg to replace the n'th element, like:
const uint N = ...;
Symbol* volatile _delay_queue[N]; // initialize to nullptr
volatile uint _index = 0;
void add(Symbol* s) {
... increment refcount for s
uint i = Atomic::add(&_index, 1u) % N;
Symbol* old = Atomic::xchg(&_delay_queue[i], s);
if (old != nullptr) {
... decrement refcount for old, possibly deleting it
}
}
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/16398#discussion_r1385484881
More information about the hotspot-dev
mailing list