-
Notifications
You must be signed in to change notification settings - Fork 246
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix race condition in AsyncResult.wait #492
Conversation
True. However, #463 removed the |
I reviewed your changes they weren't quite there. The rlock count was not zero when expected. I pushed a fix that I got to after reviewing your code to master. Lmk your thoughts. |
Thanks, I checked the commit and think that its not covering the issue entirely. The race is still on when a client sends an async request and wait for it later on. Also the core change in |
Could you add the example as a comment here or in #354 |
Run the following with two arguments. The first set to """
arguments: use_ssl wait
use_ssl == 'True' or != 'True'
wait == 'True' or != 'True'
"""
KEY = "./localhost.key"
CERT = "./localhost.pem"
VERIFY_CLIENT = (
False # False for master because arguments are incorrectly passed to .wrap_socket
)
import rpyc
import rpyc.utils.authenticators as ru_authenticators
import logging
import signal
import ssl
import subprocess
import sys
import time
PORT = 18812
logging.basicConfig(
level="DEBUG",
format="{asctime} | {levelname:8} | {message} ({name}, {threadName}, {process})",
style="{",
)
class Service(rpyc.Service):
pass
if len(sys.argv) == 3: # start server and client subprocess
server_process = subprocess.Popen(
[sys.executable, __file__, "False"] + sys.argv[1:]
)
logging.info("waiting for server to start")
time.sleep(1)
client_process = subprocess.Popen([sys.executable, __file__, "True"] + sys.argv[1:])
client_process.wait()
server_process.send_signal(signal.SIGINT)
server_process.wait()
else:
_, as_client, use_ssl, wait = sys.argv
as_client = as_client == "True"
use_ssl = use_ssl == "True"
wait = wait == "True"
if as_client:
connection = (
rpyc.ssl_connect(
"localhost",
PORT,
keyfile=KEY,
certfile=CERT,
ca_certs=CERT if VERIFY_CLIENT else None,
cert_reqs=ssl.CERT_REQUIRED if VERIFY_CLIENT else None,
)
if use_ssl
else rpyc.connect("localhost", PORT)
)
rpyc.BgServingThread(connection)
if wait:
logging.info("wait")
time.sleep(1)
logging.debug("get root")
connection.root
else:
authenticator = ru_authenticators.SSLAuthenticator(
keyfile=KEY,
certfile=CERT,
ca_certs=CERT if VERIFY_CLIENT else None,
cert_reqs=ssl.CERT_REQUIRED if VERIFY_CLIENT else None,
)
server = rpyc.ThreadedServer(
Service(), port=PORT, authenticator=authenticator if use_ssl else None
)
try:
server.start()
except KeyboardInterrupt:
pass |
The functional differences have been committed to the master branch. Since this PR does not address the underlying issue of RPyC not respecting threading context/frames in the remote address space, I am closing the PR. Feel free to making threading a first class concept in a new PR. Thanks for all your time and effort! |
Did you try the script I posted? It still waits indefinitely on I understand that my fix is lacking simplicity/readability and extending the lock is not without cost, but I still think there is no way around it (except changing the approach entirely like notEvil@46b49e3). |
…t significantly more likely that the sync request response goes to the correct thread, calling acquire/release here seems to cause a dead lock in some cases like that provided in #492; ultimately RPyC requires the design around threading and locks to be fleshed out... quite a number of users get confused w/ RPyC threading... RPyC offers a number of functions/classes that are temperamental w/ threading
…e fixing perilous corner cases like a thread never calling notify_all #492
@notEvil, I was able reproduce the issue with your script. Over the last eight commits, the issue would toggle which test case would pass and which would fail. Here are my findings over the last eight commits:
I greatly appreciate your time and energy! |
Great to hear that, and thank you for the effort! |
Fixes #354 (and maybe more)
AsyncResult.wait
data
is split to allow lock release as soon as possibleI can consistently reproduce issue 354 and provide an example if requested.