【问题标题】:Testing for correctness under asynchronous exceptions in Haskell在 Haskell 中测试异步异常下的正确性
【发布时间】:2016-06-10 10:31:03
【问题描述】:

我正在尝试测试异步异常下的正确行为。为了使事情具体化,请考虑以下示例,

casMVar :: Eq a => MVar a -> a -> a -> IO Bool
casMVar m old new = do
  cur <- takeMVar m
  if cur == old
  then putMVar m new >> return True
  else putMVar m old >> return False

其中不变量是m 不为空。我认为在异步异常下违反了这个不变量,因为这样的异常可能会到达if 表达式。然而,在我的机器上,它不会通过在分叉的casMVar 上抛出异常而暴露出来,并且延迟增加,就像这样,

values :: Maybe (List Bool)
values = action <$> killDelays
where
  killDelays = toDelays <$> [-100..100]
  toDelays :: Int -> (Int, Int)
  toDelays dt = if dt < 0 then (-dt, 0) else (0, dt)
  action :: (Int, Int) -> IO (Maybe Bool)
  action (s, t) = do
    m <- newMVar False
    threadId <- forkIO $ threadDelay s >> void (casMVar m False True)
    threadDelay t
    throwTo threadId ThreadKilled
    tryReadMVar m

虽然valuesJust False 的列表与Just True 的列表连接。

有什么方法可以揭露违规行为吗?或者至少增加在 some 机器上暴露违规行为的可能性?我并不是在寻找使这段代码正确的方法,问题纯粹是关于测试。

【问题讨论】:

  • 有趣。我会尝试在要测试的代码中添加一些yields,以便建议/强制线程切换。
  • yield 之前的 if 语句有时会暴露违规行为。
  • 您可以根据需要增加插入threadDelay的概率。
  • 这肯定有效。但我不想接触被测代码。
  • 您可能可以在&gt;&gt;= 中创建一个执行yield 的monad。但总的来说,异常安全性很难测试,正式推理要容易得多。在您的示例中,casMVar 的异常安全性取决于异步异常在执行时是否被屏蔽。

标签: haskell exception asynchronous concurrency


【解决方案1】:

我不知道在异步异常下测试任意代码的通用方法。对于您给出的特定示例,我将简单地将异常抛出值作为casMVar 的参数传递,并检查casMVar 评估后MVar 是否为非空:

boom = error "Handle me!"

pokemonHandler :: a -> SomeException -> IO a
pokemonHandler a e = return a

spec :: Spec
spec = do
  describe "casMVar" $ do
    it "puts back a value on the MVar under an exception" $ do
      m <- newMVar "foo"
      a <- async $
        casMVar m boom "bar" `catch` pokemonHandler True
      _ <- wait a
      val <- readMVar m
      val `shouldBe` "foo"

完整的代码可以在这里找到https://github.com/capitanbatata/sandbox/tree/master/testing-under-async-exceptions

【讨论】:

    猜你喜欢
    • 2018-12-31
    • 2017-08-05
    • 1970-01-01
    • 2013-03-16
    • 1970-01-01
    • 1970-01-01
    • 2015-06-02
    • 1970-01-01
    • 2014-07-12
    相关资源
    最近更新 更多