运行 来自同一个 `main` 的两个 warp 服务器安全吗?

Is it safe to run two warp servers from the same `main`?

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

看起来很安全。

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

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

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

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

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

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

下使用时 newUnique 的线程安全性

我从来没有听说过任何关于在多线程代码中使用全局变量技巧的警告,并且 unsafePerformIO 旨在实现线程安全(这就是为什么有一个单独的“更高效但可能线程不安全”版本 unsafeDupablePerformIO).

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

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

而且我看不出 运行 它在 unsafePerformIO 下如何使它成为线程不安全的。