Calling function upon interruption of ASH script

Saklad5

Member
Is it possible to have an ASH script catch an “abort” instruction and run a function to clean up, rather than simply stopping immediately?

Alternatively, is it possible to accept user input without halting unless it is received? Like a version of wait() where it is listening for instructions (e.g. “cancel”) until the duration is up?

I know it is possible to use user_confirm() with a timeout, but that creates a dialog box rather than accepting CLI input. I want my program to run on a headless system occasionally.
 
You put everything you want to 'try' in the try block, and then when it aborts the script will continue with the finally block (and everything after that).

For example:
Code:
try {
	set_property("restUsingChateau","false");
	if ( my_mp()>my_maxmp()-10000 )
		cli_execute("maximize mp, equip brimstone bracelet, equip pantsgiving, -10000 stackable mana cost, -equip bjorn, -equip crown of thrones, -familiar");
	else if (equipped_item($slot[pants]) != $item[pantsgiving])
		equip($item[pantsgiving]);
	while ( get_property("timesRested").to_int() < total_free_rests() )
	{						
		cli_execute("rest free");
		libramBurn();
	}
}
finally set_property("restUsingChateau","true");
 
Last edited:

Saklad5

Member
That’s the issue: I can’t get CLI input and have it abort in the absence of said input.

Or do you mean a user-given abort command will cause it to proceed to “finally”?
 
Last edited:

Saklad5

Member
I just thought of an incredibly stupid hack to at least stop a script gracefully: have it call another ASH script that is hard-coded to return true. I can later change this other script to return false during execution.

This is obviously not a good idea for anything being used by others, of course.


Edit: This doesn’t appear to be working. Does the ASH interpreter import all the scripts at launch, rather than as they are called? Because that would sink this plan entirely.
 
Last edited:

fronobulax

Developer
Staff member
Edit: This doesn’t appear to be working. Does the ASH interpreter import all the scripts at launch, rather than as they are called? Because that would sink this plan entirely.

I think it does although if anyone says I am wrong, then I am.

Perhaps reading and writing a map?

I'm confused about your application. You can avoid the dialog prompt by entering parameters on the command line. If you are trying to run something headless and somehow make it stop and exit cleanly then your idea of having it periodically poll an external file and exit based upon that file is probably your best bet. But you obviously need access to the underlying filesystem from outside of mafia.
 
That’s the issue: I can’t get CLI input and have it abort in the absence of said input.

Or do you mean a user-given abort command will cause it to proceed to “finally”?

Yes, a user given abort command (such as hitting escape) is treated as any other, and as such will only break you out of the try block.
 

fronobulax

Developer
Staff member
Yes, a user given abort command (such as hitting escape) is treated as any other, and as such will only break you out of the try block.

What are the user given abort commands that can be entered via the gCLI when a script is running and running headless? I didn't recall any but that is probably the easiest solution.
 

heeheehee

Developer
Staff member
I think it does although if anyone says I am wrong, then I am.

That doesn't sound right. My understanding of the situation is that scriptA imports scriptB; you then modify scriptB while scriptA is still running. The script (and its imports) is parsed when you execute the script, and cached (I think? Could be wrong) until the script is modified.

Why doesn't user_confirm with a timeout work for your use case?
 

heeheehee

Developer
Staff member
That is, if you're running in a headless environment, can't you just use a virtual framebuffer, and the dialog box will just be silently generated and closed? I don't see any problem with that.
 

Veracity

Developer
Staff member
When you execute script A for the first time, ASH parses it (including all of its imports) into a parse tree which is stored in an interpreter associated with that script. That is cached with the "last modified date". Every time you want to execute script A, ASH checks the modified date and, if it is unchanged, uses the cached version. If it has changed, it re-parses and re-caches.

Imported files are inserted into the code of script A at the time of import and are therefore parsed only when script A is parsed. If you modify them after script A is parsed and cached, script A does not see that. Script A will pick them up again next time it is parsed, but that won't happen until script A is modified or you restart KoLmafia.

We could save a dependency tree for a script, saving the modification dates of the script and its (recursive) dependencies, and reparse/recache if the script or any of its dependencies change, but we don't do that. That would be a reasonable feature request: clear cached scripts when the script or any of its dependencies change. It would not allow you change a library file while a script using it is currently running and have the new version picked up; although ASH "interprets" scripts, it compiles them into a parse tree and interprets that, rather than interpreting the raw text.
 

Saklad5

Member
Maybe I should move this to a feature request.

I want my program to run specific code if I want to interrupt it. I want to be able to interrupt it in a headless environment, so anything that relies on a GUI is useless. If I could type input into the CLI during operation to accomplish that, that would be fine.

If the abort or exit commands lead to code in the ‘finally’ section of a try-finally control structure being run, that will also work.

I’ve had issues with those in a headless environment before, but that’s outside the scope of this thread. I’ll open a bug report once I have the time to make sure it is, in fact, a bug.
 

Saklad5

Member
Huh. So you cache scripts within a KoLmafia session, updating them based on the filesystem’s modification times, and have a linker collate imported files from that cache at execution time? Interesting.
 
Maybe I should move this to a feature request.

I want my program to run specific code if I want to interrupt it. I want to be able to interrupt it in a headless environment, so anything that relies on a GUI is useless. If I could type input into the CLI during operation to accomplish that, that would be fine.
Typing 'abort' in the cli works for me.

If the abort or exit commands lead to code in the ‘finally’ section of a try-finally control structure being run, that will also work.
If you're in a try block when you give the above 'abort', the finally block (and anything after it) should be executed. That said, I don't know of a specific way to detect that an abort happens: if you successfully make it through the try block, it would also execute the finally block. Though you could of course code the try block in such a way that that can't unless an abort happened.
Alternately, you could do what cc_ascend does, where you use the relay script to set a safely abort property (in the cli, that would be: 'set stop_this_now = true'), which gets checked by the script at convenient points. ( get_property("stop_this_now").to_boolean() )
 
Last edited:
cc_ascend checks a preference for graceful abort. The preference can be set via a relay script while cc_ascend is running.

The Dictator ninjaed me by 6 hours. But yeah, he explained what I do.
 
Last edited:

xKiv

Active member
That said, I don't know of a specific way to detect that an abort happens: if you successfully make it through the try block, it would also execute the finally block.

Possibly something to the effect of
Code:
boolean aborted = true;
try {
  ...
  aborted = false;
} finally {
 if (aborted) {
   ...
 }
}
?

(unless you also return from inside the try {} block; any non-abort way of leaving the block would need its own aborted=false)
 

heeheehee

Developer
Staff member
I thought finally blocks execute even if you return from the function (even in other more general-purpose languages).

Finally blocks are weird.
 

Saklad5

Member
I’ve established that the “abort” CLI command does not work correctly in headless mode (that is, it only skips the queue of existing commands when entered through the gCLI).

Thanks to the advice in this thread, I’ve added a workaround for even that. I wrapped everything in a try block, then added the following behavior to the start of one of the main loops of the program:
  1. Read map from file
  2. If map has value of “true” at specific index, change it to false, overwrite the file with the updated map, then abort

This seems to work quite well. It’s rather sloppy, of course, but it is much better than my previous approach of sending SIGTERM, cleaning up everything manually, and restarting KoLmafia.
 
Top