zero-length segments

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Fri Jan 21 17:05:41 UTC 2022


Hi Chris,
I agree with your analysis. However, free(NULL) is guaranteed to work, 
so I don't immediately see a lot of benefit to return anything other 
than a NULL-based segment for zero size.

For the corner^4 cases in which the address of the zero-sized segment is 
actually relied upon, I guess the user has always the fallback to use 
malloc via the linker API.

Maurizio

On 21/01/2022 16:55, Chris Vest wrote:
>
>     > Furthermore, malloc and free also allow working with zero-sized
>     > segments, so it seems that even the checks in Unsafe is unnecessary.
>
>     That's true - although I guess malloc has still freedom to return
>     either
>     NULL or a constant pointer - perhaps implementors of Unsafe wanted
>     more
>     portable behavior?
>
>
> For size zero, malloc returns either NULL or a unique pointer that can 
> later be passed to free.
> It cannot be a constant value, unless the constant is NULL, AFAIK.
>
> Since foreign memory and foreign linker APIs may be used to interact 
> with platform specific APIs, it could be argued that MemorySegments 
> should do whatever the underlying malloc is doing.
> However, it would also be inconvenient for Java to specify such 
> ambiguous behaviour.
> So I think, if malloc returns NULL, it should internally be retried 
> with size 1 to get a unique pointer for the zero-sized segment.
> That should get consistent behaviour everywhere.
>
> On Fri, Jan 21, 2022 at 6:26 AM Maurizio Cimadamore 
> <maurizio.cimadamore at oracle.com 
> <mailto:maurizio.cimadamore at oracle.com>> wrote:
>
>
>     On 21/01/2022 11:33, Quân Anh Mai wrote:
>     > Hi,
>     >
>     > It seems the delegated Unsafe.allocateMemory and Unsafe.freeMemory
>     > allows working with zero-sized segments. May I ask why do we
>     have such
>     > a restriction on MemorySegment.allocateNative?
>     As I explained, there's no reason as to why zero-sized segments
>     are not
>     allowed. It's just the way the API has started out.
>     > Furthermore, malloc and free also allow working with zero-sized
>     > segments, so it seems that even the checks in Unsafe is unnecessary.
>
>     That's true - although I guess malloc has still freedom to return
>     either
>     NULL or a constant pointer - perhaps implementors of Unsafe wanted
>     more
>     portable behavior?
>
>     Maurizio
>
>     >
>     > Regards,
>     > Quan Anh
>     >
>     > malloc - cppreference.com
>     <https://urldefense.com/v3/__http://cppreference.com__;!!ACWV5N9M2RV99hQ!ZQNILH50WR0vV4ldEn1ziS0kEPK-laC8g-2JZe7WZKxJ-VFlEO7DTX9wyzRX3OewtRS1V00$>
>
>     >
>     <https://urldefense.com/v3/__https://en.cppreference.com/w/c/memory/malloc__;!!ACWV5N9M2RV99hQ!Z98zoj9sxZ1kO4YtwhCwwgsnkad_tgvFkHtLZi9tan9NQvcVcSerxkcoOBCf1CkXMNfpurc$
>     <https://urldefense.com/v3/__https://en.cppreference.com/w/c/memory/malloc__;!!ACWV5N9M2RV99hQ!Z98zoj9sxZ1kO4YtwhCwwgsnkad_tgvFkHtLZi9tan9NQvcVcSerxkcoOBCf1CkXMNfpurc$>>
>     > free - cppreference.com
>     <https://urldefense.com/v3/__http://cppreference.com__;!!ACWV5N9M2RV99hQ!ZQNILH50WR0vV4ldEn1ziS0kEPK-laC8g-2JZe7WZKxJ-VFlEO7DTX9wyzRX3OewtRS1V00$>
>
>     >
>     <https://urldefense.com/v3/__https://en.cppreference.com/w/c/memory/free__;!!ACWV5N9M2RV99hQ!Z98zoj9sxZ1kO4YtwhCwwgsnkad_tgvFkHtLZi9tan9NQvcVcSerxkcoOBCf1CkXAMZTOIg$
>     <https://urldefense.com/v3/__https://en.cppreference.com/w/c/memory/free__;!!ACWV5N9M2RV99hQ!Z98zoj9sxZ1kO4YtwhCwwgsnkad_tgvFkHtLZi9tan9NQvcVcSerxkcoOBCf1CkXAMZTOIg$>>
>     >
>     > On Fri, 21 Jan 2022 at 18:51, Maurizio Cimadamore
>     > <maurizio.cimadamore at oracle.com
>     <mailto:maurizio.cimadamore at oracle.com>
>     > <mailto:maurizio.cimadamore at oracle.com
>     <mailto:maurizio.cimadamore at oracle.com>>> wrote:
>     >
>     >     Hi Michael,
>     >     I think I'm sympathetic with your argument. Not only most of the
>     >     other
>     >     factories, as you noticed, do allow for zero-length
>     segments, but
>     >     there's also the MemorySegment::mapFile method, which
>     specifically
>     >     returns an instance of a special subclass if the mapped size
>     if zero.
>     >
>     >     I also did some more tests with the ByteBuffer API, which allows
>     >     allocation (as you mention) with size = 0, but also allows
>     slicing
>     >     with
>     >     slice size = 0 and limit of 0.
>     >
>     >     (Java arrays are another case where creating a zero-element
>     array is
>     >     indeed possible).
>     >
>     >     All this evidence point to the fact that, yes, saying no to
>     zero-byte
>     >     allocation on memory segment (of any kind) is at the very least
>     >     problematic in terms of interop with existing APIs, as it
>     will cause
>     >     surprising behavior.
>     >
>     >     For this reason, I believe the best course of action is to
>     enhance
>     >     the
>     >     API in the way you suggest, and accept zero-sized segments.
>     >
>     >     Thanks for the feedback!
>     >
>     >     Thinking of possible workarounds in the short term - but
>     maybe you
>     >     are
>     >     already doing it - you could have something like this:
>     >
>     >     ```
>     >     private static final MemorySegment EMPTY =
>     >  MemorySegment.ofByteBuffer(ByteBuffer.allocateDirect(0));
>     >
>     >     MemorySegment wrapAddress(MemoryAddress address, long size,
>     >     ResourceScope scope) {
>     >          return size == 0 ? EMPTY : MemorySegment.ofAddress(address,
>     >     size,
>     >     scope);
>     >     }
>     >     ```
>     >
>     >     In terms of performance, there are a couple of points to note:
>     >
>     >     * adding a singleton anonymous class for zero-sized segment
>     might
>     >     cause
>     >     profile pollution when using the same callsite with empty and
>     >     non-empty
>     >     segments
>     >     * having a branch (like in the above code) so that a
>     singleton is
>     >     returned if size == 0 effectively disables escape analysis
>     most of
>     >     the
>     >     times (when size is not known by C2)
>     >
>     >     And, other pseudo-random considerations:
>     >
>     >     * Looking at the impl of ByteBuffer.allocateDirect, it seems
>     like it
>     >     always allocates at least one byte:
>     >
>     >     ```
>     >     long size = Math.max(1L, (long)cap + (pa ? ps : 0));
>     >     Bits.reserveMemory(size, cap);
>     >     ```
>     >
>     >     You can see how this is suboptimal (and probably not what a
>     >     programmer
>     >     would expect).
>     >
>     >     * In the case of MemorySegment, returning a singleton is not
>     really
>     >     possible, because (as for mapped segments) the user is also
>     >     providing a
>     >     scope parameter, and it expects that the returned segment will
>     >     have same
>     >     scope as the provided parameter.
>     >
>     >     * All things considered, given it's late for 18, I'd prefer to
>     >     address
>     >     this in 19 - but I do want to address it.
>     >
>     >     Thanks
>     >     Maurizio
>     >
>     >     On 20/01/2022 22:50, Michael Zucchi wrote:
>     >     >
>     >     > Morning all,
>     >     >
>     >     > After a long break i've started experimenting with the foreign
>     >     abi to
>     >     > replace jni.  One tool i'm working on is a vulkan binding
>     generator
>     >     > that works directly from the xml specification and generates a
>     >     'nice'
>     >     > api (particularly focusing on constructors for all the
>     hundreds of
>     >     > configuration structures needed for vulkan, plus the dynamic
>     >     function
>     >     > tables), and another tool generates high-level and potentially
>     >     object
>     >     > oriented api's from c header files, for this one I use a gcc
>     >     plugin to
>     >     > extract the structures and functions and i'm trying opencl and
>     >     ffmpeg
>     >     > as test cases (and to update some projects i maintain, zcl and
>     >     > jjmpeg).  I'm not using jextract because I want to create
>     the high
>     >     > level api directly and provide much more control on the
>     created
>     >     > classes, and also because i've got the time and nothing better
>     >     to do
>     >     > with it.
>     >     >
>     >     > In general it looks pretty good after so much work, but
>     i've come
>     >     > across one oddity which adds complexity to the java code
>     for no
>     >     > obvious reason.  Is there any specific reason you cannot
>     create
>     >     zero
>     >     > length memory segments *in some cases*?
>     >     >
>     >     > e.g. something like this comes up often in C:
>     >     >
>     >     > struct blob {
>     >     >    size_t data_size;
>     >     >    uint8_t *data;
>     >     > }
>     >     >
>     >     > From java at some point you want to get a MemorySegment to
>     access
>     >     > blob.data.  It might be through a high level api such as:
>     >     >
>     >     >   class blob {
>     >     >     MemorySegment getData() {
>     >     >         return MemorySegment.ofAddress(...);
>     >     >     }
>     >     >  }
>     >     >
>     >     > If the memory was allocated in the native code (quite
>     common) this
>     >     > obvious java-side implementation just isn't possible with the
>     >     current
>     >     > MemorySegment implementation as it will fail in an
>     unexpected (and
>     >     > imho unreasonable) way if size is 0.  You'd either need to
>     wrap
>     >     > MemorySegment in some other structure which hides this
>     detail with
>     >     > it's own special case code (seems redundant), return a null
>     >     > (apparently evil these days, and kinda messy anyway), or
>     expose the
>     >     > detail by ensuring the callee checks size>0 before calling
>     >     getData()
>     >     > (yikes).
>     >     >
>     >     > One notes that it is inconsistent with the rest of the api:
>     >     >
>     >     > works:
>     >     >
>     >
>      segmentAllocator.allocate(MemoryLayout.structLayout().withBitAlignment(8))
>     >     > works: segmentAllocator.allocateArray(JAVA_BYTE, 0);
>     >     > works: segmentAllocator.allocate(0);
>     >     > works:  segment.asSlice(offset, 0);
>     >     > works: MemorySegment.ofArray(new byte[0]);
>     >     > works:
>     MemorySegment.ofByteBuffer(ByteBuffer.allocateDirect(0));
>     >     > (you get the idea ...)
>     >     >
>     >     > doesn't:
>     >     >
>     >
>      MemorySegment.allocateNative(MemoryLayout.structLayout().withBitAlignment(8),
>     >
>     >     > scope);
>     >     > doesn't: MemorySegment.allocateNative(0, 1, scope);
>     >     > doesn't: MemorySegment.ofAddress(addr, 0, scope);
>     >     >
>     >     > With the last one being the only way to wrap
>     sized-allocations from
>     >     > native memory(?)  there seems to be no workaround possible.
>     >     >
>     >     > Both java and c specifically define zero-length allocations as
>     >     valid
>     >     > everywhere else because it simplifies a lot of code, and
>     even the
>     >     > foreign-abi does for every other case, so why not here too?
>     >     >
>     >     > Cheers,
>     >     >  Z
>     >
>


More information about the panama-dev mailing list