当将括号与PTR作为资源使用时,可以将其替换为外国PTR吗?

发布于 2025-02-07 01:12:27 字数 971 浏览 1 评论 0原文

我的代码使用可以描述为指针的资源。为简单起见,我将在此处使用void指针。计算完成后必须关闭资源,因此control.exception.bracket函数是自然的选择,以确保如果发生错误,则代码不会泄漏:

run :: (Ptr () -> IO a) -> IO a
run action = bracket acquireResource closeResource action
-- no eta reduction for clarity

此模式的缺点是在Action完成后,资源将始终关闭。 Afaiu这意味着不可能执行诸如

cont <- run $ \ptr -> do
  a <- someAction ptr
  return (\x -> otherActionUsingResource ptr a x)
cont ()

资源之类的事情已经在执行时间cont之前。现在,我的方法是使用外ptr:

run' :: (ForeignPtr () -> IO a) -> IO a
run' action = do
  ptr <- acquireResource
  foreignPtr <- newForeignPtr closeResourceFunPtr ptr
  action foreignPtr

现在似乎这大致相当于第一个版本,较小的打字差异和资源结束了延迟。但是,我确实想知道这是真的,还是我错过了什么。某些错误条件可以导致这两个版本的不同结果吗?外国PTR可以安全使用吗?

My code uses a resource that can be described as a pointer; I'll use a void pointer here for simplicity. The resource must be closed after the computation with it finishes, so the Control.Exception.bracket function is a natural choice to make sure the code won't leak if an error occurs:

run :: (Ptr () -> IO a) -> IO a
run action = bracket acquireResource closeResource action
-- no eta reduction for clarity

The downside of this pattern is that the resource will always be closed after action completes. AFAIU this means that it isn't possible to do something like

cont <- run $ \ptr -> do
  a <- someAction ptr
  return (\x -> otherActionUsingResource ptr a x)
cont ()

The resource will already be close by the time cont is executed. Now my approach is to use a ForeignPtr instead:

run' :: (ForeignPtr () -> IO a) -> IO a
run' action = do
  ptr <- acquireResource
  foreignPtr <- newForeignPtr closeResourceFunPtr ptr
  action foreignPtr

Now it seems that this is roughly equivalent to the first version, minor typing differences and resource closing latency aside. However, I do wonder whether this is true, or if I miss something. Can some error conditions can lead to different outcomes with those two versions? Are ForeignPtr safe to use in this way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

尘世孤行 2025-02-14 01:12:27

如果您想这样做,我建议您避免使用运行',这使您看起来好像要关闭资源。做这样的事情。

acquire :: IO (ForeignPtr ())
acquire action = mask $ \unmask -> do
  ptr <- unmask acquireResource
  newForeignPtr closeResourceFunPtr ptr

正如卡尔(Carl)在评论中指出的那样,重要的是,在获取资源和安装最终化器以关闭它之间掩盖了例外。否则,可能会在之间提供异步异常,并导致资源泄漏。

这种挑战在于,您将其保留给用户和/或垃圾收集器,以确保资源释放。原始的ptr基于基于的代码使寿命显式。现在不是。许多人认为,明确的寿命更适合关键资源。 foreferptr为您提供了GC自动完成,这些人认为设计差。所以仔细考虑!这是您最终想释放的便宜资源(例如malloc ed内存)吗?还是您真的想确定的是昂贵的东西(例如文件描述符)?


旁注:ptr()foreferptr()不是很惯用。通常,类型的参数应该是代表所指向的任何内容的haskell类型。

If you want to do this, I'd recommend avoiding that run', which makes it look like you're going to close the resource. Do something like this instead.

acquire :: IO (ForeignPtr ())
acquire action = mask $ \unmask -> do
  ptr <- unmask acquireResource
  newForeignPtr closeResourceFunPtr ptr

As Carl pointed out in a comment, it's important that exceptions be masked between acquiring the resource and installing the finalizer to close it; otherwise an asynchronous exception could be delivered in between and cause a resource leak.

The challenge with anything of this sort is that you're leaving it up to the user and/or garbage collector to make sure the resource gets freed. The original Ptr-based code made the lifespan explicit. Now it's not. Many people believe that explicit lifespans are better for critical resources. What ForeignPtr gives you, automatic finalization by the GC, these people consider poor design. So think carefully! Is this a cheap resource (like a little bit of malloced memory) that you just want to free eventually? Or is it something expensive (like a file descriptor) that you really want to be sure about?


Side note: Ptr () and ForeignPtr () aren't very idiomatic. Usually the type argument should be a Haskell type representing whatever is being pointed to.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文