akash-akya
Exile - NIF based alternative to ports for running external programs. Provides back-pressure using non-blocking io
Exile is an alternative to beam ports for running external programs. It provides back-pressure using non-blocking io, and tries to fix all issues associated with ports.
Github: https://github.com/akash-akya/exile
Rationale and issues associated with exiting approaches are mentioned here
This is another stab at solving the issues associated with running an external command. My other approach ExCmd uses named pipes for solving these issues. But, since :file functions are blocking operations in the beam, this can cause issues. As of now, there are no non-blocking file operations available in :file module. So the only way to do non-blocking io is using NIF/port-driver.
Exile.stream! is a stream interface for interacting with the external program. It works with both Enumerable and Collectable. Apart from being compostable, stream handles closing stdin and terminating the external process.
Exile.stream!(~w(ffmpeg -i pipe:0 -f mp3 pipe:1), input: File.stream!("music_video.mkv", [], 65536))
|> Stream.into(File.stream!("music.mp3"))
|> Stream.run()
Key differences when compared to other middleware based libraries and port
- fixes issues associated with ports
- no more zombie process
- can selectively close stdin
- its is demand-driven (back-pressure)
- it uses
enif_selectfor asynchronous io, so it utilizes resources efficiently - uses non-blocking read/write system calls. so it can never block the scheduler
- it does not use any middleware
- no additional os process. no performance/resource cost
- no need to install any external command
- can run many external programs in parallel without adversely affecting schedulers (when compared to ExCmd)
- stream abstraction for interacting with the external program
- should be portable across POSIX compliant operating systems (not tested)
Non-blocking io can be used for other interesting things. Such as reading named pipe (FIFO) files. Exile.stream!(~w(cat data.pipe)) does not block schedulers so you can open hundreds of FIFO files unlike default :file module.
Please check the project page for more detail.
Note: Exile is experimental and it is still work-in-progress
feedback is welcome 
Most Liked
akash-akya
Pushed changes to use new approach
New approach
- exec
spawnershim using port, we are no longer using NIF for this - exchange
stdinandstdoutfd from shim to exile using unix domain socket (thanks to new socket API) -
spawnershim then exec specified binary - communicates with os process using NIF like before
Advantages over old approach
- exec code is simpler and slightly safer now, since we no longer need NIF for exec
- works with OTP-23 (but now minimum requirement is OTP-22.1)
- does not require polling for capturing process exit status
For more details please check: New approach to exec external process by akash-akya · Pull Request #8 · akash-akya/exile · GitHub
akash-akya
Exile.stream!(input_stream: File.stream!("priv/input.txt", [], 65536), stderr_to_console: true)
Hi, the field name is input not input_stream. Exile is ignoring input_stream param. It’s hanging because there is no input.
Anyway, exile should fail with proper error message for an invalid option. Thanks for pointing it out. I’ll fix this
mbklein
It turns out I did have the input parameter correct in my original code, but fixing it in the test code allowed me to figure out the rest and I’ve got it working now. The dumb typos are always the most frustrating to find and fix. 
Thanks for your help. I look forward to following your progress.
elcritch
Looks like a handy library! On Nerves muontrap is used to handle cleaning up various zombie processes. To do this it seems muontrap can use some Linux cgroup magic to kill sub-processes of a zombie. Not sure how it’s done but thought you might find the technique useful for the zombie sub-child issue.
dimitarvp
Very, very nice. Love it.
Just one note, might be misinformed: I am worried that with a C dependency the crate might not compile at all on Windows – have you tried it? Or are you targeting UNIX-es only?
That’s the reason I am only doing NIFs with Rust – got impressed in the past how quickly and easily it compiles stuff on Windows without complaints.







