edenapi: edenapi._spawnthread -> util.threaded

Summary:
Move the wrapper to util.py. It'll be used in dispatch.py to make the entire
command Ctrl+C friendly.

Reviewed By: singhsrb

Differential Revision: D23759715

fbshipit-source-id: fa2098362413dcfd0b68e05455aad543a6980907
This commit is contained in:
Jun Wu 2020-09-18 13:24:31 -07:00 committed by Facebook GitHub Bot
parent c4e2f5cb0f
commit 51a9d37730
2 changed files with 39 additions and 40 deletions

View File

@ -5,11 +5,9 @@
from __future__ import absolute_import
import threading
from bindings import edenapi
from . import error
from . import util
from .i18n import _
@ -27,46 +25,10 @@ class pyclient(object):
def __getattr__(self, name):
method = getattr(self._rustclient, name)
method = _warnexceptions(self._ui)(method)
method = _spawnthread(method)
method = util.threaded(method)
return method
def _spawnthread(func):
"""Decorator that spawns a new Python thread to run the wrapped function.
This is useful for FFI calls to allow the Python interpreter to handle
signals during the FFI call. For example, without this it would not be
possible to interrupt the process with Ctrl-C during a long-running FFI
call.
"""
def wrapped(*args, **kwargs):
result = ["err", error.Abort(_("thread aborted unexpectedly"))]
def target(*args, **kwargs):
try:
result[:] = ["ok", func(*args, **kwargs)]
except Exception as e:
result[:] = ["err", e]
thread = threading.Thread(target=target, args=args, kwargs=kwargs)
thread.start()
# XXX: Need to repeatedly poll the thread because blocking
# indefinitely on join() would prevent the interpreter from
# handling signals.
while thread.is_alive():
thread.join(1)
variant, value = result
if variant == "err":
raise value
return value
return wrapped
def _warnexceptions(ui):
"""Decorator that catches certain exceptions defined by the Rust bindings
and emits a user-friendly message before re-raising the exception.

View File

@ -47,6 +47,7 @@ import subprocess
import sys
import tempfile
import textwrap
import threading
import time
import traceback
import types
@ -4300,6 +4301,42 @@ class traced(object):
tracer.exit(self.spanid)
def threaded(func):
"""Decorator that spawns a new Python thread to run the wrapped function.
This is useful for FFI calls to allow the Python interpreter to handle
signals during the FFI call. For example, without this it would not be
possible to interrupt the process with Ctrl-C during a long-running FFI
call.
"""
def wrapped(*args, **kwargs):
result = ["err", error.Abort(_("thread aborted unexpectedly"))]
def target(*args, **kwargs):
try:
result[:] = ["ok", func(*args, **kwargs)]
except Exception as e:
result[:] = ["err", e]
thread = threading.Thread(target=target, args=args, kwargs=kwargs)
thread.start()
# XXX: Need to repeatedly poll the thread because blocking
# indefinitely on join() would prevent the interpreter from
# handling signals.
while thread.is_alive():
thread.join(1)
variant, value = result
if variant == "err":
raise value
return value
return wrapped
def info(name, **kwargs):
"""Log a instant event in tracing data"""
tracer.event(