RFR: 8197812: (ref) Data race in Finalizer

Peter Levart peter.levart at gmail.com
Wed Feb 14 09:58:57 UTC 2018


I take back this claim. Of course the the following race is possible:

- Thread1: calls runAllFinalizers and takes a Finalizer from 
'unprocessed' list.
- Thread2: takee the same Finalizer instance from ReferenceQueue and 
calls runFinalizer()
- Thread1: calls runFinalizer() with the same instance for the 2nd time now.

Regards, Peter

On 02/14/2018 10:39 AM, Peter Levart wrote:
> I could even claim that simplifying the if statement in remove() to:
>
>     if (unfinalized == this) {
>         unfinalized = this.next;
>     }
>
> makes checking for hasBeenFinalized() in runFinalizer() redundant as 
> it would not be possible for runFinalizer() to be called more than 
> once for each Finalizer instance because:
> - ReferenceQueue never returns the same Reference instance twice or 
> more times.
> - 'unfinalized' will never point back to the same Finalizer instance 
> for the 2nd time, because it always "travels" in the forward direction 
> (unfinalized = unfinalized.next).
>
> Regards, Peter
>
> (I rest now).
>
> On 02/14/2018 10:24 AM, Peter Levart wrote:
>> Hi,
>>
>> I may have found a situation described here...
>>
>> On 02/14/2018 09:47 AM, Peter Levart wrote:
>>>
>>> When Finalizer(s) are taken from ReferenceQueue, they are processed 
>>> in arbitrary order, so once in a while it can happen that the 
>>> Finalizer at the head of the list (pointed to by 'unfinalized') is 
>>> processed this way. The body of the 1st if statement in above method 
>>> is executed in such situation:
>>>
>>>             if (unfinalized == this) {
>>>                 if (this.next != null) {
>>>                     unfinalized = this.next;
>>>                 } else {
>>>                     unfinalized = this.prev;
>>>                 }
>>>             }
>>>
>>> But I can't figure out a situation when this.next would be null 
>>> while this.prev would be non-null. In other words, when 
>>> 'unfinalized' would not point to the head of the list. 
>>
>> This is the situation:
>>
>> unfinalized
>>     |
>>     v
>> Finalizer[1].next --> Finalizer[2].next --> null
>> Finalizer[2].prev --> Finalizer[1].prev --> null
>>
>> runAllFinalizers() is executed and the 1st pass of the for loop takes 
>> the Finalizer[1] from the 'unfinalized' pointer and moves the pointer 
>> to Finalizer[2]:
>>
>> unfinalized ----------------
>>                            |
>>                            v
>> Finalizer[1].next --> Finalizer[2].next --> null
>> Finalizer[2].prev --> Finalizer[1].prev --> null
>>
>> ...now finalizer thread kicks-in and takes the Finalizer[2] from the 
>> ReferenceQueue and calls remove(). The 1st if statement in remove() 
>> would evaluate the condition to true, as 'unfinalized' now points to 
>> Finalizer[2], so the body of 1st if would make unfinalized point back 
>> to Finalizer[1]:
>>
>> unfinalized
>>     |
>>     v
>> Finalizer[1].next --> Finalizer[2].next --> null
>> Finalizer[2].prev --> Finalizer[1].prev --> null
>>
>> ...and the remaining remove() code would unlink the Finalizer[2] from 
>> the list, resulting in this state of the list:
>>
>> unfinalized
>>     |
>>     v
>> Finalizer[1].next --> null
>> Finalizer[1].prev --> null
>>
>> This might be seen as a "correct" state, because Finalizer[1] has not 
>> been removed from the list yet although it is in the process of being 
>> removed because the next thing runAllFinalizers() loop would do is it 
>> would call runFinalizer() on it. So the simplified handling in remove():
>>
>>     if (unfinalized == this) {
>>         unfinalized = this.next;
>>     }
>>
>> ...would result in correct behavior too. Even more so because if the 
>> 2nd call to runAllFinalizers() was being executed concurrently (I 
>> don't know if this is possible though), the Finalizer[1] from above 
>> situation would be taken by the concurrent call again and its 
>> runFinalizer() executed. runFinalizer() is idempotent so no harm done 
>> here, but...
>>
>> Regards, Peter
>>
>



More information about the core-libs-dev mailing list