107 lines
3.7 KiB
Python
107 lines
3.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""发送互斥锁的边界行为测试。
|
|
|
|
这把锁是双引擎唯一的仲裁点:抢不到就跳过本轮发送。所以它的失败方式必须是
|
|
"这一轮没发",绝不能是抛异常打断轮询,更不能把锁吞掉再也不还。
|
|
"""
|
|
|
|
import threading
|
|
from unittest import TestCase, main
|
|
|
|
import send_lock
|
|
|
|
|
|
class FallbackLockTest(TestCase):
|
|
"""强制走非 Windows 回退分支(CI / macOS / Linux 开发机走的就是这条)。"""
|
|
|
|
def setUp(self):
|
|
self._saved = (
|
|
send_lock._init_done,
|
|
send_lock._handle,
|
|
send_lock._holder_tid,
|
|
)
|
|
send_lock._init_done = True
|
|
send_lock._handle = None
|
|
send_lock._holder_tid = None
|
|
while send_lock._FALLBACK_LOCK.locked():
|
|
try:
|
|
send_lock._FALLBACK_LOCK.release()
|
|
except RuntimeError:
|
|
break
|
|
|
|
def tearDown(self):
|
|
send_lock._holder_tid = None
|
|
while send_lock._FALLBACK_LOCK.locked():
|
|
try:
|
|
send_lock._FALLBACK_LOCK.release()
|
|
except RuntimeError:
|
|
break
|
|
send_lock._init_done, send_lock._handle, send_lock._holder_tid = self._saved
|
|
|
|
def test_default_zero_timeout_acquires_instead_of_raising(self):
|
|
# threading.Lock 不接受"非阻塞 + 超时"的组合;默认参数正好是这一种
|
|
self.assertTrue(send_lock.try_acquire("engine_a", 0))
|
|
send_lock.release()
|
|
|
|
def test_a_second_thread_is_turned_away_without_blocking(self):
|
|
self.assertTrue(send_lock.try_acquire("engine_a", 0))
|
|
result = {}
|
|
|
|
def other():
|
|
result["acquired"] = send_lock.try_acquire("engine_b", 0)
|
|
|
|
thread = threading.Thread(target=other)
|
|
thread.start()
|
|
thread.join(timeout=2.0)
|
|
self.assertFalse(thread.is_alive())
|
|
self.assertFalse(result["acquired"])
|
|
send_lock.release()
|
|
|
|
def test_release_makes_the_lock_available_again(self):
|
|
self.assertTrue(send_lock.try_acquire("engine_a", 0))
|
|
send_lock.release()
|
|
self.assertTrue(send_lock.try_acquire("engine_b", 0))
|
|
send_lock.release()
|
|
|
|
def test_same_thread_can_never_nest_a_send(self):
|
|
self.assertTrue(send_lock.try_acquire("engine_a", 0))
|
|
self.assertFalse(send_lock.try_acquire("engine_a", 0))
|
|
send_lock.release()
|
|
|
|
def test_positive_timeout_waits_and_still_succeeds(self):
|
|
self.assertTrue(send_lock.try_acquire("engine_a", 0.05))
|
|
send_lock.release()
|
|
|
|
def test_a_foreign_thread_cannot_release_someone_elses_lock(self):
|
|
self.assertTrue(send_lock.try_acquire("engine_a", 0))
|
|
|
|
def other():
|
|
send_lock.release()
|
|
|
|
thread = threading.Thread(target=other)
|
|
thread.start()
|
|
thread.join(timeout=2.0)
|
|
self.assertTrue(send_lock._FALLBACK_LOCK.locked())
|
|
send_lock.release()
|
|
|
|
def test_context_manager_releases_on_exception(self):
|
|
with self.assertRaises(RuntimeError):
|
|
with send_lock.lock("engine_a", 0) as acquired:
|
|
self.assertTrue(acquired)
|
|
raise RuntimeError("发送过程中崩了")
|
|
self.assertTrue(send_lock.try_acquire("engine_b", 0))
|
|
send_lock.release()
|
|
|
|
|
|
class WaitCodeTest(TestCase):
|
|
def test_abandoned_wait_is_treated_as_ownership(self):
|
|
# 上一个持有者崩溃退出时 Windows 返回 WAIT_ABANDONED:锁已经归本线程,
|
|
# 必须当成"抢到了",否则谁也不会去释放它。
|
|
self.assertEqual(send_lock._WAIT_ABANDONED, 0x80)
|
|
self.assertEqual(send_lock._WAIT_OBJECT_0, 0)
|
|
self.assertEqual(send_lock._WAIT_TIMEOUT, 0x102)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|