Templater
Templater copied to clipboard
User System Command hanging, timeout triggered
- 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
, 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.
- 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? - 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.
This honestly sounds like fork()
and exec()
shenanigans but I'm probably completely off base. Curious to see what the actual reason is.
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.
Is there a way to disown
the process similar to what fish
can do?
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