[foreign] RFR 8212987 : Binder should allows negative values for native pointers
Jorn Vernee
jbvernee at xs4all.nl
Fri Nov 2 21:09:48 UTC 2018
Comments inline...
Henry Jen schreef op 2018-11-02 19:19:
> On Nov 2, 2018, at 4:46 AM, Jorn Vernee <jbvernee at xs4all.nl> wrote:
>>
>> Henry Jen schreef op 2018-11-02 05:02:
>>> This pretty much passed all tests, except that we are expecting wrong
>>> exception, depends on SPEC, we either need to fix test or fix
>>> implementation. I’ll let Maurizio comments on what exception should
>>> we
>>> expect.
>>
>> This depends on the used invoker again. Since the universal native
>> invoker wraps the IOOBE inside an IllegalStateException that's what
>> I'm checking for. TestNG doesn't seem to have an easy way to check the
>> cause of an exception. I could do that manually, but I agree that it
>> depends on the spec, so it needs to be decided what kind of exception
>> we want to see there.
>>
>>> However, change the BoundedPointer to create new MemoryRegion has a
>>> subtle difference, which I think worth attention. EVERYTHING has a
>>> special meaning that it’s the whole available memory. With the new
>>> implementation, the region won’t allow access before the address,
>>> this
>>> is not correct in turns of Pointer as it can be moved freely in the
>>> bounded memory region, even backwards.
>>
>> I hadn't thought about that yet, that's a good point.
>>
>> Though I have to point out that the current implementation of some of
>> the BoundedPointer factories were already disallowing this by creating
>> a memory region at the requested offset [1].
>>
>
> Thanks for all the work, you can tell this is prototype and not
> production quality. :P
>
> To me, Pointer to throw IndexOutOfBoundException seems reasonable.
> Like you said, the UniversalNativeInvoker actually rely on
> Pointer.get() to get the return Pointer, and that wrap up the IOOBE
> into ISE.
>
> For now, I think we can probably just relax the expectation to be
> RuntimeException?
Yeah, that seems fine.
>>> Although we can argue that it’s better to have Pointer construct out
>>> of a strictly bounded memory region, it’s a change of current
>>> behavior
>>> and that probably need some discussion regarding to use cases.
>>> Anyway, like I said, I think enable full 64bit address space is
>>> probably not a bad thing. I suspect once we change the length instead
>>> of using min and max address value, the bound check won’t be that
>>> confusing, but you may feel differently.
>>
>> I had a version which used min and max offset, but to make that safe I
>> had to implement a version of Math.addExact for unsigned numbers,
>> which was being called in checkBounds and checkRange to make sure that
>> the offset being passed in didn't overflow when adding to the region's
>> `min` address and length. The downside of this was that it made the
>> implementation noticeably slower, I guess since my version of addExact
>> isn't intrinsified. I got rid of one of the overflow checks by using
>> lengthMinusOne (i.e. length - 1). I could probably look into that some
>> more though.
>>
>> Really, since BoundedMemoryRegion only does a bounds check and then an
>> access, maybe we should choose a design where we create an unbounded
>> version of `Pointer` and use that instead of using EVERYTHING. If the
>> intent is really to make every 64bit address accessible than that
>> should behave the same I think, but it seems much easier to make. It
>> just leaves the problem of not being able to create a memory region of
>> size >= Long.MAX_VALUE for another time.
>>
>
> I like your idea. For special cases, that is with address to be
> negative, we can have a special unbounded pointer that cannot be
> dereferenced. After all, this is the use case here, a special error
> code. I doubt there would be a valid address with negative value, but
> I don’t have extensive knowledge about CPU architecture.
It seems this doesn't hold for e.g. Solaris [1], but it depends on the
OS. So it seems that dereferencing negative pointers will have to be
supported at some point.
In the meantime I've worked on things a little more and revisited the
idea of a subclass of BoundedMemoryRegion that doesn't do the bounds
check. After thinking about it, I don't think this is as hacky as it
seemed. The idea is that you can either have a memory region with an
explicit length between 0 and Long.MAX_VALUE, and any offset, OR have a
memory region starting at 0 that covers everything, without an explicit
length. Since the only restriction is that the explicit length can not
be larger than Long.MAX_VALUE, but still any offset is accepted I think
this might get us a little more mileage.
Here's the webrev:
http://cr.openjdk.java.net/~jvernee/panama/webrevs/8212987/webrev.09/
Some implementation notes:
- Right now the memory region without an explicit offset or length
(called Everything) is a nested subclass of BoundedMemoryRegion, but
maybe these should be refactored into an abstract class and
implementation pair.
- I noticed some missing access checks in put-/get-Address, and putBits
was checking for read access instead of write access. So I've refactored
the checks into checkRead and checkWrite methods to simplify things. The
put-/get-Bits methods were also checking the size twice, both explicitly
and as part of the switch, so I removed the explicit check.
- I've changed the BoundedPointer factories to use the new Everything
class, instead of creating a memory region at the argument offset. That
should allow 'looking back' with the returned pointer as well.
The other thing is that since the EVERYTHING region doesn't have a
length, you can't request a pointer's `byteSize()` [2], or iterate over
it's `elements()` [3]. I'm not sure I find those public API points all
that sensible to be honest; with `byteSize()` I'd expect to get the size
of the Pointer's layout, but instead it returns the size of the memory
region - offset. You get the size of the layout by using
`(type.layout().bitsSize() / 8)`. And something like `elements()` seems
more at home in `Array`, since that has an explicit element count
associated with it, while a pointer does not. Imho these API points
should be moved to Array. The doc for `elements()` already talks about
the pointer as if it's an array, so it seems like these API points were
from before you had the Array interface?
Cheers,
Jorn
[1] :
http://mail.openjdk.java.net/pipermail/panama-dev/2018-October/002995.html
[2] :
http://hg.openjdk.java.net/panama/dev/file/cfee9bc4ebe4/src/java.base/share/classes/java/foreign/memory/Pointer.java#l94
[3] :
http://hg.openjdk.java.net/panama/dev/file/cfee9bc4ebe4/src/java.base/share/classes/java/foreign/memory/Pointer.java#l58
> IMHO, when we want to enable full 64-bit address space, EVERYTHING
> should still mean the whole available address space, and NOTHING would
> be a special implementation that won’t be able to do much. By that, I
> mean BoundedMemoryRegion will always have size of at least one byte.
>
> Cheers,
> Henry
>
>> What do you think?
>>
>> Jorn
>>
>> [1] :
>> http://hg.openjdk.java.net/panama/dev/file/cfee9bc4ebe4/src/java.base/share/classes/jdk/internal/foreign/memory/BoundedPointer.java#l178
>>
>>> Cheers,
>>> Henry
>>> test PointerTest.test1ByteRegionAtMaxHeapOffset(): success
>>> test PointerTest.testAutomaticLengthRegionNotBigEnough(): failure
>>> java.lang.IndexOutOfBoundsException: offset=0x3 length=0x1
>>> at
>>> java.base/jdk.internal.foreign.memory.BoundedMemoryRegion.checkBounds(BoundedMemoryRegion.java:159)
>>> at
>>> java.base/jdk.internal.foreign.memory.BoundedMemoryRegion.checkRange(BoundedMemoryRegion.java:166)
>>> at
>>> java.base/jdk.internal.foreign.memory.BoundedPointer.<init>(BoundedPointer.java:77)
>>> at
>>> java.base/jdk.internal.foreign.memory.BoundedPointer.cast(BoundedPointer.java:149)
>>> at
>>> java.base/jdk.internal.foreign.invokers.DirectSignatureShuffler.longToPointer(DirectSignatureShuffler.java:335)
>>> at
>>> PointerTest$pointers$Impl/0x00000007c032e040.get_overflow_pointer(Unknown
>>> Source)
>>> at
>>> PointerTest.testAutomaticLengthRegionNotBigEnough(PointerTest.java:292)
>>> at
>>> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native
>>> Method)
>>> at
>>> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
>>> at
>>> java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
>>> at java.base/java.lang.reflect.Method.invoke(Method.java:566)
>>> at
>>> org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:85)
>>> at org.testng.internal.Invoker.invokeMethod(Invoker.java:639)
>>> at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:821)
>>> at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1131)
>>> at
>>> org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:124)
>>> at
>>> org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:108)
>>> at org.testng.TestRunner.privateRun(TestRunner.java:773)
>>> at org.testng.TestRunner.run(TestRunner.java:623)
>>> at org.testng.SuiteRunner.runTest(SuiteRunner.java:357)
>>> at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:352)
>>> at org.testng.SuiteRunner.privateRun(SuiteRunner.java:310)
>>> at org.testng.SuiteRunner.run(SuiteRunner.java:259)
>>> at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
>>> at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
>>> at org.testng.TestNG.runSuitesSequentially(TestNG.java:1185)
>>> at org.testng.TestNG.runSuitesLocally(TestNG.java:1110)
>>> at org.testng.TestNG.run(TestNG.java:1018)
>>> at
>>> com.sun.javatest.regtest.agent.TestNGRunner.main(TestNGRunner.java:94)
>>> at
>>> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native
>>> Method)
>>> at
>>> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
>>> at
>>> java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
>>> at java.base/java.lang.reflect.Method.invoke(Method.java:566)
>>> at
>>> com.sun.javatest.regtest.agent.MainActionHelper$SameVMRunnable.run(MainActionHelper.java:229)
>>> at java.base/java.lang.Thread.run(Thread.java:835)
>>>> On Nov 1, 2018, at 6:27 PM, Jorn Vernee <jbvernee at xs4all.nl> wrote:
>>>> Ok, I have one more addition to this. I figured out the bug I was
>>>> seeing before; it turns out I was passing a double offset.
>>>> I've removed the hackish anonymous class, and removed
>>>> BoundedMemoryRegion.EVERYTHING for now since the length is just not
>>>> big enough to cover all cases. Instead I'm now creating a new memory
>>>> region at the requested offset for BoundedPointer. I looked into
>>>> treating `length` as unsigned as well but this was pretty tricky,
>>>> since to represent the length of EVERYTHING you need to have a
>>>> 65-bit number. So I was working with lengthMinusOne everywhere but
>>>> that was a lot more confusing. I think what I have right now is
>>>> better than that.
>>>> Updated webrev:
>>>> http://cr.openjdk.java.net/~jvernee/panama/webrevs/8212987/webrev.07/
>>>> Thanks for all the reviews and discussion so far.
>>>> Cheers,
>>>> Jorn
>>>> Jorn Vernee schreef op 2018-11-01 17:42:
>>>>> Henry Jen schreef op 2018-11-01 16:21:
>>>>>> I was hesitate to reply as the fix is kind of hackish, but not
>>>>>> ready
>>>>>> to suggest we go all the way to enable full range of 64-bit, after
>>>>>> all, Long.MAX_VALUE is giving us 8EB and that’s seems way more
>>>>>> than
>>>>>> enough. But to completely address rather than simply make it work
>>>>>> for
>>>>>> special cases, we probably should just treat address as unsigned
>>>>>> and
>>>>>> use Long.compareUnsigned to do comparison.
>>>>> It feels kind of hackish to me too tbh, especially since I can only
>>>>> do
>>>>> partial testing. But if all the tests pass on your side as well
>>>>> then I
>>>>> see no problem for now. If this is not the case then I will leave
>>>>> it
>>>>> for now and focus on something else until I also have the full test
>>>>> suite running as well.
>>>>> As an interim alternative for dealing with special values maybe we
>>>>> could add a 'dummy' pointer type that is used for negative values,
>>>>> but
>>>>> does not support getting/setting and is really only a wrapper
>>>>> around a
>>>>> `long`. The way to use it would be to pass it back to native which
>>>>> _can_ handle the negative value. But that is not a long term
>>>>> solution,
>>>>> since some systems use negative pointers as normal, as pointed out
>>>>> by
>>>>> Florian [1].
>>>>>> Therefore, max length for a memory always 0 - min. The length
>>>>>> should
>>>>>> be Long.compareUnsigned(length, maxLength) <= 0, with all other
>>>>>> offset
>>>>>> operation compare against length established at construction time
>>>>>> with
>>>>>> Long.compareUnsigned, that should be enough to deal with this.
>>>>> OK, I will use Long.compareUnsigned to do the check.
>>>>>> However, taking offset with large(negative) number seems really
>>>>>> confusing.
>>>>> Yes, so hopefully in almost all cases this will be abstracted away,
>>>>> and not really visible. `long` is kind of a sub-optimal carrier
>>>>> type
>>>>> for a pointer value for this reason I think. It's OK to store the
>>>>> pointer, but things get confusing when you try to look at it, since
>>>>> it
>>>>> will show up as a signed integer. But maybe once we have value
>>>>> types
>>>>> we could consider having an UnsignedLong type which has the right
>>>>> semantics and representation to it?
>>>>> Here is a version that uses Long.compareUnsigned to do the overflow
>>>>> check:
>>>>> http://cr.openjdk.java.net/~jvernee/panama/webrevs/8212987/webrev.06/
>>>>> There are 2 lines of factory methods; the first which calculate the
>>>>> length and then pass args to a private internal factory method, the
>>>>> others that check the input length for overflow and then pass args
>>>>> to
>>>>> the internal factory. The internal factory does not do the overflow
>>>>> check.
>>>>> Jorn
>>>>>> What do you think?
>>>>>> Cheers,
>>>>>> Henry
>>>>> [1] :
>>>>> http://mail.openjdk.java.net/pipermail/panama-dev/2018-October/002995.html
More information about the panama-dev
mailing list