diff --git a/CHANGELOG.md b/CHANGELOG.md index fd0f63e16e..50319103bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -151,6 +151,8 @@ Up requirements to Django 4.0+, Twisted 22+, Python 3.9 or 3.10 `LUNR_STOP_WORD_FILTER_EXCEPTIONS` can be extended to make sure common names are included. - Add `.deserialize()` method to `_Saver*` structures to help completely decouple structures from database without needing separate import. +- Add `run_in_main_thread` as a helper for those wanting to code server code + from a web view. ## Evennia 0.9.5 diff --git a/evennia/utils/idmapper/models.py b/evennia/utils/idmapper/models.py index f9c187f98c..0a4dda299e 100644 --- a/evennia/utils/idmapper/models.py +++ b/evennia/utils/idmapper/models.py @@ -41,7 +41,7 @@ PROC_MODIFIED_OBJS = WeakValueDictionary() _SELF_PID = os.getpid() _SERVER_PID, _PORTAL_PID = get_evennia_pids() _IS_SUBPROCESS = (_SERVER_PID and _PORTAL_PID) and _SELF_PID not in (_SERVER_PID, _PORTAL_PID) -_IS_MAIN_THREAD = threading.currentThread().getName() == "MainThread" +_IS_MAIN_THREAD = threading.current_thread().name == "MainThread" class SharedMemoryModelBase(ModelBase): diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index e88ed3ba69..d603a93bd2 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -11,6 +11,7 @@ import gc import sys import types import math +import threading import re import textwrap import random @@ -41,6 +42,8 @@ from evennia.utils import logger _MULTIMATCH_TEMPLATE = settings.SEARCH_MULTIMATCH_TEMPLATE _EVENNIA_DIR = settings.EVENNIA_DIR _GAME_DIR = settings.GAME_DIR +_IS_MAIN_THREAD = threading.current_thread().name == "MainThread" + ENCODINGS = settings.ENCODINGS _TASK_HANDLER = None @@ -2691,3 +2694,20 @@ def copy_word_case(base_word, new_word): ) + excess ) + +def run_in_main_thread(function_or_method, *args, **kwargs): + """ + Force a callable to execute in the main Evennia thread. This is only relevant when + calling code from e.g. web views, which run in a separate threadpool. Use this + to avoid race conditions. + + Args: + function_or_method (callable): A function or method to fire. + *args: Will be passed into the callable. + **kwargs: Will be passed into the callable. + + """ + if _IS_MAIN_THREAD: + return function_or_method(*args, **kwargs) + else: + return threads.blockingCallFromThread(reactor, function_or_method, *args, **kwargs)