@AsyncFunction

We’ve got a function that takes a good minute or so to complete (very memory intensive and no other way around it), so we were looking at the @AsyncFunction annotation for the RPC call, but it doesn’t seem to make a difference. I read through 4.8 in the SDK guide, but am having trouble making sense of it.

Do we need to make a AsyncClientTask as well as set the RPC to @AsyncFunction? Is there an example of how to make that happen? The function we’re trying to use takes a String input each time it runs.

Hi,

By doing nothing besides marking your function as an AsyncFunction, you’re changing the way the results get delivered, but not making much of a difference otherwise. To maintain backwards compatibility with the way Ignition used to do operations, invoking a function like this from the client will still block the EDT, and thus freeze the display. However, there is a subtle difference: if on the gateway, you get the [tt]TaskProgressListener[/tt] for the operation through [tt]GatewayContext.getProgressManager().getProgressListenerForThread()[/tt], and update it from time to time, you can prevent the operation in the client from timing out at the normal 60 second timeout.

In other words, here’s what happens:

  1. Normal direct RPC call: client calls to gateway on event dispatch thread, waits for result. Thread is blocked, so the UI freezes, and the socket has a timeout of 60 seconds. No matter what, if the call doesn’t return before the timeout, an error is thrown.
  2. Call to AsyncFunction: The function on the gateway is invoked. It returns immediately with a special token telling the client it’s an async call. The client blocks, and periodically checks for updates from the gateway task. If no updates arrive during the timeout period, and error is thrown.
  3. Calls invoked from [tt]ClientProgressManager.runTask[/tt]: Basically the same as #2, but started from a different thread, so the UI doesn’t block.

Right now, async tasks will cause the progress screen to be displayed. Shortly, we intend to modify the system a bit to both provide better status, and let modules opt out of this. Anyhow, hope this sorts it out for you a bit.

The AsyncClientTask is pretty much just a runnable, but the run function takes a progress listener that you can update (most useful when performing a long running task in the client that doesn’t involve the gateway, not so much for what you’re doing), as well as a title for the progress display, and a bool indicating whether the operation is cancellable. If you want to support cancelling, you need to get the progress listener on the gateway, and check isCanceled periodically during execution. If true, just stop what you’re doing.

Regards,

Unfortunately not what I was hoping to hear, but it does make sense. If I’m reading it right, there’s really no way to offload a gateway-scope task unless it can periodically provide updates through the TaskProgressListener at least every 60 seconds. I’m thinking my best bet here is to take an approach like this one and fire off a Java-based async call while continuing to “update” the progress every few seconds in the RPC method until the true async call returns a callback indicating it’s completed.

Is there a better way I’m missing or does that sound about right?

It’s not the most elegant way of doing it, but it seems to work for now! :slight_smile: It completed without an exception after ~5min, but I never did get a progress screen displayed. Here’s the code I used:

[code]@AsyncFunction
public boolean MyRpcCall(String item) throws RpcException {
RpcEmployer empl = new RpcEmployer();
//LongRunningTask implements Callable and contains the logic for what we’re doing
empl.andAction(new LongRunningTask(item));
Integer p = 0;
while(!empl.isDone()) {
//The gateway hook stores the context in IgnitionGatewayUtilities for use throughout.
TaskProgressListener progress = IgnitionGatewayUtilities.getGatewayContext().getProgressManager().getProgressListenerForThread();
progress.setProgress§;
p++;
progress.setProgressMax§;
try {
Thread.sleep(30000);
} catch (InterruptedException e) {

	}
	logger.warn("Slept for 30 seconds, retrying.");
}
return true;

}[/code]

Did I miss something to get the progress window displayed?

Ha, I should check the forum before I work on an example. I was making something very similar:

protected class LongTask extends Thread {
	private boolean finished = false;

	public boolean isFinished() {
		return finished;
	}
	@Override
	public void run() {
		try {
			doLongOperation();
		} catch (Exception e) {
			//Log err
		} finally {
			finished = true;
		}
	}
}
@AsyncFunction
public boolean yourRPCCall() {
       TaskProgressListener listener = getGatewayContext().getProgressManager().getProgressListenerForThread();
	int progress = 0;
	int maxProgress = 10;

	LongTask task = new LongTask();
	task.start();
	listener.setProgressMax(maxProgress);
	listener.setNote("Executing task...");
	while (!task.isFinished()) {
		listener.setProgress(progress++ % maxProgress);
		if (listener.isCanceled()) {
			task.interrupt();
			break;
		}
	}
}

So, as you see, pretty much equal.

As for the progress screen, I’m not sure. If’ you’re concerned about it and want to look into it further, you can try turning on the logger for “ClientProgressManager” in the client (to do this, you need to go to Help>Diagnostics in the designer or client, right click on the tabs, add the logger level tab, and find it). This should print out plenty of messages about the task updates received (to the client console, also visible on the Diagnostics panel).

Regards,

Haha, at least I’m on the right track then. Any chance you could post up a screenshot of what the progress indicator should look like? If I’m understanding it right, it’s just a dialog popup that the system generates - there isn’t anything I need to do in the designer, right?

Yeah, it’s this screen. There really isn’t anything you should need to do, maybe it’s not showing your progress because you’re not setting a message. Anyhow, as long as your task isn’t blocking the UI thread, and completing when it takes over 1 minute, you’re fundamentally OK.

I was going to suggest one other strategy. It’s a bit more work, but I don’t know what these tasks are, and how many of them you might have at a time. Instead of relying on the async function system, implement the async portion yourself. You’re basically doing this anyway, but here’s what I mean:

  1. You call your gw function over RPC to start an event.
  2. Your gw function queues up a task to run in a thread, or using gatewayContext.getExecutionManager(). Do a bit of book keeping to keep track of tasks.
  3. You return the ID for your task
  4. In the client module, periodically call a different RPC function to get the status of a task given its ID.

Basically, this is what the progress system does for you, and now that you’ve got it more or less working there isn’t much reason to change, but I was going to suggest that from the start. Again, it really depends on what kind of control you want over these tasks.

Regards,

...which? Not seeing an attachment :slight_smile:

As far as the rest, that's probably the route we'll go long-term. For this week I just need to demonstrate that we're capable of doing any kind of long-running operation without the client getting an error message. We've already got a framework built on top of the ExecutionManager for scheduled tasks that we run, so it shouldn't be too much to set it up to handle what you described. Thanks!

Err, edited to include attachment.

Ah, there we go. I appreciate the help!