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:

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

https://git.limo/redrabbit/git-limo

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

ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
Phillipp
Hey, I have a NanoPi-M3 and try to install Elixir on their Ubuntu image. I followed the Raspberry Pi installation instructions from the ...
New
Codball
Mix format works fine if run from the cmd. I’ve followed this to facilitate the implementation into VSC which involves downloading an ext...
New

Other popular topics Top

pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
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
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
AstonJ
by Lance Halvorsen Elixir and Phoenix are generating tremendous excitement as an unbeatable platform for building modern web application...
460 27162 124
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

We're in Beta

About us Mission Statement