Creating long running NIFs (or something else)

Hi,

I’m not sure if a NIF is the best option for this - I’m also not experienced with C (don’t even know how to properly link libs, for instance) at all but I’m taking small steps in order to use OpenGL.

Imagine you are building a graphical interface that needs to do OpenGL. I can write a small program in C that just opens a window and stays there. All good.

My question now is, if I want to interact with this program, like send it draw commands so it applies them to the gl context, what would be the best way to do that? Although initially a toy project I would definitely prefer the option that is the most performant, as this would be something akin to a browser.

Do you know any good resources/libs for looking into this? I’m going for the lowest common denominator possible - elixir/erl <-> c program using glfw3 - the C program also has to be able to callback somehow, since glfw handles user interaction on the window as well.

Any pointers welcomed, thanks

You could use a simple TCP socket between the two programs. Erlang has great support for that. Your C program would connect to the TCP server. Have a look at Erlang -- gen_tcp .

UI naturally lends itself to message passing so I’d put a GenServer in front of your native UI code so as to make it innately serial (one message at a time / no parallel processing of messages).

As for the native interface, it could be C, Rust or anything you are comfortable with (but ergonomic caveats do still apply so I’d pick Rust 99% of the time).

Thanks, yes that could definitively work and I’ve used gen_tcp in the past.
I’m trying to learn a bit more about lower level code, with C and OpenGl in this case, and Erlang interfacing with these.

I’m not really knowledgeable when it comes to it so perhaps a tcp socket would be in fact the best way of doing it - but if say, you’re doing a lot of drawing wouldn’t it benefit somehow of being in a NIF ?

My idea would be that initially each tab would be a gen_statem, but eventually would move to being an erlang node. Then shared resources (caches, etc) would be shared through mnesia.

Yes, I’m not really comfortable with anything lower level hence why I want to practice - I chose C because I can’t understand what Rust brings on without understanding C - most examples for OpenGL go with c++ but I would really prefer to learn the basics first before going on some rabbit chasing :smiley:

Fair. As a preliminary spoiler alert: in C it’s trivial to allocate a memory buffer of 10 bytes and then try to get a value from the 11th element. This, oversimplified, has been the reason for most of the security fiascos in the last several years.

Rust prevents those.

1 Like

Very oversimplified I would guess :slight_smile:

Wait a second, am I wrong that Rust does not prevent you from trying to grab the 11th element of a 10-vector! that’s a runtime panic. If you want erroring bounds checking you need a wrapped type

Ah, I was aiming at arrays. :slight_smile:

And still, runtime panic isn’t the same as buffer [over|under]flow which can lead to the program being compromised. But I agree that panics aren’t very useful by themselves.

Just FYI you do have full access to OpenGL from elixir already today. Have a look at the wings3d project. It’s an OpenGL based 3d modeller written in Erlang http://www.wings3d.com/

Source code: GitHub - dgud/wings: Wings3D is an advanced sub-division 3D modeller.

1 Like

I had looked at wings (was surprised the forum still seems to be active), and also scenic that uses GLFW too, but got a bit overwhelmed on how to start/work the c part :slight_smile: like from easy mode and build from there as that’s how I learn better. I will certainly look into them for inspiration.

1 Like

If you would go with s external binary, then I think that port or port driver would be much better than creating TCP connection between the two.

1 Like

Yeah I think that in the end that would be the most reliable - right now I got a unix socket working and I can paint a window on command already - the issue I’m foreseeing is how to handle crashes issues on the render side so ports could help with that.

In terms of security you mean your program, when written safely, being safe to use by a user without leading to memory access shenanigans that you might, without intention, introduce in a C program right?

Yep, exactly.

Note that ‘safety’ from the erlang vm pov is not the same as ‘safety’ from the rust pov. When you are a safe nif, one thing is that you must never, ever cause a panic or segfault because that will completely upend all of the isolation and protection guarantees that you get with the vm, unless you are in distributed erlang and have taken the pains to work out cross-node failover.

Rust will happily panic on if you buffer overflow or have an error on malloc, or a ton of other things, because it’s priority is to prevent creating side channels that can be used to exfiltrate data, and for that security model, taking down the entire process is preferable to security problems.

Which is to say, rust does not guarantee you nif safety out of the box. You absolutely need to write tests that exercise the hell out of your nifs to be confident these scenarios do not happen.

There’s also some weird shit in the vm. When writing an async bridge between yielding nifs and my zig code, I found that there was a place where I absolutely needed to have a 10ns sleep or else it would cause the erlang vm to sporadically segfault. Don’t know why that’s the case, it’s not something that I can be confident I’ll ever know. I didn’t even have a good reason for putting that nano sleep exactly there, it was just a gut feeling.

1 Like

Take a look at Nim and Nimler if C and linking seems too intimidating. Nim is much easier to learn than Rust but still provides memory safety by default. It’s become my default for NIF’s since it’s new GC plays well with BEAM. Glfw might require some fiddling with linking but the forums are helpful.

Edit: actually the nimler docs is probably better than its github: Getting started - nimler

Plus, Nim has been used in a few moderate sized games. There’s decent OpenGL libraries like NimGL or nim glfw. Or a cool looking 2d library, pixie.

Happy hacking!

P.S. If you use long running NIF’s they’ll need to be marked as “dirty nifs”. Also I wouldn’t worry upfront about killing beam, it’s just that we get used to the beam rarely ever dying. It sounds like you’re interested in local ui stuff anyway.

2 Likes

Yeap, I tried out some examples from the rust book (?) and even went looking at my backups to see if I still had one piece where I did some assignment that I thought was non-sensical to compile and it did so I was a bit confused on how that was possible (in light of what I had heard - it’s a bit vapid because I don’t have the example anymore and it could totally be my own noobness and normal that it would compile).

What I wanted to understand in terms of using NIFs or other ways of running external to the VM code was what would be the best approach for instance to driving an OpenGL context. NIFs right now seem a bit out of my league. I ended up doing a port managed by a statem, and the statem is actually the one driving the loop with 5ms timeouts. Architecturally it looks pretty clean, the timeout can be shortened to less if needed still, but for 60fps, I need 16ms cycles, so it seems a port is totally able to drive this.
statem->5ms timeout cycle->port_command::2->c read switch->redraw

I also tried with a unix socket and it worked fine, but my thinking was it would make it more difficult if someday I wanted to port it over to other archs? Besides having to take care of a bit more stuff when handling it. I’m now looking into how to pass file descriptors between C programs and between those and the BEAM (not even sure you can, sockets seemed to be difficult in that it needs a lot of ritual due to the ownership/security of the socket) fun :smiley:

Probably there’s some dragons lurking around - at 10ns you’re probably hitting some race condition?

1 Like

Thanks, I will have a look at it too.
I ended up ditching GLFW and going for OpenGLES right now, following, the linking became much easier GLESv2 EGL X11 and it’s up, GLFW I was having some trouble understand the loaders and what I actually needed to make it run, between glew’s glut’s and glwtf.

It helped that this book had a great example and source code http://www.opengles-book.com
Right now I’m using gcc and make - just everything in a single gcc invocation, imagine doing it properly will be another adventure.

1 Like

For long running you can also do threaded and yielding nifs too! Well, I don’t know about other language support for it, but ABI supports it. You can see the differences in action here:

Starting at about 10m30s

1 Like