Nativecall Buf生命周期和垃圾收集器

Cur*_*mes 6 perl6

Buf想传递给C库中有一个内存块,但是该库将在单个调用的生命周期之外使用内存。

我知道这是有问题的,因为垃圾收集器可以移动内存。

为了传递a StrNativecall文档 说:“如果C函数要求字符串的生存期超过函数调用的长度,则必须对参数进行手动编码并以CArray [uint8]的形式传递”,并举例说明:

my $array = CArray[uint8].new($string.encode.list);
Run Code Online (Sandbox Code Playgroud)

我的问题是:我是否必须为同一件事做同样的事情Buf?万一它被GC移动了?还是GC会离开我的Buf位置?对于短字符串,这没什么大不了的,但是对于大内存缓冲区,这可能是昂贵的操作。(例如,请参见Archive :: Libarchive,可以将其Buf与tar文件一起传递。该代码有问题吗?

multi method open(Buf $data!) {
    my $res = archive_read_open_memory $!archive, $data, $data.bytes; 
    ...
Run Code Online (Sandbox Code Playgroud)

是否有(可能有呢?应该有?)某种traitBuf,它告诉GC不能移动它?我知道如果我向中添加更多数据可能会很麻烦Buf,但我保证不会这样做。对于一个Blob不变的东西呢?

Jon*_*ton 6

至少在目前,您将在MoarVM上避免使用此方法,只要您在本机代码中需要对Blobor或Bufalive 的引用一直保持下去,并且(在情况下Buf)您不需要对其进行写操作可能会导致调整大小。

MoarVM 在苗圃中分配Blob/ Buf对象,并将在GC运行期间将其移动。但是,该对象不保存数据。而是保留大小和指向存储值的内存块的指针。该内存块未使用GC分配,因此不会移动。

+------------------------+
| GC-managed Blob object |
+------------------------+      +------------------------+
| Elements               |----->| Non-GC-managed memory  |
+------------------------+      | (this bit is passed to |
| Size                   |      | native code)           |
+------------------------+      +------------------------+
Run Code Online (Sandbox Code Playgroud)

是否应该依靠这个是一个棘手的问题。一些注意事项:

  • 据我所知,如果在JVM上运行,事情可能会变得不太顺利。我不了解JavaScript后端。您可以合理地决定,由于采用率的高低,您现在只需要担心在MoarVM上运行。
  • 如果您只需要自己的代码中的速度,则取决于MoarVM的实现细节是可以的,但是如果您希望在广泛使用的模块上工作,则可能需要考虑是否值得。Rakudo和MoarVM团队进行了大量工作,以不退回模块生态系统中的工作代码,即使在可以很好地证明其依赖于错误或未定义行为的情况下也是如此。但是,这可能会阻止改进。或者,有时认为破损是值得的。无论哪种方式,都非常耗时,并且要依靠一组志愿者。当然,当模块作者反应灵敏并可以应用提供的补丁时,问题就不那么严重了。

“为其添加特征”的问题在于,至少在JVM上,该决定似乎需要在分配保存数据的内存时预先做出。在这种情况下,便携式解决方案可能无法将现有的Buf/ Blob标记为此类。也许一种更好的方法是要求I / O类的东西提供类似的东西CArray,以便通过首先将数据放在“正确的内存”中来实现零复制。这可能是一个合理的功能要求。