Расширение ruby ​​​​c, как управлять сборкой мусора между двумя объектами

У меня есть расширение C, в котором у меня есть основной класс (например, класс A), созданный с помощью классического:

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

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

Если объект A собирается сборщиком мусора перед объектом B, это может привести к ошибке сегмента.

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

Изменить: дополнительная информация

Я пытаюсь написать расширение 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

Изменить 2: решение

Материал для создания объектов «b» со ссылкой на объект «a»:

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);
}

Функция c, которая используется для создания объекта "b" из объекта "a":

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/ext/mysql2/client.c ( rb_mysql_client_mark function) в проекте https://github.com/brianmario/mysql2


person cedlemo    schedule 23.05.2015    source источник
comment
Когда вы говорите генерировать, вы имеете в виду, что B объекты выглядят автономными для Ruby, но связаны внизу? Будет ли ваша объектная модель иметь смысл, если вы сможете сделать a_object.all_the_b_objects и/или b_object.parent_a_object . . . с этими отношениями в игре ответ мог бы немного отличаться от того, если бы это было не так.   -  person Neil Slater    schedule 23.05.2015
comment
Объект Б не может существовать без объекта А. Обернутые данные в объекте B зависят от обернутых данных в объекте A. Я хотел задать очень глобальный вопрос, но я добавил правку, чтобы проиллюстрировать этот вопрос тем, что я сейчас пытаюсь сделать.   -  person cedlemo    schedule 23.05.2015


Ответы (1)


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

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

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

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

person David Grayson    schedule 23.05.2015
comment
Возможно, вам придется каким-то образом изменить свой дизайн, чтобы указать указатель на объекты A. Извините, но я не понимаю, что вы имеете в виду. - person cedlemo; 23.05.2015
comment
Если я использую rb_gc_register_mark_object, это означает, что я должен отслеживать текущий экземпляр VALUE/ptr, поэтому вы, возможно, хотите сказать, что я должен предоставить указатель на объекты A? - person cedlemo; 23.05.2015
comment
Да, вы поняли. Указатель на объект Ruby A должен быть открыт для объектов B, чтобы они могли его пометить. - person David Grayson; 23.05.2015
comment
Должен ли я использовать rb_gc_register_mark_object в функции отметки с Data_Wrap_Struct или я могу использовать его после выделения моего объекта. Как и мой последний коммит здесь: github.com/cedlemo/ruby-clangc. я тесты пока не делал - person cedlemo; 23.05.2015
comment
Нет, вы делаете это неправильно в своем последнем коммите. Смысл сборщика мусора с пометкой и очисткой заключается в том, что каждый объект знает, от каких объектов он зависит, и сборщик мусора может в любое время попросить любой объект пометить объекты, от которых он зависит. Вам нужно использовать второй параметр для Data_Wrap_Struct. Найдите метку здесь: github.com/ruby/ruby/blob/trunk /doc/extension.rdoc - person David Grayson; 23.05.2015
comment
Насколько я могу сообщить, функция rb_gc_register_mark_object предотвратит сборку мусора сборщиком мусора. объект. - person David Grayson; 23.05.2015
comment
Да, мне это кажется хорошим. Но вы не должны доверять мне как авторитету в сборщике мусора Ruby, я просто указываю вам на нужную документацию. - person David Grayson; 24.05.2015
comment
Ну, вы знаете больше вещей, чем я, и любые советы хороши. Я думаю, что я попытаюсь подтвердить это, а затем напишу решение в редактировании. В любом случае спасибо - person cedlemo; 24.05.2015