mrubyにおいて、mrb_stateを共有しているRubyコード間で、userdataを自由に読み書きできるmruby-userdataというmrbgemを作りました。
これは、mrubyの状態遷移情報を格納しているmrb_stateを共有している限り、あるタイミングでコードAが呼ばれて、次に別のタイミングでコードBが呼ばれた場合、コードAでuserdataオブジェクトを保存しておけば、コードBからそのオブジェクトを取り出せる仕組みです。
グローバル変数を使えばコード間でやり取りができますが、なんとなくグローバル変数を使うのは実装上バグを生みそうなので、C側にuserdataを保存しておくハッシュテーブルオブジェクトを作っておいて、そこにRuby側から保存したい時に保存しておき、取り出したい時に取り出すためのRubyメソッドによるインターフェイスを実装しました。
比較的安全にuserdataを保存しておけると思います。
userdataの操作例
例えば以下のようなコードAがあるとします。
u.hoge = {:test => 1}
このように、userdataにhogeというオブジェクト名で何かハッシュを保存しておきます。
そして、同じmrb_stateで実行される他のコードB上から、以下のようなコードを実行すると、
hash = u.hoge
hash[:foo] = 2
u.hoge = hash
p u.hoge # => {:test => 1, :foo =>2}
このように、別のコードで保存しておいたuserdataオブジェクトを取り出して、さらに上書きすることができます。
userdataキーを使う場合
さらに、Userdataインスタンス生成時に、引数にキーを渡す事で、userdataキーによって保存しておきたいuserdataを複数作る事ができます。
例えば、以下のようなmirb結果になります。
This is a very early version, please test and report errors.
Thanks :)
> u = Userdata.new
=> #<Userdata:0x7f96ca808ad0 mrb_userdata_key_store="mrb_userdata_default_key">
> u.hoge
=> nil
> u.hoge = 1
=> 1
> u.hoge
=> 1
> q = Userdata.new
=> #<Userdata:0x7f96ca808590 mrb_userdata_key_store="mrb_userdata_default_key">
> q.hoge
=> 1
> q.hoge = q.hoge + 1
=> 2
> u.hoge
=> 2
> s = Userdata.new "my_key"
=> #<Userdata:0x7f96ca807d80 mrb_userdata_key_store="my_key">
> s.hoge
=> nil
> s.hoge = 1
=> 1
> u.hoge
=> 2
> q.hoge
=> 2
何もuserdataキーの指定がない場合は、デフォルトのuserdataキー名を使用するので、uインスタンスもqインスタンスも同じuserdataを参照できています。
一方で、”my_key”という独自のuserdataキーを指定したsインスタンスは、uやqインスタンスとは違うuserdataを扱えている事がわかります。
最後に
このように、mrb_stateを共有している場合のメリットとして、userdataの受け渡しが可能なmrbgemを作りました。これによって、mod_mrubyやngx_mrubyにおいて、初期化時にredisに接続しておいて、そのredisコンテキストオブジェクトをuserdataに保存しておけば、次のフックのタイミングで、redisに再接続することなくredisコンテキストオブジェクトを取り出して、redisの操作をすることが可能になるでしょう。
これで色々捗りそうです。
0 Comments.