From 890b475dfaa07be42ddf2189e2d2b3aca7c824bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Wed, 25 Oct 2023 17:05:36 +0100 Subject: [PATCH] Create workers in thread-safe way. Previously you couldn't call a threaded worker from another threaded worker because creating a worker was not thread safe (see #3472). However, the second threaded worker could already be called with self.call_from_thread, so we bake that in. --- src/textual/_work_decorator.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/textual/_work_decorator.py b/src/textual/_work_decorator.py index dbfdba185e..5ca34c3227 100644 --- a/src/textual/_work_decorator.py +++ b/src/textual/_work_decorator.py @@ -6,6 +6,7 @@ from __future__ import annotations +import threading from functools import partial, wraps from inspect import iscoroutinefunction from typing import TYPE_CHECKING, Callable, Coroutine, TypeVar, Union, cast, overload @@ -139,9 +140,16 @@ def decorated( debug_description = f"{method.__name__}({', '.join(token for token in tokens if token)})" except Exception: debug_description = "" + + # If we're creating/running a worker from inside a secondary thread, + # do so in a thread-safe way. + if self.app._thread_id != threading.get_ident(): + runner = partial(self.app.call_from_thread, self.run_worker) + else: + runner = self.run_worker worker = cast( "Worker[ReturnType]", - self.run_worker( + runner( partial(method, *args, **kwargs), name=name or method.__name__, group=group,