1

I plan to write an R script using FastR implementation which looks like

    java.addToClasspath("build/libs/polyglot-example-f.jar")
    clientClass <- java.type('org.algo.polyglot.JavaClient')
    javaClient <- new(clientClass, threadPoolSize=10)
    javaClient$startScheduleWithFixedDelay(5)

The org.algo.polyglot.JavaClient class looks like (packaged into a jar):

    package org.algo.polyglot;

    import org.apache.commons.lang3.RandomStringUtils;
    import org.apache.commons.lang3.RandomUtils;
    import org.graalvm.polyglot.HostAccess;

    import java.text.MessageFormat;
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;

    import org.graalvm.polyglot.HostAccess.Export;

    public class JavaClient {

        private final ScheduledExecutorService service;

        @Export
        public JavaClient(int threadPoolSize) {
            this.service = Executors.newScheduledThreadPool(threadPoolSize);
        }

        @Export
        public void startScheduleWithFixedDelay(int delayInSeconds) {
            service.scheduleWithFixedDelay(
                    () -> {
                        postMessage(fetchMessage());
                    },
                    0,
                    delayInSeconds,
                    TimeUnit.SECONDS
            );
        }

        @Export
        public void startScheduleWithFixedRate(int period) {
            service.scheduleAtFixedRate(
                    () -> {},
                    0,
                    period,
                    TimeUnit.SECONDS
            );
        }

        @Export
        public PolyglotMessage fetchMessage() {
            return new PolyglotMessage(
                    RandomStringUtils.randomAlphabetic(10, 15),
                    RandomStringUtils.randomAlphabetic(10, 15),
                    System.currentTimeMillis()
            );
        }

        @Export
        public void postMessage(PolyglotMessage message) {
            System.out.println("[Printed from JavaClient] Posting message: " + message.toString());
        }
    }

When running the script with Rscript sample.R --polyglot the script finished execution with output:

    $ Rscript sample.R --polyglot
    NULL

I have these motives:

  • R script should be the entry point, because my use case requires me to leverage java functionality in R
  • Run the R script with the provided jar
  • Execute the script with Rscript
  • Get a ScheduledExecutorService running with either fixed delay or fixed rate from the R script itself without the R script finishing execution (Stay alive till the service is running) This can be fixed by returning the scheduled future and awaiting on it using ScheduledFuture$get() The changes made to the script and code are:
    future <- javaClient$startScheduleWithFixedDelay(5)
    future$get()
    @Export
    public ScheduledFuture<?> startScheduleWithFixedDelay(int delayInSeconds) {
        return service.scheduleWithFixedDelay(
                () -> {
                    postMessage(fetchMessage());
                },
                0,
                delayInSeconds,
                TimeUnit.SECONDS
        );
    }
  • Pass a function to the executor from the R script so that the function is called accordingly by the ScheduledExecutorService (an R function can be passed to run in java code and the receiver will be a functional interface, example:)

    myFunc <- function() {
        # do some stuff
    }

javaClient$runProvidedFunction(myFunc)

    @Export
    public void runProvidedFunction(Runnable runnable) {
        runnable.run();
    }

But the access from different threads is restricted

  • Build a native image with the runnable script and jar

I want to know if these motives are possible and if possible, the proper way to do so.

Jugal Mistry
  • 160
  • 1
  • 9

1 Answers1

1

Run the R script with the provided jar

It seems that you figured it out yourself. Either you can use java.addToClasspath or you should be able to add to the class path when starting R/Rscript by --vm.cp=.... Both options work only in the JVM mode.

Execute the script with Rscript

Again, seems you figured it out?

Pass a function to the executor from the R script so that the function is called accordingly by the ScheduledExecutorService

This is tricky. R is single threaded language and so is FastR. You cannot execute R code from multiple threads, but what you can do is to create multiple FastR contexts and use a new context in each thread (you can use ThreadLocal for that). See https://www.graalvm.org/reference-manual/embed-languages. You should be able to use the Context API from the Java code that you call from R and create new contexts.

Build a native image with the runnable script and jar

That should be fine. Note that there are some known issues with some R packages and native image that we have not fixed yet.

The entry point must be some Java application that uses the Context API to start FastR. The R script you can embed into the Java application as a String or a resource. Note that the R code will not be ahead-of-time compiled. It will still be JIT compiled at runtime, but everything else will be ahead-of-time compiled: your Java code, any Java libs you use, FastR interpreter and runtime.

Steves
  • 2,798
  • 21
  • 21
  • I should have added that I wanted the R script to be the entry point and I really wanted to leverage multithreading, scheduled executors from java in R to make the R code somewhat reactive. Is there a way to force multithreaded behavior? I was already able to pass a function from R and receive it as a functional interface in the java code. – Jugal Mistry Jan 01 '22 at 10:53
  • There is no semantics for multi-threading in R as the language. We could make something up for FastR, but updating FastR to support it would be a large effort. – Steves Jan 03 '22 at 09:16
  • You can still make the R script be the entry point by writing small Java wrapper that does nothing but just creating Context and executing the R script. – Steves Jan 03 '22 at 09:16
  • Btw. FastR can be executed from multiple threads, it just must be one thread at a time, i.e., not concurrently, if that can help anyhow. – Steves Jan 03 '22 at 09:18