mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Remove sleep and switch to pipe for IPC (#28561)
Use a simple multiprocessing pipe to delay exiting the parent process until after the child has been doubly forked. Using a simple IPC to allow the forked process to start avoids the control node waiting unnecessarily for lightly loaded systems.
This commit is contained in:
parent
c1589c33c4
commit
4947936176
1 changed files with 32 additions and 1 deletions
|
@ -18,6 +18,7 @@ import traceback
|
|||
import signal
|
||||
import time
|
||||
import syslog
|
||||
import multiprocessing
|
||||
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
|
@ -26,6 +27,9 @@ PY3 = sys.version_info[0] == 3
|
|||
syslog.openlog('ansible-%s' % os.path.basename(__file__))
|
||||
syslog.syslog(syslog.LOG_NOTICE, 'Invoked with %s' % " ".join(sys.argv[1:]))
|
||||
|
||||
# pipe for communication between forked process and parent
|
||||
ipc_watcher, ipc_notifier = multiprocessing.Pipe()
|
||||
|
||||
|
||||
def notice(msg):
|
||||
syslog.syslog(syslog.LOG_NOTICE, msg)
|
||||
|
@ -129,6 +133,11 @@ def _run_module(wrapped_cmd, jid, job_path):
|
|||
jobfile = open(tmp_job_path, "w")
|
||||
result = {}
|
||||
|
||||
# signal grandchild process started and isolated from being terminated
|
||||
# by the connection being closed sending a signal to the job group
|
||||
ipc_notifier.send(True)
|
||||
ipc_notifier.close()
|
||||
|
||||
outdata = ''
|
||||
filtered_outdata = ''
|
||||
stderr = ''
|
||||
|
@ -140,6 +149,7 @@ def _run_module(wrapped_cmd, jid, job_path):
|
|||
if interpreter:
|
||||
cmd = interpreter + cmd
|
||||
script = subprocess.Popen(cmd, shell=False, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
(outdata, stderr) = script.communicate()
|
||||
if PY3:
|
||||
outdata = outdata.decode('utf-8', 'surrogateescape')
|
||||
|
@ -241,15 +251,32 @@ if __name__ == '__main__':
|
|||
# to initialize PRIOR to ansible trying to clean up the launch directory (and argsfile)
|
||||
# this probably could be done with some IPC later. Modules should always read
|
||||
# the argsfile at the very first start of their execution anyway
|
||||
|
||||
# close off notifier handle in grandparent, probably unnecessary as
|
||||
# this process doesn't hang around long enough
|
||||
ipc_notifier.close()
|
||||
|
||||
# allow waiting up to 2.5 seconds in total should be long enough for worst
|
||||
# loaded environment in practice.
|
||||
retries = 25
|
||||
while retries > 0:
|
||||
if ipc_watcher.poll(0.1):
|
||||
break
|
||||
else:
|
||||
retries = retries - 1
|
||||
continue
|
||||
|
||||
notice("Return async_wrapper task started.")
|
||||
print(json.dumps({"started": 1, "finished": 0, "ansible_job_id": jid, "results_file": job_path,
|
||||
"_ansible_suppress_tmpdir_delete": not preserve_tmp}))
|
||||
sys.stdout.flush()
|
||||
time.sleep(1)
|
||||
sys.exit(0)
|
||||
else:
|
||||
# The actual wrapper process
|
||||
|
||||
# close off the receiving end of the pipe from child process
|
||||
ipc_watcher.close()
|
||||
|
||||
# Daemonize, so we keep on running
|
||||
daemonize_self()
|
||||
|
||||
|
@ -258,6 +285,10 @@ if __name__ == '__main__':
|
|||
|
||||
sub_pid = os.fork()
|
||||
if sub_pid:
|
||||
# close off inherited pipe handles
|
||||
ipc_watcher.close()
|
||||
ipc_notifier.close()
|
||||
|
||||
# the parent stops the process after the time limit
|
||||
remaining = int(time_limit)
|
||||
|
||||
|
|
Loading…
Reference in a new issue