2016-05-05 16 views
1

私は引数をrb_thread_call_without_gvlに渡すのに苦労しています。これは私が使用している簡単なコードです。Rubyグローバルインタープリタロック(GIL) - rb_thread_call_without_gvl

#include <stdio.h> 
#include <ruby.h> 
#include <ruby/thread.h> 

VALUE summa(VALUE self, VALUE x) 
{ 
    double result; 
    result = NUM2DBL(x) + NUM2DBL(x); 

    printf("The sum in C is %f\n", result); 
    return DBL2NUM(result); 
} 


VALUE no_gvl(VALUE self) 
{ 
    double arg = 3.0; 
    double *ptr = &arg; 
    rb_thread_call_without_gvl(summa, ptr, NULL, NULL); 
    return Qnil; 
} 

void Init_csum() 
{ 
    VALUE myModule = rb_define_module("MyModule"); 
    VALUE myClass = rb_define_class_under(myModule, "MyClass", rb_cObject); 
    rb_define_method(myClass, "summa", summa, 1); 
    rb_define_method(myClass, "no_gvl", no_gvl, 0); 
} 

私は、スクリプトclient.rbでルビーから拡張子を呼び出そう:私はCでの初心者ですが、私は必要

require 'mkmf' 
extension_name = 'csum' 
create_makefile(extension_name) 

require './csum' 
obj = MyModule::MyClass.new # MyClass is defined in C 
puts "The sum in R is " + obj.summa(7.0).to_s 
puts obj.no_gvl 

そして最後に、私のextconf.rb 1つのスレッドの制限なしにライブラリを使用できる拡張機能を作成します。私のother questionを参照してください。

私は、私はそれが言うことを理解しますが、私はそれを修正する方法を見ることができない

warning: incompatible pointer types passing 'VALUE (VALUE, VALUE)' to parameter of type 'void *(*)(void *)' 

を言って警告を受ける拡張をmake。私はそれを無視する必要がありますか? またclient.rbを実行すると、obj.no_gvlを呼び出すとセグメント化エラーが発生します。

私はMac OSX 10.10.5を使用しており、Ruby 2.0.0-p247からrbenvまで使用しています。

答えて

1

まだ見ていない場合はthe source for rb_thread_call_without_gvl includes some documentationです。 (使用しているバージョンにリンクしていますが、それはpretty old Rubyです。可能であれば、最新のバージョンでこのAPIは同じですが、少なくとも2.3.1までは同じです)

void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1, 
      rb_unblock_function_t *ubf, void *data2); 

あなたに持っていると呼ばれていることはRubyのメソッドを実装するC関数を単一void *引数を受け入れ、void *を返し、それはプレーンなCの関数である。すなわち、ないすべき機能:プロトタイプは次のようになります例。実際にはがRubyメソッドを実装できないため、GVLで保護されている構造体にアクセスすることになります。

これを使用するには、ロックしないで実行したいコードを、適切なインタフェースを持つ関数に移動し、Ruby APIを使用しないでください。ここではそれに渡される引数を倍増し、GVLせずに作業を行うRubyのメソッドを作成します(あなた自身に基づく)の例である:

#include <stdio.h> 
#include <ruby.h> 
#include <ruby/thread.h> 

// The function that does the work, accepts void* and returns void* 
void* doubleArg(void* x) { 
    // Unpack the arg and cast back to double. 
    double val = *(double*)x; 
    double result = val + val; 
    printf("The sum in C is %f\n", result); 

    // If you wanted you could wrap up some data here to return to 
    // Ruby land. 
    return NULL; 
} 

// The function implementing the Ruby method 
VALUE double_no_gvl(VALUE self, VALUE arg) { 
    // First wrap up the input as a pointer. 
    // You'll probably want to do some checking on the type of the 
    // argument here too. 
    double argAsDouble = NUM2DBL(arg); 
    double *ptr = &argAsDouble; 

    // Now call the function without the GVL. 
    // It might be worth looking into providing 
    // an ubf function here too. 
    rb_thread_call_without_gvl(doubleArg, ptr, NULL, NULL); 
    return Qnil; 
} 

void Init_csum() { 
    VALUE myModule = rb_define_module("MyModule"); 
    VALUE myClass = rb_define_class_under(myModule, "MyClass", rb_cObject); 
    rb_define_method(myClass, "double_no_gvl", double_no_gvl, 1); 
} 

あなたが好きなスクリプトでそれを呼び出すことができます。

require './csum' 
obj = MyModule::MyClass.new 
obj.double_no_gvl(3.9) 
+0

非常に明確な感謝。 Re:Rubyのバージョン。このバージョンは、プラグインを作成しているソフトウェア(SketchUp)に組み込まれているため、このバージョンを使用する必要があります。また、私は[this](http://stackoverflow.com/questions/36245878/releasing-the-global-vm-lock-in-ac-extension-without-using-another-function)の問題をよく理解していると思います。質問。私は今、結果をRubyに返そうとしています。私の理解が正しいなら、私は 'void *'を返し、 'double_no_gvl'の' double'にキャストする必要があります。 – Rojj

+1

@Rojjはい、割り当てには注意してください。ローカル変数へのポインタを呼び出し関数に返すべきではありません。ポインタがスタックのどこかを指し示す呼び出し関数のように(次回は関数を呼び出すときに上書きされるでしょう)。あなたがそれを解凍してもそれは働くかもしれませんが、危険です。代わりに、いくつかの記憶領域を 'malloc'する必要があります(呼び出し関数内で' free')するか、必要な引数を含む構造体へのポインタを渡し、結果を返すのではなく設定します結果)。 – matt

関連する問題