从同一个`main`运行两个warp服务器是否安全?
s 代码库中似乎有一些“全局变量”(unsafePerformIO+ NOINLINE)warp。尽管如此,warp从同一个main函数运行两个实例是否安全?
回答
看来是安全的。
至少在 中warp-3.3.13,全局变量技巧用于(仅)为vault包生成密钥,使用如下代码:
pauseTimeoutKey :: Vault.Key (IO ())
pauseTimeoutKey = unsafePerformIO Vault.newKey
{-# NOINLINE pauseTimeoutKey #-}
注意,这是比“正常”的全局变量招不同,因为它不是建立一个全球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,它总是得到相同的键,并且该键在该线程的键中是唯一的。因此,它基本上归结为全局变量技巧的线程安全性以及newUnique在unsafePerformIO.
我从来没有听说过在多线程代码中使用全局变量技巧的任何警告,并且unsafePerformIO旨在是线程安全的(这就是为什么有一个单独的“更高效但可能线程不安全”的版本unsafeDupablePerformIO)。
newUnique 本身以线程安全的方式实现:
newUnique :: IO Unique
newUnique = do
r <- atomicModifyIORef' uniqSource $ x -> let z = x+1 in (z,z)
return (Unique r)
而且我看不出在下面运行它unsafePerformIO会如何使它成为线程不安全的。
- I'm not sure this is so obvious. What if two threads race to create the key, and the loser inserts something into the vault that it can therefore never get back out (because the key in question has been overwritten by the winner)? (I don't actually know that this would be a problem. Please read this as a curious question that I have and think should be addressed in a good answer rather than a challenge to the correctness of this answer.)