ruby - Data_Wrap_Struct 和销毁顺序

标签 ruby garbage-collection ruby-c-extension

我正在为物理引擎编写一个 Ruby 扩展。此物理引擎具有链接到世界body,因此我的Ruby 对象是WorldBody。主体是使用 world->CreateBody 构建(在 C++ 中)并使用 world->DestroyBody 销毁。

问题在于 Ruby GC 会先于 body 破坏世界。所以,当 GC 销毁物体时,世界不再存在,我得到了一个段错误。我知道我需要在某处为 GC 标记一些东西(使用 rb_gc_mark),但我不知道在哪里。


World 类非常标准,如下所示:

extern "C" void world_free(void *w)
{
    static_cast<World*>(w)->~World();
    ruby_xfree(w);
}

extern "C" void world_mark(void *w)
{
    // ???
}

extern "C" VALUE world_alloc(VALUE klass)
{
    return Data_Wrap_Struct(klass, world_mark, world_free, ruby_xmalloc(sizeof(World)));
}

extern "C" VALUE world_initialize(VALUE self)
{
    World* w;
    Data_Get_Struct(self, World, w);
    new (w) World();
    return self;
}

Body 类有点不同,因为它需要从 World 对象创建(我不能简单地 new 它)。所以它看起来像这样:

extern "C" void body_free(void* b)
{
    Body* body = static_cast<Body*>(b);
    World* world = body->GetWorld();
    world->DestroyBody(body);
}

extern "C" void body_mark(void* b)
{
    // ???
}

extern "C" VALUE body_alloc(VALUE klass)
{
    return Data_Wrap_Struct(klass, body_mark, body_free, 0);
}

extern "C" VALUE static_obj_initialize(VALUE self, VALUE world)
{
    Body* b;
    World* w;

    Data_Get_Struct(self, Body, b);
    Data_Get_Struct(world, World, w);

    b = w->CreateBody();
    DATA_PTR(self) = b;

    return self;
}

所以我的问题是:

  1. 我应该在 GC 上标记这两个对象中的哪一个?
  2. 我该怎么做?我是简单地用 rb_gc_mark 做标记,还是应该只在某些条件下做标记?哪些?
  3. 我应该标记什么?标记函数仅接收指向我的结构的裸指针,但 rb_gc_mark 函数需要一个 VALUE

最佳答案

您应该能够注册 VALUEWorld as busy 与 Body所有 实例的生命周期相关与之相关。

这意味着当它们被创建时,你会想要做这样的事情:

extern "C" VALUE static_obj_initialize(VALUE self, VALUE world)
{
    Body* b;
    World* w;

    Data_Get_Struct(self, Body, b);
    Data_Get_Struct(world, World, w);

    b = w->CreateBody();
    DATA_PTR(self) = b;
    rb_gc_register_address(&world);

    return self;
}

您希望在 Body 时注销被销毁:

extern "C" void body_free(void* b)
{
    Body* body = static_cast<Body*>(b);
    World* world = body->GetWorld();
    world->DestroyBody(body);
    rb_gc_unregister_address(&world); //almost right
}

然而,这并不十分准确,因为 rb_gc_unregister_address()想要一个 VALUE * .您应该能够编写一个简单的包装器(或使用 std::pair<>)来承载世界 VALUE .

警告:我没有测试过以上任何代码,但它的方向应该是正确的。

关于ruby - Data_Wrap_Struct 和销毁顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24932263/

相关文章:

ruby-on-rails - 如何通过 Ruby 中的 Instagram API 获取用户名和/或 ID?

c - Ruby C API 字符串和符号是否相等?

c - Ruby C 扩展,如何从段错误中恢复

javascript - 是否有必要取消垃圾收集的原始值?

Java GC CMS 收集器时间

在 Ruby 中创建 C 扩展

ruby-on-rails - RadRails 试用期已过 - Eclipse 中的 Rails 是否有免费选项?

Ruby - 关键字参数 - 你能把所有的关键字参数当作一个散列吗?如何?

ruby-on-rails - ruby open ssl api for encrypted key (without nodes option)

iphone - 这就是 Objective-C 中垃圾收集的全部内容吗?