Значение бинарного изменения после того, как NIF вызывает Erlang
Я намерен манипулировать двоичными файлами с помощью NIF для приложения, которое я планирую кодировать в Erlang. Гист-ссылки на файл cpp и файл erl для NIF приведены ниже.
[Erl Gist Link] https://gist.github.com/abhijitiitr/3a5bc97184d6dd32f97b
[C++ Gist Link] https://gist.github.com/abhijitiitr/24d2b780f2cdacebfb07
В основном я пытаюсь сделать простой тест. Совместное использование двоичных файлов по вызовам NIF и успешное управление ими с помощью последовательных вызовов NIF.
Если вы тестируете код в erlang REPL by
c(binary_test).
Ref=binary_test:open(<<1>>).
binary_test:increment(Ref,<<3>>).
В двоичных файлах хранятся изменения между вызовами NIF. Вывод REPL для третьей команды
1
3
60
60
<<"?">>
Я прошел <<1>>
во время фазы инициализации. Почему это изменилось на <<60>>
? Я не могу понять, что здесь происходит. Может кто-нибудь указать на ошибку?
Инструкции по компиляции C++
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/
на моем Mac.
Также я хотел спросить о параллельных процессах, управляющих общим ресурсом в NIF. Возможно ли это, или есть правило, что NIF должны быть доступны в одном процессе Erlang.
2 ответа
Вы сталкиваетесь с проблемами, потому что вы незаконно обращаетесь к памяти. В вашем BinaryStore
конструктор вы пытаетесь сохранить двоичный файл из списка аргументов, переданного в binary_test:open/1
, но это не работает, потому что эти аргументы освобождаются после завершения вызова NIF. Вам нужно сохранить копию аргумента, чтобы использовать его позже. Для этого сначала добавьте нового участника в свой BinaryStore
учебный класс:
ErlNifEnv* term_env;
Затем измените ваш конструктор, чтобы выделить term_env
а затем использовать его для копирования входящего термина:
BinaryStore(ERL_NIF_TERM binary)
{
term_env = enif_alloc_env();
binary_term = enif_make_copy(term_env, binary);
}
Это выделяет binary_term
в term_env
окружение, а затем копирует входящий термин в него. Вам также понадобится деструктор, чтобы освободить term_env
:
~BinaryStore()
{
enif_free_env(term_env);
}
И, наконец, вам нужно пройти term_env
вместо env
при осмотре binary_term
в increment_binary
функция:
nifpp::get_throws(term_env, binary_term, ibin);
С этими изменениями я получаю следующие результаты от запуска кода:
1> Ref=binary_test:open(<<1>>).
Reading symbols for shared libraries . done
<<>>
2> binary_test:increment(Ref,<<3>>).
1
3
1
1
<<4>>
(Кстати, вы должны использовать "\r\n"
окончания строк, а не просто "\n"
при печати из эмулятора Эрланга, чтобы переводы строк всегда возвращались в крайний левый столбец.)
У вас все еще есть одна проблема: утечка памяти, выделенной для new_bin2
,
Мой совет для изучения деталей NIF - избегать использования таких пакетов, как nifpp
сначала вы можете изучить API NIF и все детали, касающиеся владения памятью, распределения и освобождения ресурсов, а также преобразования аргументов. Как только вы поймете их, используйте такие пакеты, как nifpp
становится намного проще и плодотворнее.
ERL_NIF_TERM
s должен быть связан с ErlNifEnv
и env, который передается вашим функциям nif, действителен только на время вызова этой функции. Вы нарушаете это правило, когда сохраняете термин в BinaryStore
объект, а затем использовать его из другого вызова NIF. Ваши варианты:
Создайте новый ErlNifEnv для вашего бинарного хранилища и скопируйте термины из вызова nif в этот новый env.
Используйте структуры данных C++ (такие как
std::vector<unsigned char>
) для хранения ваших двоичных данных. Я думаю, что это будет проще для вашей ситуации.