Skip to content

Commit

Permalink
Add ProcessLocker
Browse files Browse the repository at this point in the history
  • Loading branch information
kozlovsky committed Dec 21, 2022
1 parent d33fb7c commit 8727410
Show file tree
Hide file tree
Showing 15 changed files with 724 additions and 27 deletions.
2 changes: 2 additions & 0 deletions src/run_tribler.py
Expand Up @@ -83,6 +83,8 @@ def init_boot_logger():
logger.info(f'Root state dir: {root_state_dir}')

api_port = os.environ.get('CORE_API_PORT')
api_port = int(api_port) if api_port else None

api_key = os.environ.get('CORE_API_KEY')

# Check whether we need to start the core or the user interface
Expand Down
4 changes: 4 additions & 0 deletions src/tribler/core/components/reporter/exception_handler.py
Expand Up @@ -10,6 +10,7 @@
from tribler.core.components.component import ComponentStartupException
from tribler.core.components.reporter.reported_error import ReportedError
from tribler.core.sentry_reporter.sentry_reporter import SentryReporter
from tribler.core.utilities import process_locker

# There are some errors that we are ignoring.
IGNORED_ERRORS_BY_CODE = {
Expand Down Expand Up @@ -111,6 +112,7 @@ def unhandled_error_observer(self, _, context):
should_stop=should_stop
)
self.logger.error(f"Unhandled exception occurred! {reported_error}\n{reported_error.long_text}")
process_locker.set_error(exc=exception)

if self.report_callback:
self.logger.error('Call report callback')
Expand All @@ -123,6 +125,8 @@ def unhandled_error_observer(self, _, context):
self.unreported_error = reported_error

except Exception as ex:
process_locker.set_error(exc=ex)

self.sentry_reporter.capture_exception(ex)
self.logger.exception(f'Error occurred during the error handling: {ex}')
raise ex
Expand Down
2 changes: 1 addition & 1 deletion src/tribler/core/components/session.py
Expand Up @@ -27,7 +27,7 @@ class Session:
def __init__(self, config: TriblerConfig = None, components: List[Component] = (),
shutdown_event: Event = None, notifier: Notifier = None, failfast: bool = True):
# deepcode ignore unguarded~next~call: not necessary to catch StopIteration on infinite iterator
self.exit_code = 0
self.exit_code = None
self.failfast = failfast
self.logger = logging.getLogger(self.__class__.__name__)
self.config: TriblerConfig = config or TriblerConfig()
Expand Down
3 changes: 0 additions & 3 deletions src/tribler/core/exceptions.py
Expand Up @@ -8,9 +8,6 @@
class TriblerException(Exception):
"""Super class for all Tribler-specific Exceptions the Tribler Core throws."""

def __str__(self):
return str(self.__class__) + ': ' + Exception.__str__(self)


class OperationNotPossibleAtRuntimeException(TriblerException):
"""The requested operation is not possible after the Session or Download has been started."""
Expand Down
5 changes: 5 additions & 0 deletions src/tribler/core/sentry_reporter/sentry_reporter.py
Expand Up @@ -21,6 +21,7 @@
parse_last_core_output, parse_os_environ,
parse_stacktrace,
)
from tribler.core.utilities.process_locker import get_global_process_locker

# fmt: off

Expand Down Expand Up @@ -219,6 +220,10 @@ def send_event(self, event: Dict = None, post_data: Dict = None, sys_info: Dict
reporter['events'] = extract_dict(sys_info, r'^(event|request)')
reporter[SYSINFO] = {key: sys_info[key] for key in sys_info if key not in reporter['events']}

process_locker = get_global_process_locker()
if process_locker:
reporter['last_processes'] = [p.describe() for p in process_locker.get_last_processes()]

# try to retrieve an error from the last_core_output
if last_core_output:
# split for better representation in the web view
Expand Down
25 changes: 21 additions & 4 deletions src/tribler/core/start_core.py
Expand Up @@ -16,6 +16,7 @@
from tribler.core.components.component import Component
from tribler.core.components.gigachannel.gigachannel_component import GigaChannelComponent
from tribler.core.components.gigachannel_manager.gigachannel_manager_component import GigachannelManagerComponent
from tribler.core.components.gui_process_watcher.gui_process_watcher import GuiProcessWatcher
from tribler.core.components.gui_process_watcher.gui_process_watcher_component import GuiProcessWatcherComponent
from tribler.core.components.ipv8.ipv8_component import Ipv8Component
from tribler.core.components.key.key_component import KeyComponent
Expand All @@ -39,6 +40,7 @@
from tribler.core.sentry_reporter.sentry_reporter import SentryReporter, SentryStrategy
from tribler.core.upgrade.version_manager import VersionHistory
from tribler.core.utilities.process_checker import single_tribler_instance
from tribler.core.utilities.process_locker import ProcessKind, ProcessLocker, set_global_process_locker

logger = logging.getLogger(__name__)
CONFIG_FILE_NAME = 'triblerd.conf'
Expand Down Expand Up @@ -119,7 +121,7 @@ async def core_session(config: TriblerConfig, components: List[Component]) -> in
return session.exit_code


def run_tribler_core_session(api_port: str, api_key: str, state_dir: Path, gui_test_mode: bool = False) -> int:
def run_tribler_core_session(api_port: int, api_key: str, state_dir: Path, gui_test_mode: bool = False) -> int:
"""
This method will start a new Tribler session.
Note that there is no direct communication between the GUI process and the core: all communication is performed
Expand All @@ -137,7 +139,7 @@ def run_tribler_core_session(api_port: str, api_key: str, state_dir: Path, gui_t
if SentryReporter.is_in_test_mode():
default_core_exception_handler.sentry_reporter.global_strategy = SentryStrategy.SEND_ALLOWED

config.api.http_port = int(api_port)
config.api.http_port = api_port
# If the API key is set to an empty string, it will remain disabled
if config.api.key not in ('', api_key):
config.api.key = api_key
Expand Down Expand Up @@ -177,10 +179,25 @@ def run_core(api_port, api_key, root_state_dir, parsed_args):
logger.info('Running Core' + ' in gui_test_mode' if parsed_args.gui_test_mode else '')
load_logger_config('tribler-core', root_state_dir)

gui_pid = GuiProcessWatcher.get_gui_pid()
process_locker = ProcessLocker(root_state_dir, ProcessKind.Core, gui_pid)
set_global_process_locker(process_locker)

if not process_locker.current_process.active:
msg = f'Another Core process with PID {process_locker.active_process.pid} is already running'
logger.warning(msg)
process_locker.sys_exit(1, msg)

if api_port is None:
msg = 'api_port is not specified for a core process'
logger.error(msg)
process_locker.sys_exit(1, msg)

process_locker.set_api_port(api_port)

with single_tribler_instance(root_state_dir):
version_history = VersionHistory(root_state_dir)
state_dir = version_history.code_version.directory
exit_code = run_tribler_core_session(api_port, api_key, state_dir, gui_test_mode=parsed_args.gui_test_mode)

if exit_code:
sys.exit(exit_code)
process_locker.sys_exit(exit_code)

0 comments on commit 8727410

Please sign in to comment.