Browse Source

util: add AsyncHangDetector, and use it for lnpeer._process_message

master
SomberNight 2 years ago
parent
commit
af2c9b081c
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 6
      electrum/lnpeer.py
  2. 30
      electrum/util.py

6
electrum/lnpeer.py

@ -22,7 +22,7 @@ from . import ecc
from .ecc import ecdsa_sig64_from_r_and_s, ecdsa_der_sig_from_ecdsa_sig64, ECPubkey from .ecc import ecdsa_sig64_from_r_and_s, ecdsa_der_sig_from_ecdsa_sig64, ECPubkey
from . import constants from . import constants
from .util import (bfh, log_exceptions, ignore_exceptions, chunks, OldTaskGroup, from .util import (bfh, log_exceptions, ignore_exceptions, chunks, OldTaskGroup,
UnrelatedTransactionException, error_text_bytes_to_safe_str) UnrelatedTransactionException, error_text_bytes_to_safe_str, AsyncHangDetector)
from . import transaction from . import transaction
from .bitcoin import make_op_return, DummyAddress from .bitcoin import make_op_return, DummyAddress
from .transaction import PartialTxOutput, match_script_against_template, Sighash from .transaction import PartialTxOutput, match_script_against_template, Sighash
@ -240,6 +240,10 @@ class Peer(Logger):
# note: the message handler might be async or non-async. In either case, by default, # note: the message handler might be async or non-async. In either case, by default,
# we wait for it to complete before we return, i.e. before the next message is processed. # we wait for it to complete before we return, i.e. before the next message is processed.
if asyncio.iscoroutinefunction(f): if asyncio.iscoroutinefunction(f):
async with AsyncHangDetector(
message=f"message handler still running for {message_type.upper()}",
logger=self.logger,
):
await f(*args) await f(*args)
else: else:
f(*args) f(*args)

30
electrum/util.py

@ -22,6 +22,7 @@
# SOFTWARE. # SOFTWARE.
import binascii import binascii
import concurrent.futures import concurrent.futures
import logging
import os, sys, re, json import os, sys, re, json
from collections import defaultdict, OrderedDict from collections import defaultdict, OrderedDict
from typing import (NamedTuple, Union, TYPE_CHECKING, Tuple, Optional, Callable, Any, from typing import (NamedTuple, Union, TYPE_CHECKING, Tuple, Optional, Callable, Any,
@ -488,6 +489,35 @@ def profiler(func=None, *, min_threshold: Union[int, float, None] = None):
return do_profile return do_profile
class AsyncHangDetector:
"""Context manager that logs every `n` seconds if encapsulated context still has not exited."""
def __init__(
self,
*,
period_sec: int = 15,
message: str,
logger: logging.Logger = None,
):
self.period_sec = period_sec
self.message = message
self.logger = logger or _logger
async def _monitor(self):
# note: this assumes that the event loop itself is not blocked
t0 = time.monotonic()
while True:
await asyncio.sleep(self.period_sec)
t1 = time.monotonic()
self.logger.info(f"{self.message} (after {t1 - t0:.2f} sec)")
async def __aenter__(self):
self.mtask = asyncio.create_task(self._monitor())
async def __aexit__(self, exc_type, exc, tb):
self.mtask.cancel()
def android_ext_dir(): def android_ext_dir():
from android.storage import primary_external_storage_path from android.storage import primary_external_storage_path
return primary_external_storage_path() return primary_external_storage_path()

Loading…
Cancel
Save