[concurrency-interest] DirectByteBuffers and reachabilityFence

Timo Kinnunen timo.kinnunen at gmail.com
Sat Dec 12 03:38:35 EST 2015


There’s many ways DBB.get() could be written and many ways a compiler could transform it when inlining it inside a calling method. The implementation that I looked at returns the receiver as the method call completes. My position is that within such a method, the range where the receiver is reachable is already as large as it can ever be and inserting a reachabilityFence can’t and won’t make it any larger. If it didn’t return the receiver the situation would be completely different. I can easily observe the receiver getting garbage collected in under a second while an instance method is executing when that method returns a null value that no-one reads. If it returns the receiver – still with no-one to read it -- then I can’t make it get finalized before it returns. Not even if I make it extremely unlikely to actually return the receiver and not a null, like this:
 
	return ThreadLocalRandom.current().nextInt(100) == 101 ? this : null;

As for the theory side of the issue, my position there is that asking what sort of data races thread t1 could observe in the presence of some garbage collection thread GC1 is fruitless, because garbage collection isn’t a synchronization action, the JMM doesn’t recognize that these threads synchronize with each other and in any case if thread T1 won’t attempt to synchronize with the possibly-non-existent GC1 then the whole of JLS 17.4 doesn’t even apply.

I think what ultimately decides the case at least for those instance methods that return their receiver is this part in 12.6.1: “Furthermore, none of the pre-finalization reads of fields of that object may see writes that occur after finalization of that object is initiated.” For our purposes reading the receiver to pass it to the caller could be construed as a read of all of the receiver’s fields .






-- 
Have a nice day, 
Timo

Sent from Mail for Windows 10



From: Vitaly Davidovich
Sent: Friday, December 11, 2015 22:51
To: Timo Kinnunen
Cc: Gil Tene;Peter;Doug Lea;concurrency-interest at cs.oswego.edu
Subject: Re: [concurrency-interest] DirectByteBuffers and reachabilityFence


So why is the DBB.get() method safe and not a potentially asynchronous destructor? Simply because as the last thing it does is a “return this;” there is no place inside it where a reachabilityFence(this) could be placed such that the receiver would not be used afterwards! This doesn’t by itself mean that every call to it is done in a safe manner but it does mean that a reachability fence can’t fix the implementation of DBB.get() because DBB.get() isn’t broken.

It's been shown a few times already in this thread why DBB.get() is actually broken, but happens to be lucky (presumably). 

I think this is still safe, though, as everything until here has been limited to a single thread, therefore correctly synchronized, therefore data-race free and therefore sequentially consistent. If a single-threaded program could observe a crash here then it would mean the GC had observed the return of a method call before the method call returned, which would mean the thread’s execution wasn’t sequentially consistent, which is a contradiction. Therefore such a reordering is not a legal reordering.

There's no contradiction, really.  The GC does not "observe return of a method", the GC observes oopmaps the compiler gives it; these oopmaps describe which oops are live at the point of GC (safepoint).  If compiler determines that `this` is not live anymore, GC will pick up on that.  The problem is simply that JIT thinks it's dealing with plain "long" values, but in reality that's a native pointer.  This is just an impedance mismatch between managed and native memory playing together.
 

On Fri, Dec 11, 2015 at 3:56 PM, Timo Kinnunen <timo.kinnunen at gmail.com> wrote:
Hi, 
 
I think I’ve figured out why the implementation of DirectByteBuffer.get(byte[], int, int) is safe. 
 
First, please convince yourself that reachabilityFence(this) is semantically equivalent or at least as-if compatible with a reachabilityPotentiallyEnds(this) call, with the method reachabilityPotentiallyEnds() being where all the finalization magic is implemented. (This is just for illustration and not the main point.)
 
Convinced already? Good �� 
 
First, consider a constructor. The execution of a constructor starts in a static context, where pretty much nothing except evaluating arguments and calling another constructor of the same class or some superclass constructor is allowed. After that call completes normally, the context becomes that of the newly constructed instance, all restrictions of the static context are lifted and the rest of the constructor proceeds to execute as usual. 
 
Now consider an instance method where the receiver is not used beyond a certain point, its bytecode with slot 0 not read after that point, and the native code version which therefore can overwrite and reuse the register housing the this-pointer as needed. Such a method could be refactored into methods A and B, where method A executes before B and where the receiver is used in A but not in B, so that method B can be static. For such a method an implicit reachabilityPotentiallyEnds(this) call could be considered to exist between the parts A and B. If the receiver does become unreachable in such a method then the result of such an execution would be a dual or the inverse to a constructor call. Let’s call such methods potentially asynchronous destructors:
 
A Potentially Asynchronous Destructor is an instance method where it’s possible to place a reachabilityFence(this) marker in such a way that the receiver is not referenced after execution reaches the marker and the rest of the execution happens in an effectively static context. 
 
Given this definition, any method where an explicit reachabilityFence(this) needs to be inserted so that the boundary between A and B moves to some later position to guarantee its safety is a potentially asynchronous destructor.
 
So why is the DBB.get() method safe and not a potentially asynchronous destructor? Simply because as the last thing it does is a “return this;” there is no place inside it where a reachabilityFence(this) could be placed such that the receiver would not be used afterwards! This doesn’t by itself mean that every call to it is done in a safe manner but it does mean that a reachability fence can’t fix the implementation of DBB.get() because DBB.get() isn’t broken. 
 
I think this is still safe, though, as everything until here has been limited to a single thread, therefore correctly synchronized, therefore data-race free and therefore sequentially consistent. If a single-threaded program could observe a crash here then it would mean the GC had observed the return of a method call before the method call returned, which would mean the thread’s execution wasn’t sequentially consistent, which is a contradiction. Therefore such a reordering is not a legal reordering.
So, what do you think? Sound reasoning or not?



-- 
Have a nice day, 
Timo

Sent from Mail for Windows 10
 
 

From: Gil Tene
Sent: Friday, December 11, 2015 06:01
To: Peter
Cc: Doug Lea;concurrency-interest at cs.oswego.edu

Subject: Re: [concurrency-interest] DirectByteBuffers and reachabilityFence
 
 
 
> On Dec 10, 2015, at 6:47 PM, Peter <jini at zeus.net.au> wrote:
> 
> Would another workaround be to use a phantom reference  for the DBB?
 
That's exactly what sun.misc.Cleaner already does, and DBB already uses a cleaner. Cleaners and phantom references (or weak, or soft, or finalizers) do not get rid of the race. A DBB 'this' can become unreachable between computing an address and accessing it, GC picks up on that and enqueues the phantom reference (the Cleaner object), and the Cleaner object explicitly frees the buffer memory that was held by the DBB instance. Even though the DBB memory isn't reclaimed until the next GC, that doesn't matter, since the buffer has been freed [and potentially recycled elsewhere] before the unsafe call to perform the last read or write operation into it is made.
 
There is currently (prior to reachabilityFence) no way I know of in Java to close this race in DBB short of following each unsafe access (e.g. in each put or get) with some carefully crafted (and also expensive and/or optimization-hurting) volatile accesses. E.g. you can add an operation that stores 'this' to an extra volatile field initialized to point refer to 'this'. E.g. I think something like: ... unsafe(…); this.dummy.dummy = this; … might be sufficient to ensure that 'this' is reachable until after the unsafe call if dummy is volatile. But it will make buffer operations dead slow...
 
> 
> The memory isn't reclaimed by gc until the phantom reference is cleared or becomes reachable.
> 
> The phantom reference queue functionality could be implemented in static methods and fields.  Classes aren't gc'd until all classes in a ClassLoader and the ClassLoader itself becomes reachable.
> 
> Just curious.
> 
> Regards,
> 
> Peter.
> 
> Sent from my Samsung device.
> ---- Original message ----
> From: Doug Lea <dl at cs.oswego.edu>
> Sent: 11/12/2015 10:14:54 am
> To: concurrency-interest at cs.oswego.edu
> Subject: Re: [concurrency-interest] DirectByteBuffers and reachabilityFence
> 
> On 12/10/2015 07:56 AM, Andrew Haley wrote:
> > On 12/10/2015 12:51 PM, Vitaly Davidovich wrote:
> >> I can see a few reasons against extending `this` lifetime at this stage of
> >> java's life, but what were the objections last time(s) this came up?
> >
> > Basically twofold.  Mostly efficiency, but also a reluctance to change
> > long-settled language semantics.  Making changes to something as
> > fundamental as this is always much tricker than people expect.  The
> > great advantage of reachabilityFence is that we can do it without a
> > lot of complex argument and politics.
> >
> 
> Like Andrew and others who have seen periodic protracted
> discussions of this issue over the past decade or so, I'm not
> too optimistic.
> 
> But this is also one reason for considering a @Finalized annotation
> for fields (not classes!), that would give essentially this
> guarantee only in those cases it was requested. Compilers would
> translate methods/blocks accessing @Finalized field r
> (as in .. use(r); ...)  to  the equivalent of:
>    try { ... use(r); ... } finally { reachabilityFence(); }
> 
> In the mean time, users can at least arrange this manually though.
> There is no reason to believe that the performance impact would
> be any different in manual vs automated forms, as long as some of
> us are willing to help hotspot minimize it.
> 
> -Doug
> 
> 
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
> 
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
 
 
 

_______________________________________________
Concurrency-interest mailing list
Concurrency-interest at cs.oswego.edu
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20151212/dab103b9/attachment-0001.html>


More information about the Concurrency-interest mailing list