mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-26 00:47:10 +08:00
v1.7.25
This commit is contained in:
parent
40b603a17f
commit
308241402d
62
.efrocachemap
generated
62
.efrocachemap
generated
@ -421,10 +421,10 @@
|
||||
"build/assets/ba_data/audio/zoeOw.ogg": "74befe45a8417e95b6a2233c51992a26",
|
||||
"build/assets/ba_data/audio/zoePickup01.ogg": "48ab8cddfcde36a750856f3f81dd20c8",
|
||||
"build/assets/ba_data/audio/zoeScream01.ogg": "2b468aedfa8741090247f04eb9e6df55",
|
||||
"build/assets/ba_data/data/langdata.json": "85819425f358eafcd41e91fb999612ba",
|
||||
"build/assets/ba_data/data/langdata.json": "9929c8aa90669322bc0548cdb035fb1b",
|
||||
"build/assets/ba_data/data/languages/arabic.json": "db961f7fe0541a31880929e1c17ea957",
|
||||
"build/assets/ba_data/data/languages/belarussian.json": "5e373ddcfa6e1f771b74c02298a6599a",
|
||||
"build/assets/ba_data/data/languages/chinese.json": "b600924583b899165757f412eef0dd01",
|
||||
"build/assets/ba_data/data/languages/chinese.json": "6ca44804339926f5ad21584555aa22cf",
|
||||
"build/assets/ba_data/data/languages/chinesetraditional.json": "3fe960a8f0ca529aa57b4f9cb7385abc",
|
||||
"build/assets/ba_data/data/languages/croatian.json": "766532c67af5bd0144c2d63cab0516fa",
|
||||
"build/assets/ba_data/data/languages/czech.json": "f3ce219840946cb8f9aa6d3e25927ab3",
|
||||
@ -450,7 +450,7 @@
|
||||
"build/assets/ba_data/data/languages/russian.json": "aa99f9f597787fe4e09c8ab53fe2e081",
|
||||
"build/assets/ba_data/data/languages/serbian.json": "d7452dd72ac0e51680cb39b5ebaa1c69",
|
||||
"build/assets/ba_data/data/languages/slovak.json": "27962d53dc3f7dd4e877cd40faafeeef",
|
||||
"build/assets/ba_data/data/languages/spanish.json": "8776884442afd08b31364dc6a8b0cc16",
|
||||
"build/assets/ba_data/data/languages/spanish.json": "f9047a2d5e9f453981ca5f4cb53006fa",
|
||||
"build/assets/ba_data/data/languages/swedish.json": "77d671f10613291ebf9c71da66f18a18",
|
||||
"build/assets/ba_data/data/languages/tamil.json": "b9d4b4e107456ea6420ee0f9d9d7a03e",
|
||||
"build/assets/ba_data/data/languages/thai.json": "33f63753c9af9a5b238d229a0bf23fbc",
|
||||
@ -4068,26 +4068,26 @@
|
||||
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
|
||||
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
|
||||
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "884277e559d840df55f7e0dd1c186949",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "38240f93c1ae9065cf671be2e7620684",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "0480c97db92676754cf9552ee462dccb",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "052b4f4804e30da165744893e84d4a64",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "32d4c7a4bdac7a8cb12d4b42868a5979",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "42649b501212bea15f4b2f2741583339",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "03cbedec6c8f294d7fe57a6dc64803e8",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "37b2c536cd841b40319b0efae40471ce",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "1f67054d55541b13ca912b4d9b57f499",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "5399d3f4924909ca1c0878f347e20a10",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "18c01fc3b664bb30bd2e5b6ad635b5f3",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "58559f7512a300586b8141591a21564e",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "cec05c9cf3882d00ba27b81696d95d1e",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "8e98e2bb1d7d9fa34f873fd2b7d82138",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "c215407232dbf0166d9980c1dae780b6",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "49107effdf5c1ee5a0533251a99f9f6a",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "8731458bc07c5a9ecea1c2ac46b5b96c",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "e51c409b2f1ca6433f96e3b10615a914",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "b3d961223dc84f6805613790d1e2c267",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "1cadf9af171902a4984658c624a42142",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "5c33841bddb06db422bc7b610d54e850",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "7c68be74dc87cc8bea603351d4edc19e",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "4aee26e9c9399db7036d63c05b415146",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "0c863960576179584121bfa00b0c0176",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "5c7ce41451ea6fd44f3106ad4d333def",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "189837b829a2ea704fad9fb163e50490",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "ddc816b66933c7e5b811c9c7c2dd16b8",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "98e206e6e92514f7be59096a135bed3c",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "f29ba9eeddf9a55febff06fca7db52a9",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "c63f95f2f7e813351dfe18f51e6deeb4",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "c303ae3176d11b2b134068878e786833",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "123098bb0bb01419fa2acdad01be34a0",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "0d416da46426a9969ed3b7a91f5497b5",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "9e924485487aaabbc137c02f895247f2",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "61af0496d9b62e671fc55a38df776d99",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "62d2eaf952658ef8fe0d9910c9f8d964",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "6e35a1c8093de8886134d2f1dab4a56a",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "803800bebf963742fd26cae8e793245b",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "6bb44a0e36fa87f384aa4a29f56c9dcc",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "f72615c3eeea0a5f8789faa20d053f85",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "85ba4e81a1f7ae2cff4b1355eb49904f",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "498921f7eb2afd327d4b900cb70e31f9",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "85ba4e81a1f7ae2cff4b1355eb49904f",
|
||||
@ -4104,14 +4104,14 @@
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "0ab638b6602610bdaf432e3cc2464080",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "92394eb19387c363471ce134ac9e6a1b",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "0ab638b6602610bdaf432e3cc2464080",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "aa2d3f27999da77b4ad584df0707d8c0",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "2d134738e7ea8d7751ebae3df834806e",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "451c1d7295846fcad90b649fc574ee0e",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "3e910b8da4a13dbcdde7955bbde17abb",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "987b68e1e4b4e89bd73d5014735318d6",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "520f2b3e9c7ae15954589ce148dc1506",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "d02da070911bac71d7dbb4afc7a93e6a",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "629d285dfe041d4237e14d140ac70482",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "ae1fcb25dead5a8b306d1a3c8d02887e",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "56b419764d2ea9811fbca8bef99d032e",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "787b4c6337d367c2ad0cdf1cdc980b5e",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "7d4b36b9cc305832430210d7db55f13a",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "ae9083ecbbcf81f54ab3aafbf5a58273",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "1220228d0a0036c898483ef73b41056c",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "14d5968a2cb52b1eb9c33704f90a3e4d",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "a85e95a06c5a3f696334c3da807c0ada",
|
||||
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
|
||||
"src/assets/ba_data/python/babase/_mgen/enums.py": "f8cd3af311ac63147882590123b78318",
|
||||
"src/ballistica/base/mgen/pyembed/binding_base.inc": "eeddad968b176000e31c65be6206a2bc",
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
### 1.7.25 (build 21208, api 8, 2023-07-31)
|
||||
### 1.7.25 (build 21211, api 8, 2023-08-03)
|
||||
|
||||
- Fixed an issue where the main thread was holding the Python GIL by default in
|
||||
monolithic builds with environment-managed event loops. This theoretically
|
||||
could have lead to stuttery performanace in the Android or Mac builds.
|
||||
- Did a bit of cleanup on `baenv.py` in preparation for some additional setup it
|
||||
will soon be doing to give users more control over logging.
|
||||
- `getconfig` and `setconfig` in `efrotools` are now `getprojectconfig` and
|
||||
`setprojectconfig` (to reflect the file name changes that happened in 1.7.20).
|
||||
- The efrocache system (how assets and prebuilt binaries are downloaded during
|
||||
|
||||
2
Makefile
2
Makefile
@ -1167,7 +1167,7 @@ CHECK_CLEAN_SAFETY = tools/pcommand check_clean_safety
|
||||
TOOL_CFG_INST = tools/pcommand tool_config_install
|
||||
|
||||
# Anything that affects tool-config generation.
|
||||
TOOL_CFG_SRC = tools/efrotools/pcommand.py config/projectconfig.json
|
||||
TOOL_CFG_SRC = tools/efrotools/toolconfig.py config/projectconfig.json
|
||||
|
||||
# Anything that should trigger an environment-check when changed.
|
||||
ENV_SRC = tools/pcommand tools/batools/build.py
|
||||
|
||||
@ -1,10 +1,15 @@
|
||||
;; -*- lexical-binding: t; -*-
|
||||
|
||||
(
|
||||
|
||||
;; Specify some extra paths that project.el searches and whatnot should ignore.
|
||||
;; Note that gitignored stuff is ignored implicitly.
|
||||
(nil . ((project-vc-ignores . ("docs"
|
||||
|
||||
;; Stuff that applies everywhere.
|
||||
(nil . (
|
||||
;; Short project name to save some space in mode-lines/messages/etc.
|
||||
(project-vc-name . "bainternal")
|
||||
|
||||
;; Extra paths that searches and whatnot should ignore. Note that
|
||||
;; gitignored stuff is ignored implicitly.
|
||||
(project-vc-ignores . ("docs"
|
||||
"submodules"
|
||||
"src/external"
|
||||
"src/assets/ba_data/python-site-packages"
|
||||
|
||||
@ -24,8 +24,9 @@ from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
import __main__
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any
|
||||
|
||||
from efro.log import LogHandler
|
||||
|
||||
# IMPORTANT - It is likely (and in some cases expected) that this
|
||||
@ -51,27 +52,59 @@ if TYPE_CHECKING:
|
||||
|
||||
# Build number and version of the ballistica binary we expect to be
|
||||
# using.
|
||||
TARGET_BALLISTICA_BUILD = 21208
|
||||
TARGET_BALLISTICA_BUILD = 21211
|
||||
TARGET_BALLISTICA_VERSION = '1.7.25'
|
||||
|
||||
|
||||
@dataclass
|
||||
class EnvConfig:
|
||||
"""Environment values put together by the configure call."""
|
||||
"""Final config values we provide to the engine."""
|
||||
|
||||
# Where app config/state data lives.
|
||||
config_dir: str
|
||||
|
||||
# Directory containing ba_data and any other platform-specific data.
|
||||
data_dir: str
|
||||
user_python_dir: str | None
|
||||
|
||||
# Where the app's built-in Python stuff lives.
|
||||
app_python_dir: str | None
|
||||
|
||||
# Where the app's built-in Python stuff lives in the default case.
|
||||
standard_app_python_dir: str
|
||||
|
||||
# Where the app's bundled third party Python stuff lives.
|
||||
site_python_dir: str | None
|
||||
log_handler: LogHandler | None
|
||||
|
||||
# Custom Python provided by the user (mods).
|
||||
user_python_dir: str | None
|
||||
|
||||
# We have a mechanism allowing app scripts to be overridden by
|
||||
# placing a specially named directory in a user-scripts dir.
|
||||
# This is true if that is enabled.
|
||||
is_user_app_python_dir: bool
|
||||
|
||||
# Our fancy app log handler. This handles feeding logs, stdout, and
|
||||
# stderr into the engine so they show up on in-app consoles, etc.
|
||||
log_handler: LogHandler | None
|
||||
|
||||
# Initial data from the ballisticakit-config.json file. This is
|
||||
# passed mostly as an optimization to avoid reading the same config
|
||||
# file twice, since config data is first needed in baenv and next in
|
||||
# the engine. It will be cleared after passing it to the app's
|
||||
# config management subsystem and should not be accessed by any
|
||||
# other code.
|
||||
initial_app_config: Any
|
||||
|
||||
|
||||
@dataclass
|
||||
class _EnvGlobals:
|
||||
"""Our globals we store in the main module."""
|
||||
"""Globals related to baenv's operation.
|
||||
|
||||
We store this in __main__ instead of in our own module because it
|
||||
is likely that multiple versions of our module will be spun up
|
||||
and we want a single set of globals (see notes at top of our module
|
||||
code).
|
||||
"""
|
||||
|
||||
config: EnvConfig | None = None
|
||||
called_configure: bool = False
|
||||
@ -90,7 +123,7 @@ class _EnvGlobals:
|
||||
|
||||
|
||||
def did_paths_set_fail() -> bool:
|
||||
"""Did we try to set paths and failed?"""
|
||||
"""Did we try to set paths and fail?"""
|
||||
return _EnvGlobals.get().paths_set_failed
|
||||
|
||||
|
||||
@ -104,8 +137,14 @@ def get_config() -> EnvConfig:
|
||||
"""Return the active config, creating a default if none exists."""
|
||||
envglobals = _EnvGlobals.get()
|
||||
|
||||
# If configure() has not been explicitly called, set up a
|
||||
# minimally-intrusive default config. We want Ballistica to default
|
||||
# to being a good citizen when imported into alien environments and
|
||||
# not blow away logging or otherwise muck with stuff. All official
|
||||
# paths to run Ballistica apps should be explicitly calling
|
||||
# configure() first to get a full featured setup.
|
||||
if not envglobals.called_configure:
|
||||
configure()
|
||||
configure(setup_logging=False)
|
||||
|
||||
config = envglobals.config
|
||||
if config is None:
|
||||
@ -123,17 +162,20 @@ def configure(
|
||||
app_python_dir: str | None = None,
|
||||
site_python_dir: str | None = None,
|
||||
contains_python_dist: bool = False,
|
||||
setup_logging: bool = True,
|
||||
) -> None:
|
||||
"""Set up the Python environment for running a Ballistica app.
|
||||
"""Set up the environment for running a Ballistica app.
|
||||
|
||||
This includes things such as Python path wrangling and app directory
|
||||
creation. This should be called before any other ballistica modules
|
||||
are imported since it may make changes to sys.path which can affect
|
||||
where those modules get loaded from.
|
||||
creation. This must be called before any actual Ballistica modules
|
||||
are imported; the environment is locked in as soon as that happens.
|
||||
"""
|
||||
|
||||
envglobals = _EnvGlobals.get()
|
||||
|
||||
# Keep track of whether we've been *called*, not whether a config
|
||||
# has been created. Otherwise its possible to get multiple
|
||||
# overlapping configure calls going.
|
||||
if envglobals.called_configure:
|
||||
raise RuntimeError(
|
||||
'baenv.configure() has already been called;'
|
||||
@ -141,21 +183,12 @@ def configure(
|
||||
)
|
||||
envglobals.called_configure = True
|
||||
|
||||
# The very first thing we do is set up our logging system and pipe
|
||||
# Python's stdout/stderr into it. Then we can at least debug
|
||||
# problems on systems where native stdout/stderr is not easily
|
||||
# accessible such as Android.
|
||||
log_handler = _setup_logging()
|
||||
|
||||
# We want to always be run in UTF-8 mode; complain if we're not.
|
||||
if sys.flags.utf8_mode != 1:
|
||||
logging.warning(
|
||||
"Python's UTF-8 mode is not set. Running ballistica without"
|
||||
' it may lead to errors.'
|
||||
)
|
||||
|
||||
# Attempt to set up Python paths and our own data paths so that
|
||||
# engine modules, mods, etc. are pulled from predictable places.
|
||||
# The very first thing we do is setup Python paths (while also
|
||||
# calculating some engine paths). This code needs to be bulletproof
|
||||
# since we have no logging yet at this point. We used to set up
|
||||
# logging first, but this way logging stuff will get loaded from its
|
||||
# proper final path (otherwise we might wind up using two different
|
||||
# versions of efro.logging in a single engine run).
|
||||
(
|
||||
user_python_dir,
|
||||
app_python_dir,
|
||||
@ -172,6 +205,19 @@ def configure(
|
||||
config_dir,
|
||||
)
|
||||
|
||||
# The second thing we do is set up our logging system and pipe
|
||||
# Python's stdout/stderr into it. At this point we can at least
|
||||
# debug problems on systems where native stdout/stderr is not easily
|
||||
# accessible such as Android.
|
||||
log_handler = _setup_logging() if setup_logging else None
|
||||
|
||||
# We want to always be run in UTF-8 mode; complain if we're not.
|
||||
if sys.flags.utf8_mode != 1:
|
||||
logging.warning(
|
||||
"Python's UTF-8 mode is not set. Running Ballistica without"
|
||||
' it may lead to errors.'
|
||||
)
|
||||
|
||||
# Attempt to create dirs that we'll write stuff to.
|
||||
_setup_dirs(config_dir, user_python_dir)
|
||||
|
||||
@ -188,6 +234,7 @@ def configure(
|
||||
site_python_dir=site_python_dir,
|
||||
log_handler=log_handler,
|
||||
is_user_app_python_dir=is_user_app_python_dir,
|
||||
initial_app_config=None,
|
||||
)
|
||||
|
||||
|
||||
@ -198,11 +245,13 @@ def _calc_data_dir(data_dir: str | None) -> str:
|
||||
assert Path(__file__).parts[-3:-1] == ('ba_data', 'python')
|
||||
data_dir_path = Path(__file__).parents[2]
|
||||
|
||||
# Prefer tidy relative paths like '.' if possible so that things
|
||||
# like stack traces are easier to read.
|
||||
|
||||
# NOTE: Perhaps we should have an option to disable this
|
||||
# behavior for cases where the user might be doing chdir stuff.
|
||||
# Prefer tidy relative paths like './ba_data' if possible so
|
||||
# that things like stack traces are easier to read. For best
|
||||
# results, platforms where CWD doesn't matter can chdir to where
|
||||
# ba_data lives before calling configure().
|
||||
#
|
||||
# NOTE: If there's ever a case where the user is chdir'ing at
|
||||
# runtime we might want an option to use only abs paths here.
|
||||
cwd_path = Path.cwd()
|
||||
data_dir = str(
|
||||
data_dir_path.relative_to(cwd_path)
|
||||
@ -226,7 +275,7 @@ def _setup_logging() -> LogHandler:
|
||||
|
||||
|
||||
def _setup_certs(contains_python_dist: bool) -> None:
|
||||
# In situations where we're bringing our own Python let's also
|
||||
# In situations where we're bringing our own Python, let's also
|
||||
# provide our own root certs so ssl works. We can consider
|
||||
# overriding this in particular embedded cases if we can verify that
|
||||
# system certs are working. We also allow forcing this via an env
|
||||
@ -279,7 +328,7 @@ def _setup_paths(
|
||||
envglobals.paths_set_failed = True
|
||||
|
||||
else:
|
||||
# Ok; _babase hasn't been imported yet so we can muck with
|
||||
# Ok; _babase hasn't been imported yet, so we can muck with
|
||||
# Python paths.
|
||||
|
||||
if app_python_dir is None:
|
||||
|
||||
@ -87,7 +87,7 @@ void App::RunRenderUpkeepCycle() {
|
||||
|
||||
// Pump thread messages (we're being driven by frame-draw callbacks
|
||||
// so this is the only place that it gets done at).
|
||||
event_loop()->RunEventLoop(true); // Single pass only.
|
||||
event_loop()->RunSingleCycle();
|
||||
|
||||
// Now do the general app event cycle for whoever needs to process things.
|
||||
RunEvents();
|
||||
@ -301,10 +301,14 @@ void App::DidFinishRenderingFrame(FrameDef* frame) {}
|
||||
void App::PrimeMainThreadEventPump() {
|
||||
assert(!ManagesEventLoop());
|
||||
|
||||
// Need to release the GIL while we're doing this so other thread
|
||||
// can do their Python-y stuff.
|
||||
Python::ScopedInterpreterLockRelease release;
|
||||
|
||||
// Pump events manually until a screen gets created.
|
||||
// At that point we use frame-draws to drive our event loop.
|
||||
while (!g_base->graphics_server->initial_screen_created()) {
|
||||
event_loop()->RunEventLoop(true);
|
||||
event_loop()->RunSingleCycle();
|
||||
core::CorePlatform::SleepMillisecs(1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,8 +84,7 @@ void BaseFeatureSet::OnModuleExec(PyObject* module) {
|
||||
|
||||
g_core->LifecycleLog("_babase exec begin");
|
||||
|
||||
// Want to run this at the last possible moment before spinning up our
|
||||
// BaseFeatureSet. This locks in baenv customizations.
|
||||
// This locks in a baenv configuration.
|
||||
g_core->ApplyBaEnvConfig();
|
||||
|
||||
// Create our feature-set's C++ front-end.
|
||||
@ -280,18 +279,14 @@ void BaseFeatureSet::RunAppToCompletion() {
|
||||
BA_PRECONDITION(!called_run_app_to_completion_);
|
||||
called_run_app_to_completion_ = true;
|
||||
|
||||
// Start things moving if not done yet.
|
||||
if (!called_start_app_) {
|
||||
StartApp();
|
||||
}
|
||||
|
||||
// Let go of the GIL while we're running. The logic thread or other things
|
||||
// will grab it when needed.
|
||||
// Let go of the GIL while we're running.
|
||||
Python::ScopedInterpreterLockRelease gil_release;
|
||||
|
||||
// On our event-loop-managing platforms we now simply sit in our event
|
||||
// loop until the app is quit.
|
||||
g_core->main_event_loop()->RunEventLoop(false);
|
||||
g_core->main_event_loop()->RunToCompletion();
|
||||
}
|
||||
|
||||
void BaseFeatureSet::PrimeAppMainThreadEventPump() {
|
||||
|
||||
@ -16,7 +16,8 @@ BaseSoftInterface* g_base_soft{};
|
||||
|
||||
auto CoreFeatureSet::Import(const CoreConfig* config) -> CoreFeatureSet* {
|
||||
// In monolithic builds we can accept an explicit core-config the first
|
||||
// time we're imported.
|
||||
// time we're imported. In this case, Python is not even spun up yet so
|
||||
// it can influence even that.
|
||||
if (g_buildconfig.monolithic_build()) {
|
||||
if (config != nullptr) {
|
||||
if (g_core != nullptr) {
|
||||
@ -36,7 +37,9 @@ auto CoreFeatureSet::Import(const CoreConfig* config) -> CoreFeatureSet* {
|
||||
} else {
|
||||
// In modular builds we autogenerate a CoreConfig that takes into
|
||||
// account only env-vars (or env-vars plus Python args if we're being
|
||||
// run via the baenv script).
|
||||
// run via the baenv script). In this case, Python is already spun up
|
||||
// and baenv already handled any Python environment stuff so we have
|
||||
// less to do.
|
||||
if (config != nullptr) {
|
||||
FatalError("CoreConfig can't be explicitly passed in modular builds.");
|
||||
}
|
||||
|
||||
@ -464,8 +464,6 @@ auto CorePlatform::IsRunningOnDesktop() -> bool {
|
||||
}
|
||||
|
||||
void CorePlatform::SleepMillisecs(millisecs_t ms) {
|
||||
// If we're holding the Python GIL, release it while we sleep.
|
||||
Python::ScopedInterpreterLockRelease release;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
|
||||
}
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
|
||||
namespace ballistica {
|
||||
|
||||
// These are set automatically via script; don't modify them here.
|
||||
const int kEngineBuildNumber = 21208;
|
||||
const int kEngineBuildNumber = 21211;
|
||||
const char* kEngineVersion = "1.7.25";
|
||||
|
||||
#if BA_MONOLITHIC_BUILD
|
||||
@ -116,13 +116,18 @@ auto MonolithicMain(const core::CoreConfig& core_config) -> int {
|
||||
// In environments where we control the event loop... do that.
|
||||
l_base->RunAppToCompletion();
|
||||
} else {
|
||||
// Under managed environments we now simply return and let the
|
||||
// environment feed us events until the app exits. However, we may
|
||||
// need to first 'prime the pump' here for our main thread event loop.
|
||||
// For instance, if our event loop is driven by frame draws, we may
|
||||
// need to manually pump events until we receive the 'create-screen'
|
||||
// message from the logic thread which gets our frame draws going.
|
||||
// If the environment is managing events, we now simply return and let
|
||||
// it feed us those events. However, we may first need to 'prime the
|
||||
// pump'. For instance, if the work we do here in the main thread is
|
||||
// driven by frame draws, we may need to manually pump events until we
|
||||
// receive a 'create-screen' message from the logic thread which
|
||||
// gets those frame draws going.
|
||||
l_base->PrimeAppMainThreadEventPump();
|
||||
|
||||
// IMPORTANT - We're still holding the GIL at this point, so we need
|
||||
// to permanently release it to avoid starving the app. Any of our
|
||||
// callback code that needs it will need to acquire it.
|
||||
Python::PermanentlyReleaseGIL();
|
||||
}
|
||||
} catch (const std::exception& exc) {
|
||||
std::string error_msg =
|
||||
|
||||
@ -33,31 +33,31 @@ EventLoop::EventLoop(EventLoopID identifier_in, ThreadSource source)
|
||||
void* (*funcp)(void*);
|
||||
switch (identifier_) {
|
||||
case EventLoopID::kLogic:
|
||||
func = ThreadMainLogic;
|
||||
funcp = ThreadMainLogicP;
|
||||
func = ThreadMainLogic_;
|
||||
funcp = ThreadMainLogicP_;
|
||||
break;
|
||||
case EventLoopID::kAssets:
|
||||
func = ThreadMainAssets;
|
||||
funcp = ThreadMainAssetsP;
|
||||
func = ThreadMainAssets_;
|
||||
funcp = ThreadMainAssetsP_;
|
||||
break;
|
||||
case EventLoopID::kMain:
|
||||
// Shouldn't happen; this thread gets wrapped; not launched.
|
||||
throw Exception();
|
||||
case EventLoopID::kAudio:
|
||||
func = ThreadMainAudio;
|
||||
funcp = ThreadMainAudioP;
|
||||
func = ThreadMainAudio_;
|
||||
funcp = ThreadMainAudioP_;
|
||||
break;
|
||||
case EventLoopID::kBGDynamics:
|
||||
func = ThreadMainBGDynamics;
|
||||
funcp = ThreadMainBGDynamicsP;
|
||||
func = ThreadMainBGDynamics_;
|
||||
funcp = ThreadMainBGDynamicsP_;
|
||||
break;
|
||||
case EventLoopID::kNetworkWrite:
|
||||
func = ThreadMainNetworkWrite;
|
||||
funcp = ThreadMainNetworkWriteP;
|
||||
func = ThreadMainNetworkWrite_;
|
||||
funcp = ThreadMainNetworkWriteP_;
|
||||
break;
|
||||
case EventLoopID::kStdin:
|
||||
func = ThreadMainStdInput;
|
||||
funcp = ThreadMainStdInputP;
|
||||
func = ThreadMainStdInput_;
|
||||
funcp = ThreadMainStdInputP_;
|
||||
break;
|
||||
default:
|
||||
throw Exception();
|
||||
@ -105,7 +105,7 @@ EventLoop::EventLoop(EventLoopID identifier_in, ThreadSource source)
|
||||
thread_id_ = std::this_thread::get_id();
|
||||
|
||||
// Set our own thread-id-to-name mapping.
|
||||
SetInternalThreadName("main");
|
||||
SetInternalThreadName_("main");
|
||||
|
||||
// Hmmm we might want to set our OS thread name here,
|
||||
// as we do for other threads? (SetCurrentThreadName).
|
||||
@ -116,7 +116,7 @@ EventLoop::EventLoop(EventLoopID identifier_in, ThreadSource source)
|
||||
}
|
||||
}
|
||||
|
||||
void EventLoop::SetInternalThreadName(const std::string& name) {
|
||||
void EventLoop::SetInternalThreadName_(const std::string& name) {
|
||||
assert(g_core);
|
||||
std::scoped_lock lock(g_core->thread_name_map_mutex);
|
||||
g_core->thread_name_map[std::this_thread::get_id()] = name;
|
||||
@ -135,57 +135,57 @@ void EventLoop::ClearCurrentThreadName() {
|
||||
// in stack traces which thread is running in case it is not otherwise
|
||||
// evident.
|
||||
|
||||
auto EventLoop::ThreadMainLogic(void* data) -> int {
|
||||
return static_cast<EventLoop*>(data)->ThreadMain();
|
||||
auto EventLoop::ThreadMainLogic_(void* data) -> int {
|
||||
return static_cast<EventLoop*>(data)->ThreadMain_();
|
||||
}
|
||||
|
||||
auto EventLoop::ThreadMainLogicP(void* data) -> void* {
|
||||
static_cast<EventLoop*>(data)->ThreadMain();
|
||||
auto EventLoop::ThreadMainLogicP_(void* data) -> void* {
|
||||
static_cast<EventLoop*>(data)->ThreadMain_();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto EventLoop::ThreadMainAudio(void* data) -> int {
|
||||
return static_cast<EventLoop*>(data)->ThreadMain();
|
||||
auto EventLoop::ThreadMainAudio_(void* data) -> int {
|
||||
return static_cast<EventLoop*>(data)->ThreadMain_();
|
||||
}
|
||||
|
||||
auto EventLoop::ThreadMainAudioP(void* data) -> void* {
|
||||
static_cast<EventLoop*>(data)->ThreadMain();
|
||||
auto EventLoop::ThreadMainAudioP_(void* data) -> void* {
|
||||
static_cast<EventLoop*>(data)->ThreadMain_();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto EventLoop::ThreadMainBGDynamics(void* data) -> int {
|
||||
return static_cast<EventLoop*>(data)->ThreadMain();
|
||||
auto EventLoop::ThreadMainBGDynamics_(void* data) -> int {
|
||||
return static_cast<EventLoop*>(data)->ThreadMain_();
|
||||
}
|
||||
|
||||
auto EventLoop::ThreadMainBGDynamicsP(void* data) -> void* {
|
||||
static_cast<EventLoop*>(data)->ThreadMain();
|
||||
auto EventLoop::ThreadMainBGDynamicsP_(void* data) -> void* {
|
||||
static_cast<EventLoop*>(data)->ThreadMain_();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto EventLoop::ThreadMainNetworkWrite(void* data) -> int {
|
||||
return static_cast<EventLoop*>(data)->ThreadMain();
|
||||
auto EventLoop::ThreadMainNetworkWrite_(void* data) -> int {
|
||||
return static_cast<EventLoop*>(data)->ThreadMain_();
|
||||
}
|
||||
|
||||
auto EventLoop::ThreadMainNetworkWriteP(void* data) -> void* {
|
||||
static_cast<EventLoop*>(data)->ThreadMain();
|
||||
auto EventLoop::ThreadMainNetworkWriteP_(void* data) -> void* {
|
||||
static_cast<EventLoop*>(data)->ThreadMain_();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto EventLoop::ThreadMainStdInput(void* data) -> int {
|
||||
return static_cast<EventLoop*>(data)->ThreadMain();
|
||||
auto EventLoop::ThreadMainStdInput_(void* data) -> int {
|
||||
return static_cast<EventLoop*>(data)->ThreadMain_();
|
||||
}
|
||||
|
||||
auto EventLoop::ThreadMainStdInputP(void* data) -> void* {
|
||||
static_cast<EventLoop*>(data)->ThreadMain();
|
||||
auto EventLoop::ThreadMainStdInputP_(void* data) -> void* {
|
||||
static_cast<EventLoop*>(data)->ThreadMain_();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto EventLoop::ThreadMainAssets(void* data) -> int {
|
||||
return static_cast<EventLoop*>(data)->ThreadMain();
|
||||
auto EventLoop::ThreadMainAssets_(void* data) -> int {
|
||||
return static_cast<EventLoop*>(data)->ThreadMain_();
|
||||
}
|
||||
|
||||
auto EventLoop::ThreadMainAssetsP(void* data) -> void* {
|
||||
static_cast<EventLoop*>(data)->ThreadMain();
|
||||
auto EventLoop::ThreadMainAssetsP_(void* data) -> void* {
|
||||
static_cast<EventLoop*>(data)->ThreadMain_();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -193,11 +193,11 @@ void EventLoop::PushSetPaused(bool paused) {
|
||||
assert(g_core);
|
||||
// Can be toggled from the main thread only.
|
||||
assert(std::this_thread::get_id() == g_core->main_thread_id);
|
||||
PushThreadMessage(ThreadMessage(paused ? ThreadMessage::Type::kPause
|
||||
: ThreadMessage::Type::kResume));
|
||||
PushThreadMessage_(ThreadMessage_(paused ? ThreadMessage_::Type::kPause
|
||||
: ThreadMessage_::Type::kResume));
|
||||
}
|
||||
|
||||
void EventLoop::WaitForNextEvent(bool single_cycle) {
|
||||
void EventLoop::WaitForNextEvent_(bool single_cycle) {
|
||||
assert(g_core);
|
||||
|
||||
// If we're running a single cycle we never stop to wait.
|
||||
@ -225,7 +225,7 @@ void EventLoop::WaitForNextEvent(bool single_cycle) {
|
||||
|
||||
// While we're waiting, allow other python threads to run.
|
||||
if (acquires_python_gil_) {
|
||||
ReleaseGIL();
|
||||
ReleaseGIL_();
|
||||
}
|
||||
|
||||
// If we've got active timers, wait for messages with a timeout so we can
|
||||
@ -258,11 +258,11 @@ void EventLoop::WaitForNextEvent(bool single_cycle) {
|
||||
}
|
||||
|
||||
if (acquires_python_gil_) {
|
||||
AcquireGIL();
|
||||
AcquireGIL_();
|
||||
}
|
||||
}
|
||||
|
||||
void EventLoop::LoopUpkeep(bool single_cycle) {
|
||||
void EventLoop::LoopUpkeep_(bool single_cycle) {
|
||||
assert(g_core);
|
||||
// Keep our autorelease pool clean on mac/ios
|
||||
// FIXME: Should define a CorePlatform::ThreadHelper or something
|
||||
@ -281,38 +281,41 @@ void EventLoop::LoopUpkeep(bool single_cycle) {
|
||||
#endif
|
||||
}
|
||||
|
||||
auto EventLoop::RunEventLoop(bool single_cycle) -> int {
|
||||
void EventLoop::RunToCompletion() { Run_(false); }
|
||||
void EventLoop::RunSingleCycle() { Run_(true); }
|
||||
|
||||
void EventLoop::Run_(bool single_cycle) {
|
||||
assert(g_core);
|
||||
while (true) {
|
||||
LoopUpkeep(single_cycle);
|
||||
LoopUpkeep_(single_cycle);
|
||||
|
||||
WaitForNextEvent(single_cycle);
|
||||
WaitForNextEvent_(single_cycle);
|
||||
|
||||
// Process all queued thread messages.
|
||||
std::list<ThreadMessage> thread_messages;
|
||||
GetThreadMessages(&thread_messages);
|
||||
std::list<ThreadMessage_> thread_messages;
|
||||
GetThreadMessages_(&thread_messages);
|
||||
for (auto& thread_message : thread_messages) {
|
||||
switch (thread_message.type) {
|
||||
case ThreadMessage::Type::kRunnable: {
|
||||
PushLocalRunnable(thread_message.runnable,
|
||||
thread_message.completion_flag);
|
||||
case ThreadMessage_::Type::kRunnable: {
|
||||
PushLocalRunnable_(thread_message.runnable,
|
||||
thread_message.completion_flag);
|
||||
break;
|
||||
}
|
||||
case ThreadMessage::Type::kShutdown: {
|
||||
case ThreadMessage_::Type::kShutdown: {
|
||||
done_ = true;
|
||||
break;
|
||||
}
|
||||
case ThreadMessage::Type::kPause: {
|
||||
case ThreadMessage_::Type::kPause: {
|
||||
assert(!paused_);
|
||||
RunPauseCallbacks();
|
||||
RunPauseCallbacks_();
|
||||
paused_ = true;
|
||||
last_pause_time_ = g_core->GetAppTimeMillisecs();
|
||||
messages_since_paused_ = 0;
|
||||
break;
|
||||
}
|
||||
case ThreadMessage::Type::kResume: {
|
||||
case ThreadMessage_::Type::kResume: {
|
||||
assert(paused_);
|
||||
RunResumeCallbacks();
|
||||
RunResumeCallbacks_();
|
||||
paused_ = false;
|
||||
break;
|
||||
}
|
||||
@ -328,17 +331,16 @@ auto EventLoop::RunEventLoop(bool single_cycle) -> int {
|
||||
|
||||
if (!paused_) {
|
||||
timers_.Run(g_core->GetAppTimeMillisecs());
|
||||
RunPendingRunnables();
|
||||
RunPendingRunnables_();
|
||||
}
|
||||
|
||||
if (done_ || single_cycle) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EventLoop::GetThreadMessages(std::list<ThreadMessage>* messages) {
|
||||
void EventLoop::GetThreadMessages_(std::list<ThreadMessage_>* messages) {
|
||||
assert(messages);
|
||||
assert(std::this_thread::get_id() == thread_id());
|
||||
|
||||
@ -350,7 +352,7 @@ void EventLoop::GetThreadMessages(std::list<ThreadMessage>* messages) {
|
||||
}
|
||||
}
|
||||
|
||||
auto EventLoop::ThreadMain() -> int {
|
||||
auto EventLoop::ThreadMain_() -> int {
|
||||
assert(g_core);
|
||||
try {
|
||||
assert(source_ == ThreadSource::kCreate);
|
||||
@ -395,7 +397,7 @@ auto EventLoop::ThreadMain() -> int {
|
||||
throw Exception();
|
||||
}
|
||||
assert(name && id_string);
|
||||
SetInternalThreadName(name);
|
||||
SetInternalThreadName_(name);
|
||||
g_core->platform->SetCurrentThreadName(id_string);
|
||||
|
||||
// Mark ourself as bootstrapped and signal listeners so
|
||||
@ -403,19 +405,19 @@ auto EventLoop::ThreadMain() -> int {
|
||||
bootstrapped_ = true;
|
||||
|
||||
{
|
||||
// Momentarily grab this lock. This ensures that whoever launched us
|
||||
// is now actively waiting for this notification. If we skipped this
|
||||
// it would be possible to notify before they start listening which
|
||||
// leads to a hang.
|
||||
// Momentarily grab this lock. This pauses if need be until whoever
|
||||
// launched us releases their lock, which means they're now actively
|
||||
// waiting for our notification. If we skipped this, it would be
|
||||
// possible to zip through and send the notification before they
|
||||
// start listening for it which would lead to a hang.
|
||||
std::unique_lock lock(client_listener_mutex_);
|
||||
}
|
||||
client_listener_cv_.notify_all();
|
||||
|
||||
// Now just run our loop until we die.
|
||||
int result = RunEventLoop();
|
||||
RunToCompletion();
|
||||
|
||||
ClearCurrentThreadName();
|
||||
return result;
|
||||
return 0;
|
||||
} catch (const std::exception& e) {
|
||||
auto error_msg = std::string("Unhandled exception in ")
|
||||
+ CurrentThreadName() + " thread:\n" + e.what();
|
||||
@ -452,7 +454,7 @@ void EventLoop::SetAcquiresPythonGIL() {
|
||||
assert(!acquires_python_gil_); // This should be called exactly once.
|
||||
assert(ThreadIsCurrent());
|
||||
acquires_python_gil_ = true;
|
||||
AcquireGIL();
|
||||
AcquireGIL_();
|
||||
}
|
||||
|
||||
// Explicitly kill the main thread.
|
||||
@ -465,7 +467,7 @@ void EventLoop::Quit() {
|
||||
|
||||
EventLoop::~EventLoop() = default;
|
||||
|
||||
void EventLoop::LogThreadMessageTally(
|
||||
void EventLoop::LogThreadMessageTally_(
|
||||
std::vector<std::pair<LogLevel, std::string>>* log_entries) {
|
||||
assert(g_core);
|
||||
// Prevent recursion.
|
||||
@ -480,23 +482,23 @@ void EventLoop::LogThreadMessageTally(
|
||||
for (auto&& m : thread_messages_) {
|
||||
std::string s;
|
||||
switch (m.type) {
|
||||
case ThreadMessage::Type::kShutdown:
|
||||
case ThreadMessage_::Type::kShutdown:
|
||||
s += "kShutdown";
|
||||
break;
|
||||
case ThreadMessage::Type::kRunnable:
|
||||
case ThreadMessage_::Type::kRunnable:
|
||||
s += "kRunnable";
|
||||
break;
|
||||
case ThreadMessage::Type::kPause:
|
||||
case ThreadMessage_::Type::kPause:
|
||||
s += "kPause";
|
||||
break;
|
||||
case ThreadMessage::Type::kResume:
|
||||
case ThreadMessage_::Type::kResume:
|
||||
s += "kResume";
|
||||
break;
|
||||
default:
|
||||
s += "UNKNOWN(" + std::to_string(static_cast<int>(m.type)) + ")";
|
||||
break;
|
||||
}
|
||||
if (m.type == ThreadMessage::Type::kRunnable) {
|
||||
if (m.type == ThreadMessage_::Type::kRunnable) {
|
||||
std::string m_name =
|
||||
g_core->platform->DemangleCXXSymbol(typeid(*(m.runnable)).name());
|
||||
s += std::string(": ") + m_name;
|
||||
@ -518,7 +520,7 @@ void EventLoop::LogThreadMessageTally(
|
||||
}
|
||||
}
|
||||
|
||||
void EventLoop::PushThreadMessage(const ThreadMessage& t) {
|
||||
void EventLoop::PushThreadMessage_(const ThreadMessage_& t) {
|
||||
assert(g_core);
|
||||
// We don't want to make log calls while holding this mutex;
|
||||
// log calls acquire the GIL and if the GIL-holder (generally
|
||||
@ -562,7 +564,7 @@ void EventLoop::PushThreadMessage(const ThreadMessage& t) {
|
||||
LogLevel::kError,
|
||||
"ThreadMessage list > 1000 in thread: " + CurrentThreadName());
|
||||
|
||||
LogThreadMessageTally(&log_entries);
|
||||
LogThreadMessageTally_(&log_entries);
|
||||
}
|
||||
}
|
||||
|
||||
@ -661,7 +663,7 @@ auto EventLoop::CurrentThreadName() -> std::string {
|
||||
#endif
|
||||
}
|
||||
|
||||
void EventLoop::RunPendingRunnables() {
|
||||
void EventLoop::RunPendingRunnables_() {
|
||||
// Pull all runnables off the list first (its possible for one of these
|
||||
// runnables to add more) and then process them.
|
||||
assert(std::this_thread::get_id() == thread_id());
|
||||
@ -691,27 +693,27 @@ void EventLoop::RunPendingRunnables() {
|
||||
}
|
||||
}
|
||||
|
||||
void EventLoop::RunPauseCallbacks() {
|
||||
void EventLoop::RunPauseCallbacks_() {
|
||||
for (Runnable* i : pause_callbacks_) {
|
||||
i->Run();
|
||||
}
|
||||
}
|
||||
|
||||
void EventLoop::RunResumeCallbacks() {
|
||||
void EventLoop::RunResumeCallbacks_() {
|
||||
for (Runnable* i : resume_callbacks_) {
|
||||
i->Run();
|
||||
}
|
||||
}
|
||||
|
||||
void EventLoop::PushLocalRunnable(Runnable* runnable, bool* completion_flag) {
|
||||
void EventLoop::PushLocalRunnable_(Runnable* runnable, bool* completion_flag) {
|
||||
assert(std::this_thread::get_id() == thread_id());
|
||||
runnables_.emplace_back(runnable, completion_flag);
|
||||
}
|
||||
|
||||
void EventLoop::PushCrossThreadRunnable(Runnable* runnable,
|
||||
bool* completion_flag) {
|
||||
PushThreadMessage(EventLoop::ThreadMessage(
|
||||
EventLoop::ThreadMessage::Type::kRunnable, runnable, completion_flag));
|
||||
void EventLoop::PushCrossThreadRunnable_(Runnable* runnable,
|
||||
bool* completion_flag) {
|
||||
PushThreadMessage_(EventLoop::ThreadMessage_(
|
||||
EventLoop::ThreadMessage_::Type::kRunnable, runnable, completion_flag));
|
||||
}
|
||||
|
||||
void EventLoop::AddPauseCallback(Runnable* runnable) {
|
||||
@ -729,9 +731,9 @@ void EventLoop::PushRunnable(Runnable* runnable) {
|
||||
// If we're being called from withing our thread, just drop it in the list.
|
||||
// otherwise send it as a message to the other thread.
|
||||
if (std::this_thread::get_id() == thread_id()) {
|
||||
PushLocalRunnable(runnable, nullptr);
|
||||
PushLocalRunnable_(runnable, nullptr);
|
||||
} else {
|
||||
PushCrossThreadRunnable(runnable, nullptr);
|
||||
PushCrossThreadRunnable_(runnable, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -753,7 +755,7 @@ void EventLoop::PushRunnableSynchronous(Runnable* runnable) {
|
||||
"PushRunnableSynchronous called from target thread;"
|
||||
" would deadlock.");
|
||||
} else {
|
||||
PushCrossThreadRunnable(runnable, &complete);
|
||||
PushCrossThreadRunnable_(runnable, &complete);
|
||||
}
|
||||
|
||||
if (identifier_ == EventLoopID::kLogic) {
|
||||
@ -777,15 +779,15 @@ auto EventLoop::CheckPushSafety() -> bool {
|
||||
// behave the same as the thread-message safety check.
|
||||
return (runnables_.size() < kThreadMessageSafetyThreshold);
|
||||
} else {
|
||||
return CheckPushRunnableSafety();
|
||||
return CheckPushRunnableSafety_();
|
||||
}
|
||||
}
|
||||
auto EventLoop::CheckPushRunnableSafety() -> bool {
|
||||
auto EventLoop::CheckPushRunnableSafety_() -> bool {
|
||||
std::unique_lock lock(thread_message_mutex_);
|
||||
return thread_messages_.size() < kThreadMessageSafetyThreshold;
|
||||
}
|
||||
|
||||
void EventLoop::AcquireGIL() {
|
||||
void EventLoop::AcquireGIL_() {
|
||||
assert(g_base_soft && g_base_soft->InLogicThread());
|
||||
auto debug_timing{g_core->core_config().debug_timing};
|
||||
millisecs_t startms{debug_timing ? core::CorePlatform::GetCurrentMillisecs()
|
||||
@ -805,7 +807,7 @@ void EventLoop::AcquireGIL() {
|
||||
}
|
||||
}
|
||||
|
||||
void EventLoop::ReleaseGIL() {
|
||||
void EventLoop::ReleaseGIL_() {
|
||||
assert(g_base_soft && g_base_soft->InLogicThread());
|
||||
assert(py_thread_state_ == nullptr);
|
||||
py_thread_state_ = PyEval_SaveThread();
|
||||
|
||||
@ -19,7 +19,6 @@ namespace ballistica {
|
||||
|
||||
const int kThreadMessageSafetyThreshold{500};
|
||||
|
||||
// A thread with a built-in event loop.
|
||||
class EventLoop {
|
||||
public:
|
||||
explicit EventLoop(EventLoopID id,
|
||||
@ -51,7 +50,9 @@ class EventLoop {
|
||||
// rendering contexts are recreated in new threads/etc.)
|
||||
void set_thread_id(std::thread::id id) { thread_id_ = id; }
|
||||
|
||||
auto RunEventLoop(bool single_cycle = false) -> int;
|
||||
void RunToCompletion();
|
||||
void RunSingleCycle();
|
||||
|
||||
auto identifier() const -> EventLoopID { return identifier_; }
|
||||
|
||||
// Register a timer to run on the thread.
|
||||
@ -101,26 +102,55 @@ class EventLoop {
|
||||
auto paused() { return paused_; }
|
||||
|
||||
private:
|
||||
struct ThreadMessage {
|
||||
struct ThreadMessage_ {
|
||||
enum class Type { kShutdown = 999, kRunnable, kPause, kResume };
|
||||
Type type;
|
||||
union {
|
||||
Runnable* runnable{};
|
||||
};
|
||||
bool* completion_flag{};
|
||||
explicit ThreadMessage(Type type_in) : type(type_in) {}
|
||||
explicit ThreadMessage(Type type, Runnable* runnable, bool* completion_flag)
|
||||
explicit ThreadMessage_(Type type_in) : type(type_in) {}
|
||||
explicit ThreadMessage_(Type type, Runnable* runnable,
|
||||
bool* completion_flag)
|
||||
: type(type), runnable(runnable), completion_flag{completion_flag} {}
|
||||
};
|
||||
auto CheckPushRunnableSafety() -> bool;
|
||||
void SetInternalThreadName(const std::string& name);
|
||||
void WaitForNextEvent(bool single_cycle);
|
||||
void LoopUpkeep(bool once);
|
||||
void LogThreadMessageTally(
|
||||
auto CheckPushRunnableSafety_() -> bool;
|
||||
void SetInternalThreadName_(const std::string& name);
|
||||
void WaitForNextEvent_(bool single_cycle);
|
||||
void LoopUpkeep_(bool once);
|
||||
void LogThreadMessageTally_(
|
||||
std::vector<std::pair<LogLevel, std::string>>* log_entries);
|
||||
void PushLocalRunnable(Runnable* runnable, bool* completion_flag);
|
||||
void PushCrossThreadRunnable(Runnable* runnable, bool* completion_flag);
|
||||
void NotifyClientListeners();
|
||||
void PushLocalRunnable_(Runnable* runnable, bool* completion_flag);
|
||||
void PushCrossThreadRunnable_(Runnable* runnable, bool* completion_flag);
|
||||
void NotifyClientListeners_();
|
||||
void Run_(bool single_cycle);
|
||||
|
||||
// These are all exactly the same, but running different ones for
|
||||
// different threads can help identify threads in profilers, backtraces,
|
||||
// etc.
|
||||
static auto ThreadMainLogic_(void* data) -> int;
|
||||
static auto ThreadMainLogicP_(void* data) -> void*;
|
||||
static auto ThreadMainAudio_(void* data) -> int;
|
||||
static auto ThreadMainAudioP_(void* data) -> void*;
|
||||
static auto ThreadMainBGDynamics_(void* data) -> int;
|
||||
static auto ThreadMainBGDynamicsP_(void* data) -> void*;
|
||||
static auto ThreadMainNetworkWrite_(void* data) -> int;
|
||||
static auto ThreadMainNetworkWriteP_(void* data) -> void*;
|
||||
static auto ThreadMainStdInput_(void* data) -> int;
|
||||
static auto ThreadMainStdInputP_(void* data) -> void*;
|
||||
static auto ThreadMainAssets_(void* data) -> int;
|
||||
static auto ThreadMainAssetsP_(void* data) -> void*;
|
||||
|
||||
auto ThreadMain_() -> int;
|
||||
void GetThreadMessages_(std::list<ThreadMessage_>* messages);
|
||||
void PushThreadMessage_(const ThreadMessage_& t);
|
||||
|
||||
void RunPendingRunnables_();
|
||||
void RunPauseCallbacks_();
|
||||
void RunResumeCallbacks_();
|
||||
|
||||
void AcquireGIL_();
|
||||
void ReleaseGIL_();
|
||||
|
||||
bool writing_tally_{};
|
||||
bool paused_{};
|
||||
@ -140,40 +170,13 @@ class EventLoop {
|
||||
void* auto_release_pool_{};
|
||||
#endif
|
||||
|
||||
// These are all exactly the same, but running different ones for
|
||||
// different threads can help identify threads in profilers, backtraces,
|
||||
// etc.
|
||||
static auto ThreadMainLogic(void* data) -> int;
|
||||
static auto ThreadMainLogicP(void* data) -> void*;
|
||||
static auto ThreadMainAudio(void* data) -> int;
|
||||
static auto ThreadMainAudioP(void* data) -> void*;
|
||||
static auto ThreadMainBGDynamics(void* data) -> int;
|
||||
static auto ThreadMainBGDynamicsP(void* data) -> void*;
|
||||
static auto ThreadMainNetworkWrite(void* data) -> int;
|
||||
static auto ThreadMainNetworkWriteP(void* data) -> void*;
|
||||
static auto ThreadMainStdInput(void* data) -> int;
|
||||
static auto ThreadMainStdInputP(void* data) -> void*;
|
||||
static auto ThreadMainAssets(void* data) -> int;
|
||||
static auto ThreadMainAssetsP(void* data) -> void*;
|
||||
|
||||
auto ThreadMain() -> int;
|
||||
void GetThreadMessages(std::list<ThreadMessage>* messages);
|
||||
void PushThreadMessage(const ThreadMessage& t);
|
||||
|
||||
void RunPendingRunnables();
|
||||
void RunPauseCallbacks();
|
||||
void RunResumeCallbacks();
|
||||
|
||||
void AcquireGIL();
|
||||
void ReleaseGIL();
|
||||
|
||||
bool bootstrapped_{};
|
||||
std::list<std::pair<Runnable*, bool*>> runnables_;
|
||||
std::list<Runnable*> pause_callbacks_;
|
||||
std::list<Runnable*> resume_callbacks_;
|
||||
std::condition_variable thread_message_cv_;
|
||||
std::mutex thread_message_mutex_;
|
||||
std::list<ThreadMessage> thread_messages_;
|
||||
std::list<ThreadMessage_> thread_messages_;
|
||||
std::condition_variable client_listener_cv_;
|
||||
std::mutex client_listener_mutex_;
|
||||
std::list<std::vector<char>> data_to_client_;
|
||||
|
||||
@ -77,6 +77,11 @@ const char* Python::ScopedCallLabel::current_label_ = nullptr;
|
||||
|
||||
auto Python::HaveGIL() -> bool { return static_cast<bool>(PyGILState_Check()); }
|
||||
|
||||
void Python::PermanentlyReleaseGIL() {
|
||||
assert(HaveGIL());
|
||||
PyEval_SaveThread();
|
||||
}
|
||||
|
||||
void Python::PrintStackTrace() {
|
||||
bool available{};
|
||||
if (g_base_soft) {
|
||||
|
||||
@ -76,7 +76,11 @@ class Python {
|
||||
/// sanity checking that.
|
||||
static auto HaveGIL() -> bool;
|
||||
|
||||
/// Attempt to print the python stack trace.
|
||||
/// For use in specific cases when a thread exits our control. In most
|
||||
/// cases Scoped Locks/Unlocks should be used.
|
||||
static void PermanentlyReleaseGIL();
|
||||
|
||||
/// Attempt to print the Python stack trace.
|
||||
static void PrintStackTrace();
|
||||
|
||||
/// Pass any PyObject* (including nullptr) to get a readable string
|
||||
|
||||
@ -111,8 +111,9 @@ def _filter_tool_config(projroot: Path, cfg: str) -> str:
|
||||
' :jedi (:extra_paths'
|
||||
' [__EFRO_PYTHON_PATHS_Q_REL_STR__])\n'
|
||||
' :pylsp_mypy (:enabled t\n'
|
||||
' :live_mode nil\n'
|
||||
' :dmypy t))))))))\n',
|
||||
' :live_mode :json-false\n'
|
||||
' :report_progress t\n'
|
||||
' :dmypy :json-false))))))))\n',
|
||||
)
|
||||
|
||||
# Stick project-root wherever they want.
|
||||
|
||||
@ -15,7 +15,6 @@ from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
# Pull in commands we want to expose. Its more efficient to define them in
|
||||
# modules rather than inline here because we'll be able to load them via pyc.
|
||||
# pylint: disable=unused-import
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user