MarioFlach

MarioFlach

Anonymous functions and NIF resources when working with multiple processes & nodes

Hi.

I’ve started experimenting with adding distributed support to a project of mine:

https://github.com/almightycouch/gitgud

You can see a demo running on fly.io here:

For a little bit of context:

  • I have a NIF wrapper around libgit2 written in C.
  • Most functions take and return wrapped C pointers (see erl_nif - Resource objects).
  • I have implemented a GenServer in order to safely (thread-safety) manipulate a repository from multiple processes simultaneously.

Here’s a brief example of the API:

alias GitRekt.GitAgent

# load repository
{:ok, agent} = GitAgent.start_link("tmp/my-repo.git")

# fetch master branch
{:ok, branch} = GitAgent.branch(agent, "master")

# fetch commit pointed by master
{:ok, commit} = GitAgent.peel(agent, branch)

# fetch commit author & message
{:ok, author} = GitAgent.commit_author(agent, commit)
{:ok, message} = GitAgent.commit_message(agent, commit)

IO.puts "Last commit by #{author.name} <#{author.email}>:"
IO.puts message

In this example,

  • agent is a PID,
  • branch is a struct in the form of %GitRef{oid: <<...>>, name: "master", type: :branch},
  • commit is a struct in the form of %GitCommit{oid: <<...>>, __ref__: c_ptr},
  • author is a map in the form of %{name: "...", email: "..."},
  • message is a binary.

I’m not very familiar with the internals of the BEAM and I’m not sure what really happens when passing NIF resources around processes. In the example above, commit contains the referenced pointer to an actual C pointer (:__ref__ field).

I assume that because the current process is only holding the pointer, thread-safety is not compromised (manipulating the C pointer is always done in the dedicated GitAgent process).

So here are my first questions:

  1. Is a NIF resource object a simple safe-pointer that can be passed around processes?
  2. Does any copy operations happens?
  3. Is it garbage collected like everything else?

Now GitAgent provides a subset of libgit2 functions. In order to implement more complex things on top, it provides GitAgent.transaction/2:

def resolve_commit_all(agent, commit) do
  GitAgent.transaction(agent, fn repo ->
    with {:ok, author} <- GitAgent.commit_author(repo, commit),
        {:ok, committer} <- GitAgent.commit_committer(repo, commit),
        {:ok, message} <- GitAgent.commit_message(repo, commit),
        {:ok, parents} <- GitAgent.commit_parents(repo, commit),
        {:ok, timestamp} <- GitAgent.commit_timestamp(repo, commit),
        {:ok, gpg_sig} <- GitAgent.commit_gpg_signature(repo, commit) do
      {:ok, %{
        oid: commit.oid,
        author: author,
        committer: committer,
        message: message,
        parents: Enum.to_list(parents),
        timestamp: timestamp,
        gpg_sig: gpg_sig
      }}
    end
  end)
end
  • The anonymous functions is executed by the agent process,
  • repo is a NIF resource object handle (wraps git_repository),
  • commit is not explicitly passed anywhere to it but is captured from the scope,

Again, I assume that things just work™ because my NIF resources (repo and commit) are not violating anything because they are always manipulated from the same process.


So in my adventure of running my project in a distributed environment. I have a few things I’d like to understand.

  1. Is it OK to share NIF resources across different nodes with my current approach? Basically, the NIF safe-pointers are used like simple references and are only manipulated from a unique dedicated process.

  2. How does the garbage collector handle NIF resources that are shared between multiple processes? What about running process on a different node?

  3. How are captured variables handled when running anonymous functions on different nodes? What about NIF resources?

Also I would be very interested in any good papers, docs on the internals of the BEAM for this kind of things. I’ve came across a few interesting topics in the forum

Any tips on how to debug and profile code (memory leaks, garbage collector, etc.) when working with NIFs are also very welcome.

Where Next?

Popular in Questions Top

aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
Lily
In templates/appointment/index.html.eex: &lt;%= for appointment &lt;- @appointments do %&gt; &lt;tr&gt; &lt;td&gt;&lt;%= appoi...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
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
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New

Other popular topics Top

marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
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...
274 41539 114
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement