Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8331734

Atomic MemorySegment VarHandle operations fails for element layouts

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P2 P2
    • 23
    • 22, 23
    • core-libs

      Atomic operations (such as `VarHandle::getVolatile` and `VarHandle::setVolatile`) fail if derived from a memory layout enclosed in another composite layout (such as a StructLayout).

      The reason for this is in `LayoutPath::dereferenceHandle`. Instead of just providing a layout with an alignment of 1, we need to consider both the enclosing layout(s) (transitively) and the current element.

      Reproducer:

      ```
      /*
       * @test
       * @run junit TestAtomicVarHandle
       */

      import org.junit.jupiter.params.ParameterizedTest;
      import org.junit.jupiter.params.provider.Arguments;
      import org.junit.jupiter.params.provider.MethodSource;

      import java.lang.foreign.Arena;
      import java.lang.foreign.MemoryLayout;
      import java.lang.invoke.VarHandle;
      import java.util.stream.Stream;

      import static java.lang.foreign.ValueLayout.JAVA_INT;
      import static org.junit.jupiter.api.Assertions.*;

      final class TestAtomicVarHandle {

          static final VarHandle INT_VH = JAVA_INT.varHandle();
          static final VarHandle INT_IN_SUPER_ALIGNED_VH = MemoryLayout.structLayout(JAVA_INT.withName("i"))
                  .withByteAlignment(32)
                  .varHandle(MemoryLayout.PathElement.groupElement("i"));

          @ParameterizedTest
          @MethodSource("methodHandles")
          void test(VarHandle vh, String name) {
              try (Arena arena = Arena.ofConfined()) {
                  var segment = arena.allocate(1000, 64);
                  int value = (int)vh.getVolatile(segment, 0L);
                  assertEquals(0, value);
                  vh.set(segment, 0L, 42);
                  value = (int)vh.getVolatile(segment, 0L);
                  assertEquals(42, value);
              }
          }

          private static Stream<Arguments> methodHandles() {
              return Stream.of(
                      Arguments.of(INT_VH, "INT_VH"),
                      Arguments.of(INT_IN_SUPER_ALIGNED_VH, "INT_IN_SUPER_ALIGNED_VH")
              );
          }

      }


      STARTED TestAtomicVarHandle::test '[1] VarHandle[varType=int, coord=[interface java.lang.foreign.MemorySegment, long]], INT_VH'
      SUCCESSFUL TestAtomicVarHandle::test '[1] VarHandle[varType=int, coord=[interface java.lang.foreign.MemorySegment, long]], INT_VH'
      STARTED TestAtomicVarHandle::test '[2] VarHandle[varType=int, coord=[interface java.lang.foreign.MemorySegment, long]], INT_IN_SUPER_ALIGNED_VH'
      java.lang.UnsupportedOperationException: Unsupported access mode for alignment: 1
      at java.base/java.lang.invoke.VarHandleSegmentViewBase.newUnsupportedAccessModeForAlignment(VarHandleSegmentViewBase.java:63)
      at java.base/java.lang.invoke.VarHandleSegmentAsInts.offsetNonPlain(VarHandleSegmentAsInts.java:88)
      at java.base/java.lang.invoke.VarHandleSegmentAsInts.getVolatile(VarHandleSegmentAsInts.java:132)
      at TestAtomicVarHandle.test(TestAtomicVarHandle.java:54)

      ```

      Workaround:


      ```

              static final VarHandle INDEX =
                      //HEADER.varHandle(PathElement.groupElement("index")); // Original
                      offset(JAVA_INT.varHandle(), HEADER.byteOffset(PathElement.groupElement("index"))); // <-- FIX


          private static VarHandle offset(VarHandle target, long offset) {
              MethodHandle mh = MethodHandles.insertArguments(MH_ADD, 0, offset);
              return MethodHandles.collectCoordinates(target, 1, mh);
          }

          private static final MethodHandle MH_ADD;

          static {
              MethodHandles.Lookup lookup = MethodHandles.lookup();
              try {
                  MH_ADD = lookup.findStatic(Long.class, "sum",
                          MethodType.methodType(long.class, long.class, long.class));
              } catch (ReflectiveOperationException e) {
                  throw new ExceptionInInitializerError(e);
              }

          }
      ```

            mcimadamore Maurizio Cimadamore
            pminborg Per-Ake Minborg
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: