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

Malloc/free fails on Mac

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Duplicate
    • Icon: P2 P2
    • None
    • 25
    • core-libs
    • None

      Using `UNSAFE.allocateMemory()` and ` UNSAFE.freeMemory()` in certain ways causes the VM to crash.

      This issue was only observed on macosx-aarch64 and macosx-x64 and not on Linux and Windows.

      Reproduce (see code further below):

       make test TEST=open/test/jdk/java/foreign/TestCarrierLocalArenaPoolsStress.java JTREG_REPEAT_COUNT=1000

      ...
      Repeating Jtreg run: 64 out of 1000
      ...

      STARTED TestCarrierLocalArenaPoolsStress::stress 'stress()'
      STDOUT:
      PT: 0:000007208 EXPANDING VT FJP
      PT: 0:005320166 DONE EXPANDING
      PT: 0:005854708 CREATING 1024 THREADS USING java.lang.ThreadBuilders$VirtualThreadBuilder@1581572f
      PT: 0:011715500 SLEEPING
      PT: 10:019071666 INTERRUPTING
      PT: 10:020941708 DONE INTERRUPTING
      PT: 10:036864250 ALL THREADS COMPLETED
      PT: 10:037108583 CREATING 32 THREADS USING java.lang.ThreadBuilders$PlatformThreadBuilder@465bb39d
      PT: 10:161722958 SLEEPING

      TEST RESULT: Error. Agent communication error: java.io.EOFException; check console log for any additional details

      Code:

      ```
      final class TestCarrierLocalArenaPoolsStress {

          private static final Unsafe UNSAFE = Unsafe.getUnsafe();
          private static final long POOL_SIZE = 64;
          private static final VarHandle LONG_HANDLE = JAVA_LONG.varHandle();

          /**
           * The objective of this test is to try to provoke a situation where threads are
           * competing to use allocated pooled memory and then trying to make sure no thread
           * can see the same shared memory another thread is using.
           */
          @Test
          void stress() throws InterruptedException {
              final long begin = System.nanoTime();

              System.out.println(duration(begin) + "EXPANDING VT FJP");

              // Encourage the VT ForkJoin pool to expand/contract so that VT:s will be allocated
              // on FJP threads that are later terminated.
              LongStream.range(0, Runtime.getRuntime().availableProcessors() * 2L)
                      .parallel()
                      // Using a CompletableFuture expands the FJP
                      .forEach(_ -> Thread.ofVirtual().start(() -> {
                          try {
                              TimeUnit.SECONDS.sleep(1);
                          } catch (InterruptedException ie) {
                              throw new RuntimeException(ie);
                          }
                      }));

              System.out.println(duration(begin) + "DONE EXPANDING");

              // Make sure it works for both virtual and platform threads
              for (var threadBuilder : List.of(Thread.ofVirtual(), Thread.ofPlatform())) {
                  final int noThreads = threadBuilder instanceof Thread.Builder.OfVirtual ? 1024 : 32;
                  System.out.println(duration(begin) + "CREATING " + noThreads + " THREADS USING " + threadBuilder);
                  final Thread[] threads = IntStream.range(0, noThreads).mapToObj(_ ->
                          threadBuilder.start(() -> {
                              final long threadId = Thread.currentThread().threadId();
                              while (!Thread.interrupted()) {
                                  for (int i = 0; i < 1_000_000; i++) {
                                          // Try to assert no two threads get allocated the same memory region.
                                      final long adr = UNSAFE.allocateMemory(POOL_SIZE);
                                      UNSAFE.putLongVolatile(null, adr, threadId);
                                      long v = UNSAFE.getLongVolatile(null, adr);
                                      assertEquals(threadId, v);
                                      UNSAFE.freeMemory(adr);
                                      //}
                                  }
                                  Thread.yield(); // make sure the driver thread gets a chance.
                              }
                          })).toArray(Thread[]::new);
                  System.out.println(duration(begin) + "SLEEPING");
                  Thread.sleep(Duration.of(10, SECONDS));
                  System.out.println(duration(begin) + "INTERRUPTING");
                  Arrays.stream(threads).forEach(
                          thread -> {
                              assertTrue(thread.isAlive());
                              thread.interrupt();
                          });
                  System.out.println(duration(begin) + "DONE INTERRUPTING");

                  // VTs are daemon threads ...
                  Arrays.stream(threads).forEach(t -> {
                      try {
                          t.join();
                      } catch (InterruptedException e) {
                          throw new RuntimeException(e);
                      }
                  });
                  System.out.println(duration(begin) + "ALL THREADS COMPLETED");
              }
              System.out.println(duration(begin) + "DONE");
          }
      }

          private static String duration(Long begin) {
              var duration = Duration.of(System.nanoTime() - begin, ChronoUnit.NANOS);
              long seconds = duration.toSeconds();
              int nanos = duration.toNanosPart();
              return (Thread.currentThread().isVirtual() ? "VT: " : "PT: ") +
                      String.format("%3d:%09d ", seconds, nanos);
          }

      ```


            Unassigned Unassigned
            pminborg Per-Ake Minborg
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: