Select in Fortress

Java’s ExecutorService and Future can be used to simulate the behavior of select and channels. Here’s how we can implement a similar functionality:

import java.util.concurrent.*;
import java.util.Arrays;

public class Select {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // We'll use ExecutorService to manage our tasks
        ExecutorService executor = Executors.newFixedThreadPool(2);

        // For our example we'll create two Callable tasks
        Callable<String> task1 = () -> {
            TimeUnit.SECONDS.sleep(1);
            return "one";
        };

        Callable<String> task2 = () -> {
            TimeUnit.SECONDS.sleep(2);
            return "two";
        };

        // Submit the tasks and get Future objects
        Future<String> future1 = executor.submit(task1);
        Future<String> future2 = executor.submit(task2);

        // We'll use CompletionService to await both of these values
        // simultaneously, printing each one as it arrives.
        CompletionService<String> completionService = new ExecutorCompletionService<>(executor);
        completionService.submit(task1);
        completionService.submit(task2);

        for (int i = 0; i < 2; i++) {
            Future<String> completedFuture = completionService.take();
            System.out.println("received " + completedFuture.get());
        }

        executor.shutdown();
    }
}

In this example, we use Java’s concurrency utilities to simulate the behavior of Go’s select statement:

  1. We create an ExecutorService to manage our concurrent tasks.

  2. We define two Callable tasks, each of which sleeps for a certain amount of time before returning a value. This simulates the blocking operations in the original Go example.

  3. We submit these tasks to the ExecutorService and get Future objects in return.

  4. We use a CompletionService to await the completion of these tasks. The CompletionService allows us to retrieve completed tasks in the order they finish, similar to how select works in Go.

  5. We iterate twice (since we have two tasks) and use completionService.take() to get the next completed task. This is similar to the select statement in the Go example, which waits for either channel to have a value ready.

  6. We print the result of each completed task as it arrives.

  7. Finally, we shut down the ExecutorService.

To run the program, save it as Select.java and use javac to compile it:

$ javac Select.java
$ java Select
received one
received two

Note that the total execution time is only ~2 seconds since both the 1 and 2 second sleeps execute concurrently.

This Java implementation provides similar functionality to the Go example, demonstrating how to wait on multiple concurrent operations and process their results as they become available.