toxygen/toxygen/middleware/threads.py

233 lines
7.0 KiB
Python

# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
import sys
import threading
import queue
from PyQt5 import QtCore
from bootstrap.bootstrap import *
from bootstrap.bootstrap import download_nodes_list
import tests.support_testing as ts
from utils import util
if 'QtCore' in sys.modules:
def qt_sleep(fSec):
if fSec > .001:
QtCore.QThread.msleep(int(fSec*1000.0))
QtCore.QCoreApplication.processEvents()
sleep = qt_sleep
elif 'gevent' in sys.modules:
import gevent
sleep = gevent.sleep
else:
import time
sleep = time.sleep
import time
sleep = time.sleep
# LOG=util.log
global LOG
import logging
LOG = logging.getLogger('app.'+'threads')
# log = lambda x: LOG.info(x)
def LOG_ERROR(l): print('ERRORt: '+l)
def LOG_WARN(l): print('WARNt: '+l)
def LOG_INFO(l): print('INFOt: '+l)
def LOG_DEBUG(l): print('DEBUGt: '+l)
def LOG_TRACE(l): pass # print('TRACE+ '+l)
# -----------------------------------------------------------------------------------------------------------------
# Base threads
# -----------------------------------------------------------------------------------------------------------------
class BaseThread(threading.Thread):
def __init__(self, name=None, target=None):
self._stop_thread = False
if name:
super().__init__(name=name, target=target)
else:
super().__init__(target=target)
def stop_thread(self, timeout=-1):
self._stop_thread = True
if timeout < 0:
timeout = ts.iTHREAD_TIMEOUT
i = 0
while i < ts.iTHREAD_JOINS:
self.join(timeout)
if not self.is_alive(): break
i = i + 1
else:
LOG_WARN(f"BaseThread {self.name} BLOCKED")
class BaseQThread(QtCore.QThread):
def __init__(self, name=None):
# NO name=name
super().__init__()
self._stop_thread = False
self.name = str(id(self))
def stop_thread(self, timeout=-1):
self._stop_thread = True
if timeout < 0:
timeout = ts.iTHREAD_TIMEOUT
i = 0
while i < ts.iTHREAD_JOINS:
self.wait(timeout)
if not self.isRunning(): break
i = i + 1
sleep(ts.iTHREAD_TIMEOUT)
else:
LOG_WARN(f"BaseQThread {self.name} BLOCKED")
# -----------------------------------------------------------------------------------------------------------------
# Toxcore threads
# -----------------------------------------------------------------------------------------------------------------
class InitThread(BaseThread):
def __init__(self, tox, plugin_loader, settings, app, is_first_start):
super().__init__(name='InitThread')
self._tox = tox
self._plugin_loader = plugin_loader
self._settings = settings
self._app = app
self._is_first_start = is_first_start
def run(self):
LOG_DEBUG('InitThread run: ')
try:
if self._is_first_start:
if self._settings['download_nodes_list']:
LOG_INFO('downloading list of nodes')
download_nodes_list(self._settings, oArgs=self._app._args)
if False:
lNodes = ts.generate_nodes()
LOG_INFO(f"bootstrapping {len(lNodes)!s} nodes")
for data in lNodes:
if self._stop_thread:
return
self._tox.bootstrap(*data)
self._tox.add_tcp_relay(*data)
else:
LOG_INFO(f"calling test_net nodes")
threading.Timer(1.0,
self._app.test_net,
args=list(),
kwargs=dict(lElts=None, oThread=self, iMax=2)
).start()
if self._is_first_start:
LOG_INFO('starting plugins')
self._plugin_loader.load()
except Exception as e:
LOG_DEBUG(f"InitThread run: ERROR {e}")
pass
for _ in range(ts.iTHREAD_JOINS):
if self._stop_thread:
return
sleep(ts.iTHREAD_SLEEP)
return
class ToxIterateThread(BaseQThread):
def __init__(self, tox):
super().__init__()
self._tox = tox
def run(self):
LOG_DEBUG('ToxIterateThread run: ')
while not self._stop_thread:
try:
iMsec = self._tox.iteration_interval()
self._tox.iterate()
except Exception as e:
# Fatal Python error: Segmentation fault
LOG_ERROR('ToxIterateThread run: {e}')
sleep(iMsec / 1000)
class ToxAVIterateThread(BaseQThread):
def __init__(self, toxav):
super().__init__()
self._toxav = toxav
def run(self):
LOG_DEBUG('ToxAVIterateThread run: ')
while not self._stop_thread:
self._toxav.iterate()
sleep(self._toxav.iteration_interval() / 1000)
# -----------------------------------------------------------------------------------------------------------------
# File transfers thread
# -----------------------------------------------------------------------------------------------------------------
class FileTransfersThread(BaseQThread):
def __init__(self):
super().__init__('FileTransfers')
self._queue = queue.Queue()
self._timeout = 0.01
def execute(self, func, *args, **kwargs):
self._queue.put((func, args, kwargs))
def run(self):
while not self._stop_thread:
try:
func, args, kwargs = self._queue.get(timeout=self._timeout)
func(*args, **kwargs)
except queue.Empty:
pass
except queue.Full:
LOG_WARN('Queue is full in _thread')
except Exception as ex:
LOG_ERROR('in _thread: ' + str(ex))
_thread = FileTransfersThread()
def start_file_transfer_thread():
_thread.start()
def stop_file_transfer_thread():
_thread.stop_thread()
def execute(func, *args, **kwargs):
_thread.execute(func, *args, **kwargs)
# -----------------------------------------------------------------------------------------------------------------
# Invoking in main thread
# -----------------------------------------------------------------------------------------------------------------
class InvokeEvent(QtCore.QEvent):
EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
def __init__(self, fn, *args, **kwargs):
QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
self.fn = fn
self.args = args
self.kwargs = kwargs
class Invoker(QtCore.QObject):
def event(self, event):
event.fn(*event.args, **event.kwargs)
return True
_invoker = Invoker()
def invoke_in_main_thread(fn, *args, **kwargs):
QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs))