2015-05-23 3 views
3

У меня есть расширение C, в котором у меня есть основной класс (класс А, например), созданные с классическим:рубиновые с расширения, как управлять сборкой мусора между 2 объектами

Data_Wrap_Struct 
rb_define_alloc_func 
rb_define_private_method(mymodule, "initialize" ...) 

В этом одном классе есть экземпляр метод, который генерирует объект B. Эти объекты B могут генерироваться только из объектов A и иметь C-данные, которые зависят от данных, завернутых в экземпляр A.

I Объект A собран сборщиком мусора перед объектом B, это может привести к ошибке Seg.

Как я могу сказать GC не собирать экземпляр A, пока некоторые из его объектов B все еще остаются. Думаю, мне нужно использовать rb_gc_mark или что-то в этом роде. Должен ли я отмечать экземпляр A каждый раз, когда создается объект B?

Edit: Больше специфика Информация

Я пытаюсь написать расширение Clang. С clang вы сначала создаете CXIndex, из которого вы можете получить CXTranslationUnit, из которого вы можете получить CXDiagnostic и/или CXCursor и т. Д. вот простой пример:

Clangc::Index#new => Clangc::Index 
Clangc::Index#create_translation_unit => Clangc::TranslationUnit 
Clangc::TranslationUnit#diagnostic(index) => Clangc::Diagnostic 

Вы можете увидеть код здесь: https://github.com/cedlemo/ruby-clangc

Edit 2: Раствор

материал для создания «B» объекты с ссылкой на «а» объект:

typedef struct B_t { 
    void * data; 
    VALUE instance_of_a; 
} B_t; 

static void 
c_b_struct_free(B_t *s) 
{ 
    if(s) 
    { 

    if(s->data) 
    a_function_to_free_the_data(s->data); 

    ruby_xfree(s); 
    } 
} 
static void 
c_b_mark(void *s) 
{ 
    B_t *b =(B_t *)s; 
    rb_gc_mark(b->an_instance_of_a); 
} 

VALUE 
c_b_struct_alloc(VALUE klass) 
{ 

    B_t * ptr; 
    ptr = (B_t *) ruby_xmalloc(sizeof(B_t)); 
    ptr->data = NULL; 
    ptr->an_instance_of_a = Qnil; 
    return Data_Wrap_Struct(klass, c_b_mark, c_b_struct_free, (void *) ptr); 
} 

функция с, которая используется для создания «B» объект из «а» объекта:

VALUE c_A_get_b_object(VALUE self, VALUE arg) 
{ 

    VALUE mModule = rb_const_get(rb_cObject, rb_intern("MainModule"));\ 
    VALUE cKlass = rb_const_get(mModule, rb_intern("B")); 

    VALUE b_instance = rb_class_new_instance(0, NULL, cKlass); 
    B_t *b; 
    Data_Get_Struct(b_instance, B_t, b); 
    /* 
    transform ruby value arg to C value c_arg 
    */ 
    b->data = function_to_fill_the_data(c_arg); 
    b->instance_of_a = self; 
    return b_instance; 
} 

В Init_mainModule функции:

void Init_mainModule(void) 
{ 
    VALUE mModule = rb_define_module("MainModule"); 
    /*some code ....*/ 
    VALUE cKlass = rb_define_class_under(mModule, "B", rb_cObject); 
    rb_define_alloc_func(cKlass, c_b_struct_alloc); 
} 

же использование в rb_gc_mark можно найти в mysql2/внутр/mysql2/client.c (rb_mysql_client_mark function) в проекте https://github.com/brianmario/mysql2

+0

Когда вы говорите «сгенерируете», вы имеете в виду, что объекты 'B' выглядят автономными с Ruby, но связаны между собой? Будет ли ваша объектная модель иметь смысл, если вы можете сделать 'a_object.all_the_b_objects' и/или' b_object.parent_a_object'. , , с этими отношениями в игре ответ может быть несколько иным, если бы они не были. –

+0

Объект B не может существовать без объекта A. Обтекаемые данные в объекте B зависят от обернутых данных в объекте A. Я хотел задать очень глобальный вопрос, но я добавил редактирование, чтобы проиллюстрировать этот вопрос тем, что я сейчас пытаюсь сделать. – cedlemo

ответ

1

В mark для вашего класса B, вы должны пометить объект A Ruby, сообщая сборщику мусора, чтобы он не собирал мусор.

Функция метки может быть указана как второй аргумент Data_Wrap_Struct. Возможно, вам придется каким-то образом изменить свой дизайн, чтобы выставить указатель на объекты A.

Другой вариант - позволить объекту A быть переменной экземпляра объекта B. Вероятно, вы должны сделать это так, чтобы код Ruby мог получить объект A из объекта B. Это будет иметь побочный эффект от того, чтобы сборщик мусора не собирал A до B, но вы не должны полагаться на этот побочный эффект, потому что ваш код Ruby может случайно испортить переменную экземпляра, а затем вызвать ошибка сегментации.

Редактировать: Другой вариант - использовать подсчет ссылок для общих данных C. Затем, когда последний объект Ruby, который использует эти общие данные, получает сбор мусора, вы удаляете общие данные. Это должно было бы найти хороший кросс-платформенный, поточно-безопасный способ сделать подсчет ссылок, чтобы он не был тривиальным.

+0

«Возможно, вам нужно каким-то образом изменить свой дизайн, чтобы выставить указатель на объекты A» Я сожалею, но я не понимаю, что вы имеете в виду. – cedlemo

+0

Если я использую rb_gc_register_mark_object, это значит, что мне нужно отслеживать текущий экземпляр VALUE/ptr, чтобы вы, возможно, захотели сказать, что мне нужно выставить указатель на «объекты A»? – cedlemo

+0

Да, вы поняли. Указатель на объект Ruby A должен быть подвергнут воздействию объектов B, чтобы они могли его пометить. –

Смежные вопросы