【问题标题】:Is it safe to run two warp servers from the same `main`?从同一个`main`运行两个warp服务器是否安全?
【发布时间】:2021-04-13 08:40:54
【问题描述】:

warps 代码库中似乎有一些“全局变量”(unsafePerformIO + NOINLINE)。尽管如此,从同一个 main 函数运行两个 warp 实例是否安全?

【问题讨论】:

  • 呃。他们为什么这样做?所以非Haskell。
  • @DanielWagner,如果答案中描述的用途实际上是唯一的,那么它可能没问题......除了测试目的。

标签: haskell yesod haskell-warp


【解决方案1】:

看来是安全的。

至少在warp-3.3.13 中,全局变量技巧(仅)用于为vault 包生成密钥,使用如下代码:

pauseTimeoutKey :: Vault.Key (IO ())
pauseTimeoutKey = unsafePerformIO Vault.newKey
{-# NOINLINE pauseTimeoutKey #-}

请注意,这与“通常”的全局变量技巧不同,因为它不会创建多个线程可能会尝试使用的全局 IORef,而每个线程都希望成为唯一的用户参考文献。

相反,vault 包提供了一个类型安全的持久“存储”Vault,它的作用类似于各种类型的可变变量的集合,可通过唯一键访问。密钥在IO 中生成,有效地使用来自Data.UniquenewUniqueVault 本身就是一个纯粹的、安全的数据结构。它是使用不安全的操作实现的,但以使其安全的方式构造。最终,它是一个从Key a(因此,一个类型注释的Integer)到一个Any 值的HashMap,它可以是unsafeCoerced 到所需的类型a,类型安全由类型保证附在钥匙上。 Vault 中的值通过在映射中插入新值而“变异”,创建更新后的Vault,因此这里没有实际发生变异。

由于 Vaults 只是花哨的不可变 HashMaps 纯值,因此即使使用相同的密钥,两台服务器也不会有覆盖彼此保管库中的值的危险。

据我所知,确保安全所需的只是,当一个线程调用类似pauseTimeoutKey 的东西时,它总是获得相同的密钥,并且该密钥在该线程的密钥中是唯一的。因此,它基本上归结为全局变量技巧的线程安全性以及newUniqueunsafePerformIO 下使用时的线程安全性。

我从未听说过在多线程代码中使用全局变量技巧的任何注意事项,而 unsafePerformIO 旨在实现线程安全(这就是为什么有一个单独的“更高效但可能线程不安全”的原因版本unsafeDupablePerformIO)。

newUnique 本身以线程安全的方式实现:

newUnique :: IO Unique
newUnique = do
  r <- atomicModifyIORef' uniqSource $ \x -> let z = x+1 in (z,z)
  return (Unique r)

而且我看不出在unsafePerformIO 下运行它会使其线程不安全。

【讨论】:

  • 我不确定这是否如此明显。如果两个线程竞相创建密钥,而失败者将一些东西插入到保险库中,因此它永远无法退出(因为有问题的密钥已被获胜者覆盖)怎么办? (我实际上并不知道这会是一个问题。请将此视为一个奇怪的问题,我认为应该在一个好的答案中解决这个问题,而不是挑战这个答案的正确性。)
  • @DanielWagner,这个特定的场景不是问题,因为保险库实际上是不可变的纯结构,因此一个线程的插入对其他线程是不可见的。但是,导致pauseTimeoutKey 通过不同的调用返回不同的键的比赛同样糟糕。正如我现在解释的那样,我认为这不可能发生。无论如何,我的原始答案肯定是缺乏的,所以我已经大大扩展了它。
猜你喜欢
  • 1970-01-01
  • 2011-05-11
  • 1970-01-01
  • 1970-01-01
  • 2019-01-21
  • 1970-01-01
  • 1970-01-01
  • 2018-02-19
  • 1970-01-01
相关资源
最近更新 更多