20
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ElixirAdvent Calendar 2022

Day 1

堅牢なNIFの書き方: パフォーマンスの高いフォールト・トレラント・システムのためのElixirとCの併用〜その3アサーションを積極的に書く

Last updated at Posted at 2022-11-18

この記事は「Elixir Advent Calendar 2022」2日目の記事です.また,この内容の講演がElixirConf EU 2023にacceptされました.

パフォーマンスを必要とするシステムを実装する場合,Elixir からよりパフォーマンスの高い C コードを呼び出すメカニズムである NIF を使用することがあります.NIF は,C を呼び出すもう 1 つのメカニズムである Port よりも優れたパフォーマンスを発揮します.ただし,NIF がクラッシュすると,Supervisor の制御下であっても,Erlang VM 全体が異常終了するという欠点があります.この欠陥は,フォールト・トレラント・システムを構築する際の大きな障害となります.

1つ目のポイントhttps://qiita.com/zacky1972/items/b1cbac9a4f31cd60800a
2つ目のポイントhttps://qiita.com/zacky1972/items/fa52c07532c8d4c704b0 に示しました.

この記事では,堅牢な NIF を説明する 3 つ目のポイントを示します.

  • 暗黙の前提条件を指定するアサーションを作成し, Supervisor が処理する例外を発生させます.

アサーション(表明)というのはご存知ですか? たとえばCだと次のようなプログラムがあったとします.

mysqrt.c
#include <math.h>
#include <assert.h>

double mysqrt(double a)
{
  assert(a >= 0);
  return sqrt(a);
}

引数aに負の値を与えると,次のように異常終了(abort)します.

Assertion failed: (a >= 0), function mysqrt, file mysqrt.c, line 6.
zsh: abort      ./a.out

アサーションを用いることで,意図しない入力を与えたり,意図しない出力結果が得られたりした時に,プログラムを強制停止することができます.このような入力や出力によって,破滅的な結果を招くことを予防することになります.

Cの場合にはアサーションによって異常終了するのみですが,Elixirの場合には Supervisor を用いることで,アサーションによって意図しない入出力を強制停止した後で,プロセスを再起動して再び入力を受け付けられるようにすることができます.Elixirにはこの Supervisor が存在することで,アサーションをより実用的に積極的に使うことができます.

NIFの場合には,assert.hを用いる代わりに,アサーションで表明する条件で分岐した後,条件に反する場合にはその2に書いた例外を発生させる方法で強制停止させます.たとえば次のようにします.

#include <math.h>
#include <erl_nif.h>

static ERL_NIF_TERM mysqrt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
  if(argc != 1) {
    return enif_make_badarg(env);
  }

  double a;
  if(!enif_get_double(env, argv[0], &a)) {
    return enif_make_badarg(env);
  }
  
  // assert(a >= 0);
  if(!(a >= 0)) {
    ERL_NIF_TERM reason = enif_make_string(env, "Assertion failed: (a >= 0), function mysqrt", ERL_NIF_LATIN1);
    return enif_raise_exception(env, reason);
  }
  
  return enif_make_double(env, sqrt(a));
}

static ErlNifFunc nif_funcs[] =
{
    {"mysqrt", 1, mysqrt}
};

ERL_NIF_INIT(Elixir.MySqrt, nif_funcs, NULL, NULL, NULL, NULL)

いかがだったでしょうか? 些細なことでも質問があれば,遠慮なくコメントしてください!

20
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?