gpt4 book ai didi

cgo 与使用线程本地存储的 C 库交互

转载 作者:行者123 更新时间:2023-12-01 20:19:29 27 4
gpt4 key购买 nike

我正在用 cgo 包装一个 C 库,以供普通 Go 代码使用。

我的问题是我想将错误字符串向上传播到 Go API,但是有问题的 C 库通过线程本地存储提供错误字符串;有一个全局 get_error() 调用返回一个指向线程本地字符数据的指针。

我最初的计划是通过 cgo 调用 C,检查调用是否返回错误,如果是,则使用 C.GoString 包装错误字符串,将其从原始字符指针转换为一个 Go 字符串。它看起来像 C.GoString(C.get_error())

我在这里预见的问题是 C 中的 TLS 在 native 操作系统线程级别上工作,但据我所知,调用 Go 代码将来自潜在的 N 个 goroutine 之一,这些 goroutine 在一定数量的底层 native 线程之间进行多路复用由 Go 调度程序管理的线程池中的线程。

我害怕的是遇到这样一种情况,我调用了 C 例程,然后在 C 例程返回后,但在我复制错误字符串之前,Go 调度程序决定将当前 goroutine 换成另一个 goroutine .当原始 goroutine 被换回时,据我所知,它可能位于不同的 native 线程上,但即使它被换回同一个线程,在此期间运行在那里的任何 goroutine 都可能改变状态TLS,导致我为不相关的调用加载错误字符串。

我的问题是:

  • 这是一个合理的担忧吗?我是否误解了有关 go 调度程序的某些内容,或者它与 cgo 交互的方式,这会导致这不是问题?
  • 如果这是一个合理的担忧,我该如何解决?
    • cgo 以某种方式设法将 errno 值传播回调用 Go 代码,这些代码也存储在 TLS 中,这让我认为必须有一种安全的方法来执行此操作。
    • 我想不出 C 代码本身可以被 go 调度程序抢占的方法,所以我应该引入一个包装 C 函数并让 IT 进行必要的调用,然后在返回之前有条件地复制错误字符串去戈兰岛?

我对允许我将错误字符串传播到 Go 的其余部分的任何解决方案感兴趣,但我希望避免任何需要我围绕 TLS 序列化访问的解决方案,如添加锁只是抓取一个错误字符串对我来说似乎非常不幸。

提前致谢!

最佳答案

What I'm afraid of is running into a situation where I call into the C routine, then after the C routine returns, but before I copy the error string, the Go scheduler decides to swap the current goroutine out for another one. ...

Is this a reasonable concern?

是的。 cgo“调用 C 代码”包装器在每次调用期间锁定到一个 POSIX/OS 线程,但它们锁定的线程并非始终固定;事实上bop around ,就像随着时间推移到多个不同的线程一样,只要你的 goroutines 正常运行。 (由于 Go 在当前实现中是协同调度的,在某些情况下,您可以小心不要做任何可能让您切换底层操作系统线程的事情,但这可能不是一个好的计划。)

可以使用runtime.LockOSThread在这里,但我认为最好的计划是:

how can I work around it?

Go 恢复其正常调度算法之前(即,在从 C/POSIX 线程解锁 goroutine 之前)抓取错误。

cgo somehow manages to propagate errno values ...

它在 从 POSIX 线程解锁 goroutine 之前获取 errno 值。

My original plan was to call into C via cgo, check if the call returned an error, and if so, wrap the error string using C.GoString to convert it from a raw character pointer into a Go string. It'd look something like C.GoString(C.get_error()).

如果有一个接受错误 number 的变体(而不是从 TLS 变量中取出它),该计划应该仍然有效:只需确保您的 C 例程同时提供返回值和错误号。

如果没有,请按照您的建议编写您自己的 C 包装器:

ftype wrapper_for_realfunc(char **errp, arg1type arg1, arg2type arg2) {
ftype ret = realfunc(arg1, arg2);
if IS_ERROR(ret) {
*errp = get_error();
} else {
*errp = NULL;
}
return ret;
}

现在你的 Go 包装器简单地调用包装器,它用一个额外的 *C.char 参数填充一个指向 C 内存的指针,如果没有错误则将其设置为 nil,并将其设置为如果出现错误,您可以在其上使用 C.GoString

如果出于某种原因这不可行,请考虑使用 runtime.LockOSThread 及其对应项 runtime.UnlockOSThread .

关于cgo 与使用线程本地存储的 C 库交互,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61431530/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com