ZeroNet/src/Test/TestThreadPool.py

164 lines
4.6 KiB
Python

import time
import threading
import gevent
import pytest
from util import ThreadPool
class TestThreadPool:
def testExecutionOrder(self):
with ThreadPool.ThreadPool(4) as pool:
events = []
@pool.wrap
def blocker():
events.append("S")
out = 0
for i in range(10000000):
if i == 3000000:
events.append("M")
out += 1
events.append("D")
return out
threads = []
for i in range(3):
threads.append(gevent.spawn(blocker))
gevent.joinall(threads)
assert events == ["S"] * 3 + ["M"] * 3 + ["D"] * 3
res = blocker()
assert res == 10000000
def testLockBlockingSameThread(self):
lock = ThreadPool.Lock()
s = time.time()
def unlocker():
time.sleep(1)
lock.release()
gevent.spawn(unlocker)
lock.acquire(True)
lock.acquire(True, timeout=2)
unlock_taken = time.time() - s
assert 1.0 < unlock_taken < 1.5
def testLockBlockingDifferentThread(self):
lock = ThreadPool.Lock()
def locker():
lock.acquire(True)
time.sleep(0.5)
lock.release()
with ThreadPool.ThreadPool(10) as pool:
threads = [
pool.spawn(locker),
pool.spawn(locker),
gevent.spawn(locker),
pool.spawn(locker)
]
time.sleep(0.1)
s = time.time()
lock.acquire(True, 5.0)
unlock_taken = time.time() - s
assert 1.8 < unlock_taken < 2.2
gevent.joinall(threads)
def testMainLoopCallerThreadId(self):
main_thread_id = threading.current_thread().ident
with ThreadPool.ThreadPool(5) as pool:
def getThreadId(*args, **kwargs):
return threading.current_thread().ident
t = pool.spawn(getThreadId)
assert t.get() != main_thread_id
t = pool.spawn(lambda: ThreadPool.main_loop.call(getThreadId))
assert t.get() == main_thread_id
def testMainLoopCallerGeventSpawn(self):
main_thread_id = threading.current_thread().ident
with ThreadPool.ThreadPool(5) as pool:
def waiter():
time.sleep(1)
return threading.current_thread().ident
def geventSpawner():
event = ThreadPool.main_loop.call(gevent.spawn, waiter)
with pytest.raises(Exception) as greenlet_err:
event.get()
assert str(greenlet_err.value) == "cannot switch to a different thread"
waiter_thread_id = ThreadPool.main_loop.call(event.get)
return waiter_thread_id
s = time.time()
waiter_thread_id = pool.apply(geventSpawner)
assert main_thread_id == waiter_thread_id
time_taken = time.time() - s
assert 0.9 < time_taken < 1.2
def testEvent(self):
with ThreadPool.ThreadPool(5) as pool:
event = ThreadPool.Event()
def setter():
time.sleep(1)
event.set("done!")
def getter():
return event.get()
pool.spawn(setter)
t_gevent = gevent.spawn(getter)
t_pool = pool.spawn(getter)
s = time.time()
assert event.get() == "done!"
time_taken = time.time() - s
gevent.joinall([t_gevent, t_pool])
assert t_gevent.get() == "done!"
assert t_pool.get() == "done!"
assert 0.9 < time_taken < 1.2
with pytest.raises(Exception) as err:
event.set("another result")
assert "Event already has value" in str(err.value)
def testMemoryLeak(self):
import gc
thread_objs_before = [id(obj) for obj in gc.get_objects() if "threadpool" in str(type(obj))]
def worker():
time.sleep(0.1)
return "ok"
def poolTest():
with ThreadPool.ThreadPool(5) as pool:
for i in range(20):
pool.spawn(worker)
for i in range(5):
poolTest()
new_thread_objs = [obj for obj in gc.get_objects() if "threadpool" in str(type(obj)) and id(obj) not in thread_objs_before]
#print("New objs:", new_thread_objs, "run:", num_run)
# Make sure no threadpool object left behind
assert not new_thread_objs