Значение бинарного изменения после того, как 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_TERMs должен быть связан с ErlNifEnvи env, который передается вашим функциям nif, действителен только на время вызова этой функции. Вы нарушаете это правило, когда сохраняете термин в BinaryStore объект, а затем использовать его из другого вызова NIF. Ваши варианты:

  1. Создайте новый ErlNifEnv для вашего бинарного хранилища и скопируйте термины из вызова nif в этот новый env.

  2. Используйте структуры данных C++ (такие как std::vector<unsigned char>) для хранения ваших двоичных данных. Я думаю, что это будет проще для вашей ситуации.

Другие вопросы по тегам