akash-akya

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_select for 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 :slight_smile:

Most Liked

akash-akya

akash-akya

Pushed changes to use new approach

New approach

  • exec spawner shim using port, we are no longer using NIF for this
  • exchange stdin and stdout fd from shim to exile using unix domain socket (thanks to new socket API)
  • spawner shim 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

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

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. :confused:

Thanks for your help. I look forward to following your progress.

elcritch

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

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.

Where Next?

Popular in Libraries Top

Qqwy
TypeCheck: Fast and flexible runtime type-checking for your Elixir projects. Core ideas Type- and function specifications are const...
336 13801 100
New
kip
Image is an image processing library for Elixir. It is based upon the fabulous vix library that provides a libvips wrapper for Elixir. I...
574 16576 179
New
mathieuprog
Hello :wave: Allow me to introduce you to Tz, an alternative time zone database support to Tzdata. Why another library? First and fore...
New
sabiwara
Dune is a sandbox for Elixir and aims to safely evaluate user-provided code. You can try it out using this basic Elixir playground made ...
New
wfgilman
I’ve cleaned up and open sourced three financial libraries I was using for my company. They are bindings for the APIs of these three comp...
New
Qqwy
Hello everyone, I wrote a small library today called MapDiff. It returns a map listing the (smallest amount of) changes to get from map...
New
woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New
mattludwigs
Grizzly is a library for working with Z-Wave devices. Z-Wave is a low-frequency radio protocol for controlling smart home devices on a me...
New
ostinelli
Let’s write a database! Well not really, but I think it’s a little sad that there doesn’t seem to be a simple in-memory distributed KV da...
New
bluzky
You may know https://ui.shadcn.com/, a UI component library for React. I really love it’s design style and components. I’ve built some co...
381 12391 119
New

Other popular topics Top

New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
beno
I will often find my self writing things similar to: case some_value do nil -> something() "" -> something() _ -> someth...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
273 38985 115
New
magnetic
Hey :wave:t3: Elixir community, I’ve been learning Elixir, and working on some side projects. My editor of choice is VSCode, and althoug...
New

Sub Categories:

We're in Beta

About us Mission Statement