从同一个 `main` 运行两个 warp 服务器是否安全?

Geo*_*nov 3 haskell yesod haskell-warp

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

K. *_*uhr 5

看来是安全的。

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

pauseTimeoutKey :: Vault.Key (IO ())
pauseTimeoutKey = unsafePerformIO Vault.newKey
{-# NOINLINE pauseTimeoutKey #-}
Run Code Online (Sandbox Code Playgroud)

注意,这是比“正常”的全局变量招不同,因为它不是建立一个全球IORef多个线程可能会尝试使用,而每个期待为参考的唯一用户。

相反,该vault包提供了一个类型安全的、持久的“存储”, a Vault,它的作用类似于各种类型的可变变量的集合,可通过唯一键访问。密钥在 中生成IO,有效地使用newUniquefrom Data.Unique。本Vault本身就是一个纯净,安全的数据结构。它是使用不安全的操作实现的,但以使其安全的方式构建。最终,它是一个HashMapfrom Key a(so, a type-annotated Integer) to a Anyvalue that can be unsafeCoerced to the required type a,类型安全由附加到键的类型保证。在价值观Vault的“突变”在地图上插入新的价值观,创造更新的Vault,所以这里发生没有实际的突变。

由于Vaults 只是HashMap纯值的花哨的不可变s,因此即使两个服务器使用相同的键,也不存在两个服务器覆盖彼此保管库中的值的危险。

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

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

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

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

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

  • 我不确定这是否如此明显。如果两个线程竞相创建密钥,而失败者将一些东西插入到金库中,因此它永远无法取回(因为有问题的密钥已被获胜者覆盖),该怎么办?(我实际上不知道这会是一个问题。请将此视为我的一个好奇问题,并认为应该以一个好的答案来解决,而不是对该答案的正确性提出挑战。) (2认同)