单元测试预期和非预期死锁行为的明智策略

时间:2011-06-04 23:46:43

标签: unit-testing language-agnostic deadlock verification

我想知道如何测试一些可以阻止的对象,等待另一个参与者。要测试的具体单位是参与者之间的渠道,参与者自己是用于测试目的的模拟装置。

验证参与者在预期时会发生死锁会很好,但这对我来说并不是非常重要,因为之后发生的事情死锁可以合理地描述为未定义。

更重要的是验证参与者定义的交互是否死锁。

在任何一种情况下,我都不确定最佳测试策略应该是什么。我目前的想法是让测试运行器为每个参与者触发一个线程,休眠一段时间,然后发现子线程是否已经返回。在他们没有及时返回的情况下,假设他们已经死锁,并安全地终止线程,并且测试失败(或者如果预期死锁则成功)。

这感觉有点可能性,因为可能有各种各样的原因(但不太可能)线程可能需要比预期更长的时间来完成。是否还有其他好方法可以解决这个问题?

编辑:我确信测试中的合理性会很好,但我认为我不需要它。我正在考虑三个级别的测试确定性。

  • “实际行为已证明符合预期行为”无法发生死锁
  • “实际行为符合预期行为”N次测试中没有发生死锁
  • “实际行为符合预期行为”N测试在预期截止日期内完成

第一个当然是一个有价值的测试,但ShiDoiSi的回答说明了这一点的不切实际。第二个明显弱于第一个,但仍然很难;如何确定进程网络实际上已陷入僵局?我不确定这比第一次更容易证明(可能更难)

最后一个更像我的想法。

4 个答案:

答案 0 :(得分:5)

可靠地测试死锁的唯一方法是检测锁定子系统以检测并报告死锁。我最后一次必须这样做,我们构建了一个调试版本,它记录了哪些线程保持锁定并检查每次锁定获取调用的潜在死锁。它可以是一个具有大量锁定的系统中的重量级操作,但我们发现它非常有价值,我们重组了子系统,因此我们可以在运行时使用开关打开和关闭它,即使在生产版本中也是如此。 / p>

答案 1 :(得分:2)

学术界可能会告诉你(实际上它现在正在告诉你),你应该对一些所谓的模型检查框架(CSP, pi-calculus)进行忠实的抽象。然后,这将模拟抽象执行(通过所有可能的调度程序交错进行穷举搜索)。当然,诀窍是确保抽象实际上是忠实的。您不再检查程序的实际来源,而是检查其他语言的来源。

否则,我会想到一些像特定语言使用Java Path Finder/Explorer(它做一些非常相似的事情)的严厉方法。 C存在类似的研究原型,Intelother companies也在这个行业中使用专业工具。

您正在研究计算机科学研究中的一个热门话题,对于非平凡/真实系统,无论是详尽的测试还是形式验证都不能轻易应用于实际代码。

一种有价值的方法可能是检测您的代码,以便它实际上检测到死锁,并可能尝试恢复。为了检测死锁,FreeBSD kernel使用一组跟踪锁使用情况的C宏,并通过report potential violations机制跟踪witness(4)。但同样,只会很少发现很少发生的错误。

(免责声明:我没有参与上面链接的任何商业工具 - 我只是添加了它们,让您感觉到您遇到的问题的难度。)

答案 2 :(得分:0)

为了测试是否没有死锁,您可以使用相当于NUnit的TimeoutAttribute,如果执行时间超过上限,则会中止并失败测试。你可以带来一个良好的超时值,例如,如果测试没有在30秒内完成 - 有些事情是错误的。

我不确定(或者我没有遇到过这种情况)断言发生了死锁。死锁通常是不受欢迎的。我很难理解如何编写一个单元测试失败,除非测试块 - 单元测试通常被认为是快速且无阻塞的。

答案 3 :(得分:0)

既然你已经做了足够的抽象来模拟参与者,为什么不进一步抽象你的线程同步(互斥,信号量,诸如此类)?

当您考虑什么构成死锁时,您可以在测试中使用专门的,死锁感知的线程同步器。通过“死锁感知”,我并不是说它应该通过使用超时等来检测蛮力方式的死锁,而是通过标志,计数器等了解导致死锁的情况。它可以检测到死锁,同时可选地提供预期的线程同步功能。我基本上说的是,使用检测线程同步进行测试...

这太抽象,说起来容易做起来难。而且我并没有声称已成功完成它。我可能只是在这里傻。但也许如果你只能提供一个(不完整的)测试,那么这个问题就会受到更具体的攻击。<​​/ p>