Elixir vs Unix named pipe

Hi,

I’m rather new to Elixir. To cut my teeth, I’d like to write into a UNIX named pipe (created via mkfifo command).

When I try to open it to read or write, I got this:

ook@pity:~/work/light$ mkfifo -m a=rw a_named_pipe
ook@pity:~/work/light$ ls -l a_named_pipe
prw-rw-rw- 1 ook ook 0 juil. 19 17:56 a_named_pipe
ook@pity:~/work/light$ iex
Erlang/OTP 18 [erts-7.1] [source] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.3.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> {:ok, file} = File.open "a_named_pipe", [:write]
** (MatchError) no match of right hand side value: {:error, :eisdir}

Does that mean Elixir don’t know how to talk with a named pipe? I didn’t find any dedicated section for named pipe, and “pipe” is not really easy to search around Elixir :wink:

Thanks for any suggestion.

1 Like

I vaguely recall digging into this at one point and if I recall correctly the underlying Erlang libraries basically restrict File.open to only “standard” unix files. The error gets reported
as :eisdir, when it is actually :eisnotanordinary file. I ran into this problem in attempting to
open /dev/random.

My understanding for this reasoning is that opening anything other than a standard file potentially exposes the BEAM scheduler to delays that would break the VM.

3 Likes

I think the way forward with this is to use the Port facility, but I admit I pretty much gave up on it
at that point. This blog post has more details.

1 Like

Thanks a lot bbense! It makes sense for BEAM scheduler. Will have a look into your link.

1 Like

I’d recommend not using named_pipes but rather a TCP tunnel if possible.

However 1) See rational why no named pipes / devices / etc… at: http://erlang.org/faq/problems.html#idp32909136

However 2) I ran across this that someone did that redirects via a shell script to let you do it via a port: https://gist.github.com/jaredmorrow/1c342c6e9156eddd20b2

3 Likes

For those who land on this page, note that Elixir and Erlang support reading from named pipes since Erlang 21.

For example, in the terminal do:

$ mkfifo pipe
$ echo "Hello\nWorld" > pipe

The last command blocks writing to the pipe. As such, open another terminal, and run this IEx session to read from the pipe:

$ iex
Erlang/OTP 24 [erts-12.2.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit] [dtrace]

Interactive Elixir (1.13.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> {:ok, p} = File.open("pipe")
{:ok, #PID<0.107.0>}
iex(2)> IO.read(p,:line)            
"Hello\n"
iex(3)> IO.read(p,:line)
"World\n"
iex(4)> IO.read(p,:line)
:eof

Note that the other terminal is no longer blocked since the pipe was fully read.

Hope this is useful to you!

Cheers

6 Likes

6 years for a “real” solution. Thank you mguimas, you made my day :slight_smile:

1 Like

Note that underneath File.open there might be some caveats as we are warned about this in the Erlang’s documentation for the file module:

Warning

While this function can be used to open any file, we recommend against using it for NFS-mounted files, FIFOs, devices, or similar since they can cause IO threads to hang forever.

If your application needs to interact with these kinds of files we recommend breaking out those parts to a port program instead.

So my recommendation is to do careful testing when reading from pipes, especially to pay attention to avoid blocking on the pipe from the Erlang side, or at least to avoid blocking from more the 1 ms that is the maximum recommended time for NIFs to block (I suppose Erlang is doing this using some internal NIF).

3 Likes