pelemay icon indicating copy to clipboard operation
pelemay copied to clipboard

Fallback with warning when the NIF cannot be loaded

Open zacky1972 opened this issue 3 years ago • 4 comments

Current Pelemay will fail to execute user code in defpelemay when the NIF generated by Pelemay cannot be loaded for some reasons.

So, Pelemay is expected to add a fallback feature to execute the user code by Elixir instead of native code in that case.

zacky1972 avatar Dec 12 '20 02:12 zacky1972

I believe that will be useful to support mix upload.hotswap.

zacky1972 avatar Dec 12 '20 02:12 zacky1972

In case that NIFs exist but mix upload.hotswap is newer than them, Pelemay should give up to load NIFs and fallback to execute the user code by Elixir.

zacky1972 avatar Dec 12 '20 03:12 zacky1972

This issue is related to #156

zacky1972 avatar Dec 13 '20 02:12 zacky1972

Sorry, in Japanese.(I explained at Nerves JP Slack)

Pelemayの仕組みを説明すると,次のような感じです。

  1. defpelemay do … end の区間のASTをPelemayマクロが受け取る
  2. 関数ごとに解析してCコードを Application.app_dir(:pelemay, "src")に生成する
  3. Makefileを Applicatoin.app_dir(:pelemay, "build") に生成してビルドする。生成したNIFライブラリは Application.app_dir(:pelemay, "priv")に生成される
  4. スタブElixirコードを Application.app_dir(:pelemay, "priv")に生成する。このコードをロードすると NIFライブラリが読み込まれる。
  5. 元の関数をスタブElixirコードを呼び出すように書き換える

mix firmware && mix upload は,lib配下にあるモジュールに加えて,Application.app_dir(*, "priv") もターゲットに転送します。 これに対し,mix upload.hotswaplib 配下にあるモジュールをホットスワップしますが, Application.app_dir(*, "priv") をターゲットに転送することはしません。

したがって,Pelemayを組み込んだプロジェクトを mix upload.hotswap すると,上記の5だけが見えて,Application.app_dir(:pelemay, "priv") に配置したはずのNIFライブラリとスタブElixirコードが見えないので,スタブElixirコードのモジュールが存在しないというエラーになります。

NIFライブラリを転送することができないのはどうしようもないのですが,スタブElixirコードについては,ファイルを配置してロードするというアプローチをやめて,マクロで実現すれば,現状のmix upload.hotswapの仕組みで実現できます。

あとは,スタブElixirコードがNIFライブラリが存在しない,もしくは古いことを検出した場合に,NIFライブラリを読み込んで実行することを諦めて,元のElixirコードをそのまま実行するようにすれば良いというわけです。

実運用を考えた時,

  1. チョコチョコ修正しては,mix upload.hotswapで動作確認をして,というのをある程度繰り返し,
  2. コミットする前に mix firmware && mix uploadして動作確認をしてファームイメージを更新する,

というような感じになると思います。

このような運用の時に,Pelemayは,1のときには Elixir での実行で動作し,2 のときにはネイティブコードで動作するという感じにできれば良いかなと思います。

いったん2をすると,Application.app_dir(:pelemay, "priv")にNIFが転送されるので,そのあとで1をすると,NIFをロードした時に古いNIFを読み込んでしまうと不一致になってしまうと思います。

そこで,バージョンに1対1対応した番号を,スタブElixirコードと Application.app_dir(:pelemay, "priv") に埋め込んでおき,スタブコードのロード時にこの番号が一致した場合はNIFをロードし,一致しなかったりロードに失敗したら元のElixirコードを実行する,というようにすれば良いかなと思いました。

zacky1972 avatar Dec 13 '20 02:12 zacky1972