Changeset View
Changeset View
Standalone View
Standalone View
swh/loader/mercurial/hgutil.py
# Copyright (C) 2020-2021 The Software Heritage developers | # Copyright (C) 2020-2021 The Software Heritage developers | ||||
# See the AUTHORS file at the top-level directory of this distribution | # See the AUTHORS file at the top-level directory of this distribution | ||||
# License: GNU General Public License version 3, or any later version | # License: GNU General Public License version 3, or any later version | ||||
# See top-level LICENSE file for more information | # See top-level LICENSE file for more information | ||||
import io | import io | ||||
from multiprocessing import Process, Queue | import os | ||||
import signal | |||||
import time | |||||
import traceback | import traceback | ||||
from typing import Dict, NewType | from typing import Dict, NewType | ||||
from billiard import Process, Queue | |||||
# The internal Mercurial API is not guaranteed to be stable. | # The internal Mercurial API is not guaranteed to be stable. | ||||
from mercurial import context, error, hg, smartset, util # type: ignore | from mercurial import context, error, hg, smartset, util # type: ignore | ||||
import mercurial.ui # type: ignore | import mercurial.ui # type: ignore | ||||
NULLID = mercurial.node.nullid | NULLID = mercurial.node.nullid | ||||
HgNodeId = NewType("HgNodeId", bytes) | HgNodeId = NewType("HgNodeId", bytes) | ||||
Repository = hg.localrepo | Repository = hg.localrepo | ||||
BaseContext = context.basectx | BaseContext = context.basectx | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | try: | ||||
) | ) | ||||
except Exception as e: | except Exception as e: | ||||
exc_buffer = io.StringIO() | exc_buffer = io.StringIO() | ||||
traceback.print_exc(file=exc_buffer) | traceback.print_exc(file=exc_buffer) | ||||
errors.put_nowait(exc_buffer.getvalue()) | errors.put_nowait(exc_buffer.getvalue()) | ||||
raise e | raise e | ||||
def clone(src: str, dest: str, timeout: int) -> None: | def clone(src: str, dest: str, timeout: float) -> None: | ||||
"""Clone a repository with timeout. | """Clone a repository with timeout. | ||||
Args: | Args: | ||||
src: clone source | src: clone source | ||||
dest: clone destination | dest: clone destination | ||||
timeout: timeout in seconds | timeout: timeout in seconds | ||||
""" | """ | ||||
errors: Queue = Queue() | errors: Queue = Queue() | ||||
process = Process(target=_clone_task, args=(src, dest, errors)) | process = Process(target=_clone_task, args=(src, dest, errors)) | ||||
process.start() | process.start() | ||||
process.join(timeout) | process.join(timeout) | ||||
if process.is_alive(): | if process.is_alive(): | ||||
process.terminate() | process.terminate() | ||||
process.join(1) | # Give it a second (literally), then kill it | ||||
if process.is_alive(): | # Can't use `process.join(1)` here, billiard appears to be bugged | ||||
process.kill() | # https://github.com/celery/billiard/issues/270 | ||||
raise CloneTimeout(src, timeout) | killed = False | ||||
for _ in range(10): | |||||
time.sleep(0.1) | |||||
if not process.is_alive(): | |||||
break | |||||
else: | |||||
killed = True | |||||
os.kill(process.pid, signal.SIGKILL) | |||||
raise CloneTimeout(src, timeout, killed) | |||||
vlorentz: What about this, to save some time `sleep`ing? | |||||
if not errors.empty(): | if not errors.empty(): | ||||
raise CloneFailure(src, dest, errors.get()) | raise CloneFailure(src, dest, errors.get()) |
What about this, to save some time sleeping?