blob: 7366a4256a13159410c0f87761cbaaf7a3f32f5f [file] [edit]
# Test cases for librt.threading (compile and run)
[case testLockBasics_librt]
from typing import Any
from testutil import assertRaises
from librt.threading import Lock
class BadBool:
def __bool__(self) -> bool:
raise RuntimeError("bad bool")
class MutableLockHolder:
def __init__(self) -> None:
self.lock = Lock()
def test_lock_basic() -> None:
lock = Lock()
assert not lock.locked()
assert lock.acquire()
assert lock.locked()
lock.release()
assert not lock.locked()
def test_lock_context_manager() -> None:
lock = Lock()
with lock as acquired:
assert acquired is True
assert lock.locked()
assert not lock.locked()
def test_mutable_lock_attribute() -> None:
mutable_holder = MutableLockHolder()
assert not mutable_holder.lock.locked()
with mutable_holder.lock:
assert mutable_holder.lock.locked()
assert not mutable_holder.lock.locked()
dynamic_holder: Any = mutable_holder
replacement = Lock()
dynamic_holder.lock = replacement
assert mutable_holder.lock is replacement
with mutable_holder.lock:
assert replacement.locked()
with assertRaises(TypeError):
dynamic_holder.lock = object()
def test_lock_non_blocking() -> None:
lock = Lock()
assert lock.acquire()
assert not lock.acquire(False)
lock.release()
assert lock.acquire(False)
lock.release()
def test_contention() -> None:
import threading
lock = Lock()
counter = [0]
n_threads = 4
n_increments = 10000
def worker() -> None:
for _ in range(n_increments):
lock.acquire()
counter[0] += 1
lock.release()
threads = [threading.Thread(target=worker) for _ in range(n_threads)]
for t in threads:
t.start()
for t in threads:
t.join()
assert counter[0] == n_threads * n_increments
def test_cross_thread_release() -> None:
# threading.Lock is unowned: a lock acquired on one thread may be
# released from another, after which another thread can acquire it.
import threading
lock = Lock()
assert lock.acquire()
started = threading.Event()
released = threading.Event()
def releaser() -> None:
started.set()
# Hand the lock off from a different thread than the acquirer.
lock.release()
released.set()
t = threading.Thread(target=releaser)
t.start()
started.wait()
# This may block until releaser releases the lock on the other thread.
assert lock.acquire()
t.join()
assert released.is_set()
lock.release()
assert not lock.locked()
def test_context_manager_exception() -> None:
lock = Lock()
try:
with lock:
assert lock.locked()
raise ValueError("test")
except ValueError:
pass
assert not lock.locked()
def test_acquire_blocking_true() -> None:
lock = Lock()
assert lock.acquire(True)
assert lock.locked()
lock.release()
def test_lock_constructor_errors() -> None:
lock_type: Any = Lock
make_type: Any = type
with assertRaises(TypeError):
lock_type(1)
with assertRaises(TypeError):
lock_type(foo=1)
with assertRaises(TypeError):
make_type("LockSubclass", (lock_type,), {})
def test_lock_acquire_argument_errors() -> None:
lock: Any = Lock()
with assertRaises(TypeError):
lock.acquire(True, False)
with assertRaises(TypeError):
lock.acquire(foo=True)
def test_lock_acquire_blocking_truthiness() -> None:
lock: Any = Lock()
assert lock.acquire(blocking=True)
assert lock.locked()
assert not lock.acquire(blocking=False)
lock.release()
assert lock.acquire(None)
assert lock.locked()
assert not lock.acquire(None)
lock.release()
assert lock.acquire(1)
lock.release()
assert lock.acquire(0)
lock.release()
def test_lock_acquire_blocking_bool_error() -> None:
lock: Any = Lock()
with assertRaises(RuntimeError, "bad bool"):
lock.acquire(BadBool())
with assertRaises(RuntimeError, "bad bool"):
lock.acquire(blocking=BadBool())
def test_lock_exit_manual_call() -> None:
lock: Any = Lock()
lock.acquire()
assert lock.__exit__(None, None, None) is None
assert not lock.locked()
lock.acquire()
assert lock.__exit__() is None
assert not lock.locked()
def test_release_unlocked() -> None:
lock = Lock()
with assertRaises(RuntimeError):
lock.release()
# Also after acquire + release
lock.acquire()
lock.release()
with assertRaises(RuntimeError):
lock.release()