Skip to content

Commit 95a25c3

Browse files
committed
gateway: Cache rinfo for main_thread_only
Fix a bug triggered by pytest-cov when it tries to call _rinfo after the gateway is already busy with a remote_exec call: pytest-dev/pytest-xdist#1070 (comment)
1 parent 4999a10 commit 95a25c3

File tree

2 files changed

+43
-0
lines changed

2 files changed

+43
-0
lines changed

src/execnet/gateway.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ def __init__(self, io: IO, spec: XSpec) -> None:
3131
super().__init__(io=io, id=spec.id, _startcount=1)
3232
self.spec = spec
3333
self._initreceive()
34+
if self.execmodel.backend == "main_thread_only":
35+
# This will cause a deadlock if there is already a running
36+
# remote_exec, so cache the result before there are any
37+
# other remote_exec calls.
38+
self._rinfo()
3439

3540
@property
3641
def remoteaddress(self) -> str:

testing/test_gateway.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,3 +645,41 @@ def test_main_thread_only_concurrent_remote_exec_deadlock(
645645
ch.close()
646646
gw.exit()
647647
gw.join()
648+
649+
650+
def test_concurrent_rinfo(execmodel: gateway_base.ExecModel) -> None:
651+
# Use a temporary Group to force the desired execmodel, since
652+
# groups used by fixtures tend to use the thread execmodel.
653+
group = execnet.Group(execmodel=execmodel.backend)
654+
gw = group.makegateway(f"execmodel={execmodel.backend}//popen")
655+
656+
# For main_thread_only _rinfo will deadlock unless the gateway
657+
# has cached the result earlier, but only if sleep_time
658+
# exceeds the 1 second grace period in the WorkerGateway
659+
# _local_schedulexec method. For other execmodels, a shorter
660+
# sleep_time is sufficient to test concurrent _rinfo.
661+
sleep_time = 1.5 if execmodel.backend == "main_thread_only" else 0.5
662+
try:
663+
ch = gw.remote_exec(
664+
"""
665+
import os, time
666+
time.sleep({sleep_time})
667+
channel.send(os.getpid())
668+
""".format(
669+
sleep_time=sleep_time
670+
)
671+
)
672+
if execmodel.backend == "main_thread_only":
673+
assert hasattr(gw, "_cache_rinfo")
674+
rinfo = gw._rinfo()
675+
try:
676+
res = ch.receive()
677+
finally:
678+
ch.close()
679+
ch.waitclose()
680+
681+
assert res == rinfo.pid
682+
finally:
683+
gw.exit()
684+
gw.join()
685+
group.terminate(0.5)

0 commit comments

Comments
 (0)