I have an Elixir application that is structured the following way:

Application --> Supervisor --> Worker

The Application Module is fairly simple, it has a use Application and a

def start(_type, _args)
  MyApp.Supervisor.start_link()
end

The Supervisor is a bit more complicated:

def start_link() do
    {:ok, pid} = Supervisor.start_link(__MODULE__, [], name: __MODULE__)
    halt_exit(wait_for_child())
    {:ok, pid}
end

def init(data) do
    children =
    [
        worker(MyApp.Worker, [], restart: :transient)
    ]

    Supervisor.init(children, [strategy: :one_for_one])
end

where halt_exit(pid) performs a Process.sleep(100) and then calls itself every time it finds the process pid is still alive (or simply returns nil when pid is not alive anymore), and wait_for_child finds the Worker module's pid (taking into consideration that it might have to wait until it's started).

My Worker module interacts with the user using IO.puts and IO.gets. Why all this complicated unnecessary logic? It seems that

  • when using mix run and omit halting exit, the application immediately dies
  • when using mix release (distillery) and run myapp.bat console from cmd and omit halting exit, my application starts, immediately dies, and then iex is started
  • when using mix release w/ myapp.bat console and include halting exit, my app outputs correctly, but can't receive any input (and by this I mean I can't type into the window of werl.exe)
  • when using mix release w/ myapp.bat foreground, my app starts and only then there is an error in the batch file (The system cannot find the file `%get_pid_cmd%`. ERROR: The search filter cannot be recognised. '@call' is not recognized as an internal or external command, operable program or batch file.) at which point my app terminates.
  • when using mix run --no-halt (no exit-halting logic inside app), my app starts, terminates immediately, and the erlang VM remains running in the cmd window
  • only when I mix run and include halt_exit/1 does my app works as expected

From all this, I concluded that 1) the moment my application module's start/2 returns, the app is shut down, which doesn't make much sense as Elixir is designed for applications that put the load on other processes instead of, not besides the application's main module. 2) input is done through the main Application module's process (but for some reason only when run in a separate werl.exe window, seems to work fine with mix run), so I can't put any blocking-until-work-finished logic there if I want my user input.

How can I solve this, and equally importantly (so I can avoid future problems) what behaviour causes my problem? I would need to be able to run this on a computer without mix and Erlang VM installed, so my only option is to make it work with the distillery package generated, which at the moment I can't.

No amount of googling has led me to similar tactics to artificially halt exit, and no forum/tutorial I've found mentions problems with this "early-exit" so I guess the correct way to solve this would be to somehow get it not want to exit while any process is running.

1

There are 1 best solutions below

0
On

The system cannot find the file %get_pid_cmd%. ERROR: The search filter cannot be recognised

In the following line:

@for /f "delims=" %%i in (`%%get_pid_cmd%%`) do set pid=%%i

Try changing the backticks around %%get_pid_cmd%% to single quotes.

Also, in the following line:

set get_pid_cmd=%escript% %nodetool% eval %node_type% %node_name% -setcookie "!cookie!" "erlang:list_to_integer(os:getpid()).'

Change the final single quote to a double quote.

Source: https://www.bountysource.com/issues/50408118-starting-in-foreground-mode-on-windows-fails-to-get-pid-and-returns-errorlevel-1

Making these two changes in boot_win.eex, deleting the _build folder, and rebuilding worked for me.