3

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

4

1 回答 1

5

它似乎是安全的。

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

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

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

取而代之的是,该vault包提供了一个类型安全、持久的“存储” a Vault,它的作用类似于各种类型的可变变量的集合,可通过唯一键访问。密钥在 中生成IO,有效地使用newUniquefrom Data.Unique。它Vault本身是一个纯粹的、安全的数据结构。它是使用不安全的操作实现的,但以使其安全的方式构造。最终,它是HashMapKey a(因此,类型注释Integer)到Any可以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会如何使其线程不安全。

于 2021-01-07T17:10:49.673 回答