RFR (M) 8195099: Concurrent safe-memory-reclamation mechanism
David Holmes
david.holmes at oracle.com
Wed Apr 11 10:57:15 UTC 2018
Hi Robbin,
On 11/04/2018 8:02 PM, Robbin Ehn wrote:
> On 04/11/2018 11:26 AM, David Holmes wrote:
>> On 11/04/2018 6:01 PM, Kim Barrett wrote:
>>>> On Apr 10, 2018, at 9:34 PM, David Holmes <david.holmes at oracle.com>
>>>> wrote:
>>>>
>>>> Hi Robin,
>>>>
>>>> On 10/04/2018 10:18 PM, Robbin Ehn wrote:
>>>>> Hi all,
>>>>> We have moved the global-counter to a separate change-set. The
>>>>> global-counter
>>>>> uses a counter to determine current generation. Any reader needs to
>>>>> have a local
>>>>> counter for which generation is currently read. By increment the
>>>>> global-counter
>>>>> and scan for threads reading an old generation and wait for them to
>>>>> complete, we
>>>>> know when an old generation is not visible (no pre-existing
>>>>> reader). In RCU
>>>>> terms, this creates a grace-period. Making this mechanism suitable
>>>>> for a read-mostly scenario. In this initial change-set we scan
>>>>> JavaThreads and the VMThread.
>>>>
>>>> Sorry but I don't understand how this works. If a reader calls:
>>>>
>>>> 31 inline void GlobalCounter::critical_section_begin(Thread
>>>> *thread) {
>>>> 32 assert(thread == Thread::current(), "must be current thread");
>>>> 33 assert(thread->is_VM_thread() || thread->is_Java_thread(),
>>>> "must be VMThread or JavaThread");
>>>> 34 assert((*thread->get_rcu_counter() & COUNTER_ACTIVE) == 0x0,
>>>> "nestled critical sections, not supported yet");
>>>> 35 volatile uintx gbl_cnt =
>>>> OrderAccess::load_acquire(&_global_counter._counter);
>>>> 36 OrderAccess::release_store_fence(thread->get_rcu_counter(),
>>>> gbl_cnt + 1);
>>>> 37 }
>>>>
>>>> and is preempted before the store at line 36, the writer will not
>>>> see it and can go ahead and free the data used in the critical
>>>> section. The reader does no validation of the counter value and so
>>>> continues in to the critical section. Surely there has to be a
>>>> handshake between the reader and writer, where the reader signals
>>>> their intention to enter a critical section for generation X, then
>>>> re-reads the generation count to check it has not changed. ??
>>>
>>> A writer updates its protected shared state and then waits for any
>>> readers that might see the previous value of that shared state. That
>>> is, any reader that is in the critical section (has the active bit set
>>> in its local counter) *and* entered before the shared state was
>>> updated. The latter is conservatively approximated by actually
>>> checking whether the reader entered the critical section before the
>>> wait started, with the wait ordered after the shared state update.
>>>
>>> The wait check implements the latter condition via a range test. If
>>> the reader entered before the wait, then it's local counter is "less
>>> than" the writer's updated counter. This is implementated by an
>>> unsigned subtract and checking for a large result. (With this
>>> implementation there is a limit on just how stale the reader can be; a
>>> reader must complete a critical section before max_uintx/4 writers
>>> wait.)
>>>
>>> When a writer updates protected shared state, increments the global
>>> counter, and then checks / waits for readers,
>>>
>>> (1) If a reader obtained a global counter value before that increment,
>>> but has not yet set its local state to indicate it is active, then the
>>> writer will not wait for it. But that's okay, because when the reader
>>> gets around to entering the critical region and examining the
>>> protected shared state, it is guaranteed not to see the old value of
>>> the protected shared state.
>>>
>>> (2) If a reader obtained a global counter value before that increment
>>> and marked itself active, then the writer will wait until the reader
>>> exits the critical section. The reader might see the old value of the
>>> protected shared state while in the critical region.
>>>
>>> (3) If a reader obtained a global counter value after that increment,
>>> then it is guaranteed not to see the old value of the protected shared
>>> state.
>>
>> Sorry Kim but I can't quite follow all that. What is the "shared
>> state" you are referring to?
>>
>> If a reader executes line 35 to read the current global counter and
>> then is preempted before storing that into thread->get_rcu_counter(),
>> then there is nothing for the writer to see - it has no knowledge of
>> this about-to-be reader.
>
> Hi,
>
> Thanks Kim for the explanation.
>
> A generation X for reader A don't have to be identical with generation X
> for reader B. We do not guarantee that a reader will see generation X,
> it might see a newer one. But it will be 'stable'. In your context
> switch case the reader of generation X might see generation
> X+(2*n_generation). The write-side do not know what the reader actually
> sees, but it knows it _might_ see the memory for which an ABA problem
> exists, therefore needs to wait with the reclamation.
>
> In summary generation X is oldest possible visible generation for a reader.
>
> If that helps David?
Sorry no, I don't understand what this counter is doing at all. I feel I
need to see what is between the begin/end critical section for this to
make any sense to me. And to see what the writer actually does.
David
-----
> /Robbin
>
>>
>> David
>>
>>>
More information about the hotspot-dev
mailing list