Templater icon indicating copy to clipboard operation
Templater copied to clipboard

User System Command hanging, timeout triggered

Open dima-stefantsov opened this issue 2 years ago • 4 comments

  • OS: Windows 10
  • Templater version: 1.12.0
  • Obsidian version: 0.14.2
  • Templater settings: timeout 10 seconds, tp.user.exec() = system user function %cmd%

I am using my system user function, which launches whatever I tell it to. For example, if I do

<%*
var cmd = 'php test.php'
return tp.user.exec({ cmd })
%>

I get "php test ok" string (echoed from test.php), as expected. If I do

<%*
var cmd = 'timer.exe 3minutes'
return tp.user.exec({ cmd })
%>

timer GUI application is properly started, empty string is returned instantly, as expected.

But if I try to use php script as a router tp.user.exec -> php -> timer.exe, starting timer.exe from it, weird things happen: templater acts as if command is not finished, waiting for 10 seconds to timeout. Only after 10 seconds it unexpectedly prints output while my cursor is already somewhere else working on the next thing.

here how it looks when I do tp.user.exec(timer.exe) directly image , function finishes instantly printing return value, but in processes tree I can see it will still wait 10 seconds before killing this cmd.exe proxy, effectively moving the timer.exe out of obsidian.exe, it's parentless now.

I want to understand how templater works, I don't want it's behaviour to be unexpected.

  1. does <%* return tp.user.exec({ cmd }) %> hangs until application exit or timeout? If it's true, then how does my exec(timer.exe) can instantly return while timer keeps running?
  2. does <%* return tp.user.exec({ cmd }) %> return instantly as soon as cmd application has died? If it's true, then why does it hangs even after I killed my php application which have spawned a standalone timer process? Basically
    $cmd = "$timer_filename $args";
    error_log("cmd: $cmd");
    pclose(popen("start $cmd", 'r')); // spawns timer as a separate process
    error_log("done");
    echo 'qwe';
    die; // totally instantly kills current php process

in this example I can see log "done" instantly. Php app died instantly as expected. Timer instantly became parentless. But Templater will still hang for 10 seconds and only then will it output the "qwe" string which php application returned. It will print "qwe" faster if I manually kill this now-parentless timer.exe process.

How does this work? Why is it so different? Is it a bug on controlling whether launched app has finished (it did!!), or is it my lack of understanding somewhere? Should be a bug.

dima-stefantsov avatar Apr 09 '22 06:04 dima-stefantsov

This honestly sounds like fork() and exec() shenanigans but I'm probably completely off base. Curious to see what the actual reason is.

AB1908 avatar Apr 17 '22 22:04 AB1908

I've debugged it a little further, looks like when I call timer.exe right from javascript it dies with 10s timeout as well. Only difference is me not calling "await" there, therefore getting my cursor back faster.

So now the difference between js -> php -> timer and js -> timer is settled (hopefully). What remains is: I've spawned a new independent process, yet Templater is still waiting not for just parent process that has died, but for all it's spawned children to die as well.

Can it maybe just wait for exact spawned process to die? It's not like Templater can get extra output after the process that was spawned has already died. There's nothing to wait for.

dima-stefantsov avatar Apr 18 '22 09:04 dima-stefantsov

Is there a way to disown the process similar to what fish can do?

AB1908 avatar Apr 18 '22 11:04 AB1908

All templates does is run exec using node's child_process: https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback

Here's the place in the code this is defined: https://github.com/SilentVoid13/Templater/blob/78d2970f5287d6832664b0e5bfbddb99366a1988/src/core/functions/user_functions/UserSystemFunctions.ts#L87

If you have an improvement please open a PR

shabegom avatar Sep 01 '22 23:09 shabegom