xref: /trunk/test/aoo-test/src/aootest/proc.py (revision 2cb6d6f119bbe2f78742f1c5c8538cb0d4e72ede)
1################################################################
2#
3#  Licensed to the Apache Software Foundation (ASF) under one
4#  or more contributor license agreements.  See the NOTICE file
5#  distributed with this work for additional information
6#  regarding copyright ownership.  The ASF licenses this file
7#  to you under the Apache License, Version 2.0 (the
8#  "License"); you may not use this file except in compliance
9#  with the License.  You may obtain a copy of the License at
10#
11#    http://www.apache.org/licenses/LICENSE-2.0
12#
13#  Unless required by applicable law or agreed to in writing,
14#  software distributed under the License is distributed on an
15#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16#  KIND, either express or implied.  See the License for the
17#  specific language governing permissions and limitations
18#  under the License.
19#
20################################################################
21
22"""Starting a process in its own session or process group, and killing it together with everything it started."""
23import os
24import signal
25import subprocess
26
27POSIX = os.name == "posix"
28
29
30def spawn(cmd, **kw):
31    """Popen in a new session (POSIX) or process group (Windows), so kill_tree() can reach the whole tree."""
32    if POSIX:
33        kw.setdefault("start_new_session", True)
34    else:
35        kw.setdefault("creationflags", subprocess.CREATE_NEW_PROCESS_GROUP)
36    return subprocess.Popen(cmd, **kw)
37
38
39def kill_tree(proc, wait=10):
40    """Kills `proc` (which must come from spawn()) and its descendants, then reaps it.
41
42    POSIX: SIGKILL to the process group, only while the leader is still alive (its pid cannot have been reused) and
43    never to our own group. Windows: taskkill /T /F. A leader that has already exited leaves its children alone."""
44    if proc.poll() is None:
45        if POSIX:
46            try:
47                if os.getpgid(proc.pid) != os.getpgrp():
48                    os.killpg(proc.pid, signal.SIGKILL)
49                else:
50                    proc.kill()
51            except ProcessLookupError:
52                pass
53        else:
54            subprocess.run(["taskkill", "/T", "/F", "/PID", str(proc.pid)], capture_output=True)
55            if proc.poll() is None:
56                proc.kill()
57    try:
58        proc.wait(timeout=wait)
59    except subprocess.TimeoutExpired:
60        pass
61