1 /*
  2  * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 package org.openjdk.bench.java.util.concurrent.forkjoin;
 24 
 25 import java.util.*;
 26 import java.util.concurrent.*;
 27 import org.openjdk.jmh.annotations.*;
 28 
 29 /**
 30  * CPU saturated with threads scheduling + cancelling delayed tasks.
 31  */
 32 
 33 @BenchmarkMode(Mode.AverageTime)
 34 @OutputTimeUnit(TimeUnit.MICROSECONDS)
 35 @State(Scope.Benchmark)
 36 @Warmup(iterations = 5, time = 1)
 37 @Measurement(iterations = 5, time = 1)
 38 @Fork(3)
 39 public class ScheduleAndCancelDelayedTasks2 {
 40     private ForkJoinPool fjpPool;
 41     private ExecutorService threadPool;
 42     private volatile boolean done;
 43 
 44     private void scheduleAndCancelDelayedTask(Deque<Future<?>> queue, int maxPending) {
 45         long delay = 60 + ThreadLocalRandom.current().nextInt(60);
 46         Future<?> future;
 47         try {
 48             future = fjpPool.schedule(() -> { }, delay, TimeUnit.SECONDS);
 49         } catch (RejectedExecutionException _) {
 50             return;
 51         }
 52         if (maxPending > 1) {
 53             queue.offer(future);
 54             if (queue.size() >= maxPending) {
 55                 future = queue.poll();
 56             }
 57         }
 58         future.cancel(false);
 59     }
 60 
 61     private Deque<Future<?>> pendingDelayedTasks;
 62 
 63     @Param({"1", "100"})
 64     int maxPending;
 65 
 66     @Param({"true", "false"})
 67     boolean internal;
 68 
 69     @Setup
 70     public void setup() {
 71         int ncores = Runtime.getRuntime().availableProcessors();
 72         fjpPool = new ForkJoinPool(ncores);
 73         fjpPool.cancelDelayedTasksOnShutdown();
 74 
 75         // saturate CPU with tasks that schedule and cancel delayed tasks
 76         Executor executor;
 77         if (internal) {
 78             executor = fjpPool;
 79         } else {
 80             executor = threadPool = Executors.newCachedThreadPool();
 81         }
 82         for (int i = 0; i <  (ncores - 1); i++) {
 83             executor.execute(() -> {
 84                 Deque<Future<?>> queue = new ArrayDeque<>();
 85                 while (!done) {
 86                     scheduleAndCancelDelayedTask(queue, maxPending);
 87                 }
 88             });
 89         }
 90         pendingDelayedTasks = new ArrayDeque<>();
 91     }
 92 
 93     @TearDown
 94     public void teardown() {
 95         done = true;
 96         if (threadPool != null) threadPool.close();
 97         fjpPool.close();
 98     }
 99 
100     @Benchmark
101     public void test() throws Exception {
102         scheduleAndCancelDelayedTask(pendingDelayedTasks, maxPending);
103     }
104 }