I intend to manipulate binaries using NIFs for an app which I'm planning to code in Erlang. The gist links to the cpp file and erl file for the NIF are given below.
[Erl Gist Link] https://gist.github.com/abhijitiitr/3a5bc97184d6dd32f97b
[C++ Gist Link] https://gist.github.com/abhijitiitr/24d2b780f2cdacebfb07
Basically I'm trying to do a simple test. Share binaries across NIF calls and successfully manipulate them with successive NIF calls.
If you test the code in erlang REPL by
c(binary_test).
Ref=binary_test:open(<<1>>).
binary_test:increment(Ref,<<3>>).
The binaries stored changes in between the NIF calls. The REPL output for the third command is
1
3
60
60
<<"?">>
I passed <<1>>
during the initialize phase. Why did it change to <<60>>
? I'm unable to figure out whats happening here. Can somebody point out the error?
C++ compile instructions
clang++ -std=c++11 -stdlib=libc++ -undefined dynamic_lookup -O3 -dynamiclib binary_test.cpp -o binary_test.so -I /usr/local/Cellar/erlang/17.0/lib/erlang/erts-6.0/include/
on my Mac.
Also I wanted to ask about concurrent processes manipulating a shared resource in NIF. Is that possible or there is a rule that NIFs have to be accessed in a single Erlang process.
You're running into problems because you're illegally accessing memory. In your BinaryStore
constructor you're attempting to save a binary from the argument list passed to binary_test:open/1
, but this doesn't work because those arguments are freed once the NIF call finishes with them. You need to save a copy of the argument to use it later. To do this, first add a new member to your BinaryStore
class:
ErlNifEnv* term_env;
Next, modify your constructor to allocate term_env
and then use it to copy the incoming term:
BinaryStore(ERL_NIF_TERM binary)
{
term_env = enif_alloc_env();
binary_term = enif_make_copy(term_env, binary);
}
This allocates binary_term
in the term_env
environment and then copies the incoming term into it. You'll also need a destructor to free term_env
:
~BinaryStore()
{
enif_free_env(term_env);
}
And finally, you need to pass term_env
instead of env
when inspecting binary_term
in the increment_binary
function:
nifpp::get_throws(term_env, binary_term, ibin);
With these modifications in place, I get the following results from running the code:
1> Ref=binary_test:open(<<1>>).
Reading symbols for shared libraries . done
<<>>
2> binary_test:increment(Ref,<<3>>).
1
3
1
1
<<4>>
(By the way, you should use "\r\n"
line endings rather than just "\n"
when printing from inside the Erlang emulator so that newlines always return to the leftmost column.)
You still have one remaining problem, which is that you leak the memory allocated for new_bin2
.
My advice for learning the details of NIFs is to avoid using packages like nifpp
at first, so you can learn the NIF API and all the details regarding memory ownership, resource allocation and deallocation, and argument conversions. Once you understand them, using packages like nifpp
becomes much easier and more fruitful.
ERL_NIF_TERM
s must be associated with an ErlNifEnv
, and the env that is passed to your nif functions is only valid for the duration of that function call. You are breaking this rule when you store a term into a BinaryStore
object and then later use it from another nif call. Your options:
Create a new ErlNifEnv for your binary store and copy terms from the nif call into this new env.
Use C++ data structures (such as std::vector<unsigned char>
) to store your binary data. I think this will be simpler for your situation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With