LoginSignup
6

More than 5 years have passed since last update.

mrubyで時間のかかる処理を中断させる

Last updated at Posted at 2013-12-10

GUIアプリケーション等にmrubyを組み込む時、スクリプトの実行時間に制限を掛けたい場合が少なからずあると思います。スクリプトが走っているスレッドを外部から強制終了する...わけにもいきませんので、インタプリタ側から処理を中断できるようにする必要があります。

簡易的なものですが、これを書いている時点でのmrubyのHEADは fa4ae7dd4d では、mruby本体には手を加えなくてもデバッグ用のVMフックを利用することで、このような制限を実装できます。

準備

mrbconf.h を編集して、ENABLE_DEBUGのコメントアウトを外します。
この状態でmrubyをビルドすると、struct mrb_stateのメンバにcode_fetch_hook という関数ポインタが追加されます。

mrbconf.h
/* -DENABLE_XXXX to enable following features */
#define ENABLE_DEBUG        /* hooks for debugger */

サンプル

mrb->code_fetch_hookにセットした関数が、バイトコードが実行されるごとに呼び出されます。
適当にリミットを設け、vm_hookの呼び出しが指定した回数を超えたときに例外を投げて強制的に停止させます。

#include <stdio.h>
#include <mruby.h>
#include <mruby/compile.h>

int vm_step_count = 0;
int vm_step_limit = 0;

void 
vm_hook(struct mrb_state* mrb, struct mrb_irep *irep, mrb_code *pc, mrb_value *regs)
{
  if (vm_step_limit > 0)  {
    if (vm_step_count > vm_step_limit) {
      vm_step_count = vm_step_limit = 0;
      mrb_raise(mrb, E_RUNTIME_ERROR, "step limit exceeded");
    }
    vm_step_count++;
  }
}

mrb_value 
mrb_limited_load_string(mrb_state *mrb, const char *source, int limit)
{
  vm_step_count = 0;
  vm_step_limit = limit;
  return mrb_load_string(mrb, source);
}

int main(void)
{
  mrb_state *mrb = mrb_open();
  mrb->code_fetch_hook = vm_hook;
  mrb_limited_load_string(mrb, "loop do; end", 3000);
  mrb_close(mrb);
}

実際には、実時間での実行時間で制限したほうが使い勝手は良いと思います。
当然のことながら、C言語で実装されている関数内で時間がかかっている場合には対処できません。

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
What you can do with signing up
6