SelfSchedulingRunnable continues running after unregister

I have a thread which needs to run essentially immediately on the first run, and every 60 seconds after that. Normally I could simply use one of the rate/delay registration methods on ExecutionManager. In this particular case, however, it is important that the timing actually line up at the turn of each minute. I noticed the following method and decided to try and use it.

public void register(String owner, String name, SelfSchedulingRunnable command);

This worked out very well as far as scheduling goes. The problem I ran into is that when I call unregister for this SelfSchedulingRunnable, it gets removed from the execution status page only temporarily until it revives itself at the turn of the next minute. Is there any way to get it to unregister fully such that currently scheduled runs at the time of calling unregister do not take place? I can keep track of some flag variable that allows me to return immediately from run() and return 0 from getNextExecDelayMillis() to prevent further scheduling, but things would be a lot cleaner in my case if the unregister simply ended everything. I know that I could also chain this together manually using successive executeOnce() calls as needed, but I prefer the diagnostics in the status page that actual scheduling provides.

Below is code that demonstrates this behavior.

    @Override
	public void registerSelfSchedulingRunnable() {
    	ExecutionManager executionManager = getSelfSchedulingRunnableExecutionManager();
    	executionManager.register("owner", "name", new SelfSchedulingRunnableTest());
    }

    @Override
	public void unregisterSelfSchedulingRunnable() {
    	ExecutionManager executionManager = getSelfSchedulingRunnableExecutionManager();
    	executionManager.unRegister("owner", "name");
    }

    private static class SelfSchedulingRunnableTest implements SelfSchedulingRunnable {

        private Instant nextScheduledTime;

        private SchedulingController controller;

        private Logger log = Logger.getLogger(SelfSchedulingRunnableTest.class);

        @Override
        public void run() {
            Instant now = Instant.now();
            log.info(String.format("SelfSchedulingRunnableTest running at: %s", now));

            Instant nextMinute = now.truncatedTo(ChronoUnit.MINUTES).plus(Duration.ofMinutes(1));
            nextScheduledTime = nextMinute;

            controller.requestReschedule(this);
        }

        @Override
        public void setController(SchedulingController controller) {
            this.controller = controller;
        }

        @Override
        public long getNextExecDelayMillis() {
            if (isFirstRun()) {
                return getInitialDelay();
            } else {
                return getNextDelay();
            }
        }

        private boolean isFirstRun() {
            return nextScheduledTime == null;
        }

        private long getInitialDelay() {
            return Duration.ofSeconds(1).toMillis();
        }

        private long getNextDelay() {
            Instant now = Instant.now();
            if (nextScheduledTime.isAfter(now)) {
                return nextScheduledTime.toEpochMilli() - now.toEpochMilli();
            } else {
                // This shouldn't happen under normal circumstances, but we don't want to return
                // 0 or a negative number, because that signals Ignition not to reschedule
                return 1;
            }
        }
    }

Essentially I set things up so that I could call the register/unregister through RPC from the scripting console.

Below is an image showing an example session running this code. At some point between the logged data I ran unregister and watched the execution status page. Sure enough, the thread was removed from view. Once the turn of the minute came around, however, it ran again and added itself back into the map and continued to run every minute.

As a side question… I poked around the relevant SDK code and everything made sense to me in terms of the future being cancelled and stuff, but I could not debug due to line numbers not matching up. I am running 7.9.9 locally but I think my SDK version is set to 7.9.4 in Maven. Should I be able to set it to 7.9.9 and successfully debug into the Ignition code?

Thanks!

Can you show us the implementation of getSelfSchedulingRunnableExecutionManager()

Are you sure that method returns the same ExecutionManager instance every time?

Hi @chi.

During setup() of the module’s gateway hook class, I create an ExecutionManager and set it to an instance variable like so:

selfSchedulingRunnableExecutionManager = context.createExecutionManager("Self Scheduling Runnable Test", 1);

getSelfSchedulingRunnableExecutionManager() is basically just a stand-in for the actual code which was a bit more complicated, but all it does is get back to the gateway hook class instance and pull the ExecutionManager instance variable out. Since it is little more than a simple getter, I am quite certain it is the same instance. The other factor is that the unregister does indeed remove the diagnostic information temporarily from the gateway page, which confirms that it is the right instance. I suppose I could have written a simpler test driver where the ExecutionManager is created as a local variable, used to register a SelfSchedulingRunnable, and then used to unregister the runnable a few seconds later. Since I was dealing with minute intervals in my case and the client/gateway communication times out after 60 seconds by default, I structured it in such a way that I could call the register and unregister whenever.

I imagine you will find the same behavior as I did if you use the above template on 7.9.9, but I could condense it a bit further if it would help!

Thanks,
Ryan

Ryan,

try removing the call to controller.requestReschedule(this); from your run() method. Most probably this is the cause of your problem.

1 Like

Indeed, that fixed the problem! For some reason I was thinking I needed to call this on each time around to signal that I wanted my Runnable to keep getting scheduled, not realizing that Ignition already reschedules it internally after each run.

Thanks for your help @chi!