/*
 * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import jdk.jshell.tool.JavaShellToolBuilder;

/*
 * @test
 * @bug 8347418
 * @summary Verify that launching jshell concurrently doesn't lead to a
 *          NullPointerException when jshell tries to load the history
 * @run main ${test.main.class}
 */
public class JshellConcurrentLoadHistory {

    public static void main(final String[] args) throws Throwable {
        final int numTasks = 10;
        final CountDownLatch latch = new CountDownLatch(numTasks);
        final List<Future<Void>> results = new ArrayList<>();
        // launch jshell execution concurrently
        try (final ExecutorService executor = Executors.newCachedThreadPool()) {
            for (int i = 0; i < numTasks; i++) {
                final Future<Void> f = executor.submit(new JshellExecution(latch));
                results.add(f);
            }
            // wait for the tasks to complete
            for (final Future<Void> f : results) {
                f.get();
            }
        }
        System.out.println("successfully completed");
    }

    private static final class JshellExecution implements Callable<Void> {
        private final CountDownLatch latch;

        private JshellExecution(final CountDownLatch latch) {
            this.latch = latch;
        }

        @Override
        public Void call() throws Exception {
            latch.countDown(); // announce our arrival
            latch.await(); // wait for others

            // launch jshell
            JavaShellToolBuilder
                    .builder()
                    // some arbitrary input code
                    .in(new ByteArrayInputStream("int x = 42".getBytes()), null)
                    .start();

            return null;
        }
    }
}