ripped out vestigial thread-module system and other c++ cleanup

This commit is contained in:
Eric 2022-09-09 14:49:58 -07:00
parent 38b94df9a5
commit b77feb4d0e
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
77 changed files with 782 additions and 930 deletions

View File

@ -3995,50 +3995,50 @@
"assets/src/ba_data/python/ba/_generated/__init__.py": "https://files.ballistica.net/cache/ba1/ee/e8/cad05aa531c7faf7ff7b96db7f6e", "assets/src/ba_data/python/ba/_generated/__init__.py": "https://files.ballistica.net/cache/ba1/ee/e8/cad05aa531c7faf7ff7b96db7f6e",
"assets/src/ba_data/python/ba/_generated/enums.py": "https://files.ballistica.net/cache/ba1/b2/e5/0ee0561e16257a32830645239f34", "assets/src/ba_data/python/ba/_generated/enums.py": "https://files.ballistica.net/cache/ba1/b2/e5/0ee0561e16257a32830645239f34",
"ballisticacore-windows/Generic/BallisticaCore.ico": "https://files.ballistica.net/cache/ba1/89/c0/e32c7d2a35dc9aef57cc73b0911a", "ballisticacore-windows/Generic/BallisticaCore.ico": "https://files.ballistica.net/cache/ba1/89/c0/e32c7d2a35dc9aef57cc73b0911a",
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/cb/30/c0e7d89b368751cf26b1cef50664", "build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/30/30/76f78a5f222c39ea755b5098b51f",
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/67/14/230fd06e06cb9040a03f6e74c1d7", "build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/34/fd/2d0510b7da8899c78378296e38d3",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b6/59/c8badf86a08fa213610edbcfedac", "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/5b/49/356d0f8f7fee022b778ba14d6612",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f0/13/399ce592b72284029a6291b89a0e", "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/bb/ee/7d33058363bb336fa9c52eb87207",
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/ae/4c/84fcfbec817118d204402be28de8", "build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/b4/88/1574d641b1ea03e7f6e388f614f9",
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/46/c0/9f10a2154b3ccb59a9710492c1c8", "build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/bb/09/a68b6c70115f316fc5c044286e53",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/68/9b/b68e52f40fbab1c3a8cf3a26c596", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/44/47/02f87d28d4b3129f32e6233c34f5",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/2e/f4/ab1229ec3bda80f8f47c3cf8bc62", "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/09/4f/fea5a0cd8b43fd4bfa91238c584e",
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/9d/3b/3bd29560197990be6a4da17604b8", "build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/c8/4a/769908c66c6d1eb74c8103ff5c30",
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d1/4f/9fb5889baf79de40ced767a75f47", "build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/c8/33/2067e22417e6546ebc62623a9186",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/4b/b0/7854b4f01fd1516fdb2aaabe74af", "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d1/62/eb550f8507ff920da54219ff195b",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/5d/4d/70ee88bd453725a3af8469b832a9", "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/2d/91/3b78ced38a1060fc6fe85438792c",
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/bd/df/b2fdbba76d96d5d64ae222a9cb5f", "build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/84/eb/9de1b6c883311cbd8729341599b9",
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/4b/d5/01958f073d57e11b8a7af71c88b2", "build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d3/3b/86294ecdabdce129672784085280",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/38/f0/9f544b99cd379536e7b776e726ab", "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/51/07/0037ac9ef4cea6bc3bfdb3682729",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ff/7f/96e6a7b76cf6c5a2b2cd381e4700", "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/29/e2/f84c711e2c7cc601dd5847cc60c1",
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/10/58/8dea7e5b6987346f9116d3758a6b", "build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/99/17/17a739a9a1e2f19449a5756a5c19",
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/7f/cd/93c54dc256e1b3c3575d44efb730", "build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/71/bc/e74bae5930032177abc44078e056",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/d0/d0/8d9ae932ed95175a9aae1310de96", "build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/1e/6c/5b7173be93adfc9c0e1d007cf65e",
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/d9/30/7b9bf572809afa6102afb5c396c4", "build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/bc/07/b08c86874771ee922da097fb0b06",
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/4f/62/10fba4712bc7c1649de95f6d435a", "build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8f/63/9dc4b8c56eaf6ee5259e4b60bc46",
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7f/74/101f9c7cdbea3924a3eb753372b8", "build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e3/25/692a46a2437c11055c58d95f0a10",
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3d/10/0213350b34d61574be888f434776", "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/06/8f/1f7fcebee0a16c8f843cabebba1c",
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2f/69/3195bc2ac8ab95a072deb6328383", "build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ec/c2/51a05d1066c208b3a17e8828238e",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/fb/b8/1d53882382e1bdffea589b8abd52", "build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a8/e3/88791ad2fbd654a60e6652e64554",
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/66/7d/4cf7e08113052eae664b714bdc64", "build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/6b/06/86d995f8ea1b6e27ac3b9818e267",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/0a/3e/0e759cd0a7a1e098e3c1daff0827", "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f3/7d/b632f966f8ae8c39dc9015fb8eee",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8a/a0/eb48807010d1e7fa6a4956d0967e", "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e9/9e/39dd389564d0a14f01bdb54b82ce",
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/45/1b/cd3972f7bb9cb4a7d4bfeb0977fb", "build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/cb/7e/e865aa9e850350deefa7a826b89e",
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b2/c1/92b3546f4ea454e3813062e7c2a0", "build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/9d/1f/20ea75eb2a38a5aa740aebf68893",
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d8/7f/694dfd1b8580077fd1d3ab585611", "build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/35/87/49cc3fb53218a2c19633551d0f38",
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8c/ea/38c4415b9582016f40ecb9c371f6", "build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ac/38/4fc5345b0bbc0cc6965d510b2074",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2d/54/3f42d8cbbabb633fe47dc9e63f8b", "build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b3/b0/c4f8ff30c9b62d2d0dbc926095fa",
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e0/9c/78bc9f3658e495238a9ae7095ad0", "build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ec/96/b22889b59ba3279627d8de232a23",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3e/60/48c02923c5c7e7b8a3a705cdbe49", "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/0e/6b/3b6758dd4b6b63ab9f666af9396a",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7b/cb/93116682d10e6f868ec9215c0ab2", "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/65/8c/6fa15f798f6ca73d753571f8cd4b",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/98/9b/43700428b53a552af3f32aa2577c", "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/2d/af/9cc4b56883bcd72f65b9a814c401",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/37/e2/b6da8c9dd13e91ebb705da51c653", "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/5a/18/c0ff4809d4550c794a516a320ed1",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/f5/93/774197e1f445c4dee88ed2910006", "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/27/25/4b3bd86f0ad71b2ad29ea8a9837d",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/2e/7c/139654fe813f9254ed5de42c7ef7", "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/2f/1a/29105b0e71173826d2ea404982b6",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/ef/ea/8ccb18f593d003984a1f56cf0c78", "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/b7/8a/3f0476b3981a630cc6ef8f1ec364",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/99/f6/7e56f9dd76d97493a53ebe755fba", "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/4d/32/9ffcdc35d17ac6c9e3bb2bfd0e3c",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/cf/d5/2d58578076ced4f6e2b218b18d11", "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/be/f0/8901dcd613fec9e58d02b08dce4b",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/a2/07/d85112deb0bf08aa01d18f098b94", "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/e3/c1/f79676f11b4f4778e26933ba42d7",
"src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/7d/3e/229a581cb2454ed856f1d8b564a7", "src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/7d/3e/229a581cb2454ed856f1d8b564a7",
"src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/98/12/571b2160d69d42580e8f31fa6a8d" "src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/98/12/571b2160d69d42580e8f31fa6a8d"
} }

View File

@ -1,4 +1,4 @@
### 1.7.7 (build 20778, api 7, 2022-09-06) ### 1.7.7 (build 20794, api 7, 2022-09-09)
- Added `ba.app.meta.load_exported_classes()` for loading classes discovered by the meta subsystem cleanly in a background thread. - Added `ba.app.meta.load_exported_classes()` for loading classes discovered by the meta subsystem cleanly in a background thread.
- Improved logging of missing playlist game types. - Improved logging of missing playlist game types.
- Some ba.Lstr functionality can now be used in background threads. - Some ba.Lstr functionality can now be used in background threads.

View File

@ -1 +1 @@
161826047581351482927423840323346436406 265405600297036989512577170988205019181

View File

@ -3,6 +3,9 @@
"""Bootstrapping.""" """Bootstrapping."""
from __future__ import annotations from __future__ import annotations
import os
import sys
import signal
import threading import threading
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
@ -11,6 +14,8 @@ import _ba
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, TextIO, Callable from typing import Any, TextIO, Callable
_g_did_bootstrap = False # pylint: disable=invalid-name
def bootstrap() -> None: def bootstrap() -> None:
"""Run bootstrapping logic. """Run bootstrapping logic.
@ -18,9 +23,10 @@ def bootstrap() -> None:
This is the very first userland code that runs. This is the very first userland code that runs.
It sets up low level environment bits and creates the app instance. It sets up low level environment bits and creates the app instance.
""" """
import os global _g_did_bootstrap # pylint: disable=global-statement, invalid-name
import sys if _g_did_bootstrap:
import signal raise RuntimeError('Bootstrap has already been called.')
_g_did_bootstrap = True
# The first thing we set up is capturing/redirecting Python # The first thing we set up is capturing/redirecting Python
# stdout/stderr so we can at least debug problems on systems where # stdout/stderr so we can at least debug problems on systems where
@ -32,7 +38,7 @@ def bootstrap() -> None:
# Give a soft warning if we're being used with a different binary # Give a soft warning if we're being used with a different binary
# version than we expect. # version than we expect.
expected_build = 20778 expected_build = 20794
running_build: int = env['build_number'] running_build: int = env['build_number']
if running_build != expected_build: if running_build != expected_build:
print( print(

View File

@ -241,8 +241,6 @@ add_executable(ballisticacore
${BA_SRC_ROOT}/ballistica/core/logging.h ${BA_SRC_ROOT}/ballistica/core/logging.h
${BA_SRC_ROOT}/ballistica/core/macros.cc ${BA_SRC_ROOT}/ballistica/core/macros.cc
${BA_SRC_ROOT}/ballistica/core/macros.h ${BA_SRC_ROOT}/ballistica/core/macros.h
${BA_SRC_ROOT}/ballistica/core/module.cc
${BA_SRC_ROOT}/ballistica/core/module.h
${BA_SRC_ROOT}/ballistica/core/object.cc ${BA_SRC_ROOT}/ballistica/core/object.cc
${BA_SRC_ROOT}/ballistica/core/object.h ${BA_SRC_ROOT}/ballistica/core/object.h
${BA_SRC_ROOT}/ballistica/core/thread.cc ${BA_SRC_ROOT}/ballistica/core/thread.cc

View File

@ -232,8 +232,6 @@
<ClInclude Include="..\..\src\ballistica\core\logging.h" /> <ClInclude Include="..\..\src\ballistica\core\logging.h" />
<ClCompile Include="..\..\src\ballistica\core\macros.cc" /> <ClCompile Include="..\..\src\ballistica\core\macros.cc" />
<ClInclude Include="..\..\src\ballistica\core\macros.h" /> <ClInclude Include="..\..\src\ballistica\core\macros.h" />
<ClCompile Include="..\..\src\ballistica\core\module.cc" />
<ClInclude Include="..\..\src\ballistica\core\module.h" />
<ClCompile Include="..\..\src\ballistica\core\object.cc" /> <ClCompile Include="..\..\src\ballistica\core\object.cc" />
<ClInclude Include="..\..\src\ballistica\core\object.h" /> <ClInclude Include="..\..\src\ballistica\core\object.h" />
<ClCompile Include="..\..\src\ballistica\core\thread.cc" /> <ClCompile Include="..\..\src\ballistica\core\thread.cc" />

View File

@ -130,12 +130,6 @@
<ClInclude Include="..\..\src\ballistica\core\macros.h"> <ClInclude Include="..\..\src\ballistica\core\macros.h">
<Filter>ballistica\core</Filter> <Filter>ballistica\core</Filter>
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ballistica\core\module.cc">
<Filter>ballistica\core</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\core\module.h">
<Filter>ballistica\core</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\core\object.cc"> <ClCompile Include="..\..\src\ballistica\core\object.cc">
<Filter>ballistica\core</Filter> <Filter>ballistica\core</Filter>
</ClCompile> </ClCompile>

View File

@ -227,8 +227,6 @@
<ClInclude Include="..\..\src\ballistica\core\logging.h" /> <ClInclude Include="..\..\src\ballistica\core\logging.h" />
<ClCompile Include="..\..\src\ballistica\core\macros.cc" /> <ClCompile Include="..\..\src\ballistica\core\macros.cc" />
<ClInclude Include="..\..\src\ballistica\core\macros.h" /> <ClInclude Include="..\..\src\ballistica\core\macros.h" />
<ClCompile Include="..\..\src\ballistica\core\module.cc" />
<ClInclude Include="..\..\src\ballistica\core\module.h" />
<ClCompile Include="..\..\src\ballistica\core\object.cc" /> <ClCompile Include="..\..\src\ballistica\core\object.cc" />
<ClInclude Include="..\..\src\ballistica\core\object.h" /> <ClInclude Include="..\..\src\ballistica\core\object.h" />
<ClCompile Include="..\..\src\ballistica\core\thread.cc" /> <ClCompile Include="..\..\src\ballistica\core\thread.cc" />

View File

@ -130,12 +130,6 @@
<ClInclude Include="..\..\src\ballistica\core\macros.h"> <ClInclude Include="..\..\src\ballistica\core\macros.h">
<Filter>ballistica\core</Filter> <Filter>ballistica\core</Filter>
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ballistica\core\module.cc">
<Filter>ballistica\core</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\core\module.h">
<Filter>ballistica\core</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\core\object.cc"> <ClCompile Include="..\..\src\ballistica\core\object.cc">
<Filter>ballistica\core</Filter> <Filter>ballistica\core</Filter>
</ClCompile> </ClCompile>

View File

@ -17,7 +17,7 @@
namespace ballistica { namespace ballistica {
App::App(Thread* thread) App::App(Thread* thread)
: Module("app", thread), stress_test_(std::make_unique<StressTest>()) { : thread_(thread), stress_test_(std::make_unique<StressTest>()) {
assert(g_app == nullptr); assert(g_app == nullptr);
g_app = this; g_app = this;
@ -37,8 +37,6 @@ void App::PostInit() {
g_platform->SetHardwareCursorVisible(g_buildconfig.hardware_cursor()); g_platform->SetHardwareCursorVisible(g_buildconfig.hardware_cursor());
} }
App::~App() = default;
auto App::ManagesEventLoop() const -> bool { auto App::ManagesEventLoop() const -> bool {
// We have 2 redundant values for essentially the same thing; // We have 2 redundant values for essentially the same thing;
// should get rid of IsEventPushMode() once we've created // should get rid of IsEventPushMode() once we've created
@ -104,7 +102,7 @@ void App::SetScreenResolution(float width, float height) {
} }
void App::PushShutdownCompleteCall() { void App::PushShutdownCompleteCall() {
PushCall([this] { ShutdownComplete(); }); thread()->PushCall([this] { ShutdownComplete(); });
} }
void App::ShutdownComplete() { void App::ShutdownComplete() {
@ -154,8 +152,9 @@ void App::OnPause() {
g_graphics->SetGyroEnabled(false); g_graphics->SetGyroEnabled(false);
// IMPORTANT: Any on-pause related stuff that threads need to do must // IMPORTANT: Any on-pause related stuff that threads need to do must
// be done from their HandleThreadPause(). If we push runnables to them, // be done from registered pause-callbacks. If we instead push runnables
// they may or may not be called before the thread is actually paused. // to them from here they may or may not be called before the thread
// is actually paused.
Thread::SetThreadsPaused(true); Thread::SetThreadsPaused(true);
@ -210,7 +209,7 @@ void App::OnResume() {
} }
auto App::GetProductPrice(const std::string& product) -> std::string { auto App::GetProductPrice(const std::string& product) -> std::string {
std::lock_guard<std::mutex> lock(product_prices_mutex_); std::scoped_lock lock(product_prices_mutex_);
auto i = product_prices_.find(product); auto i = product_prices_.find(product);
if (i == product_prices_.end()) { if (i == product_prices_.end()) {
return ""; return "";
@ -221,7 +220,7 @@ auto App::GetProductPrice(const std::string& product) -> std::string {
void App::SetProductPrice(const std::string& product, void App::SetProductPrice(const std::string& product,
const std::string& price) { const std::string& price) {
std::lock_guard<std::mutex> lock(product_prices_mutex_); std::scoped_lock lock(product_prices_mutex_);
product_prices_[product] = price; product_prices_[product] = price;
} }
@ -261,7 +260,7 @@ void App::PrimeEventPump() {
void App::PushShowOnlineScoreUICall(const std::string& show, void App::PushShowOnlineScoreUICall(const std::string& show,
const std::string& game, const std::string& game,
const std::string& game_version) { const std::string& game_version) {
PushCall([show, game, game_version] { thread()->PushCall([show, game, game_version] {
assert(InMainThread()); assert(InMainThread());
g_platform->ShowOnlineScoreUI(show, game, game_version); g_platform->ShowOnlineScoreUI(show, game, game_version);
}); });
@ -269,7 +268,7 @@ void App::PushShowOnlineScoreUICall(const std::string& show,
void App::PushNetworkSetupCall(int port, int telnet_port, bool enable_telnet, void App::PushNetworkSetupCall(int port, int telnet_port, bool enable_telnet,
const std::string& telnet_password) { const std::string& telnet_password) {
PushCall([port, telnet_port, enable_telnet, telnet_password] { thread()->PushCall([port, telnet_port, enable_telnet, telnet_password] {
assert(InMainThread()); assert(InMainThread());
// Kick these off if they don't exist. // Kick these off if they don't exist.
// (do we want to support changing ports on existing ones?) // (do we want to support changing ports on existing ones?)
@ -290,58 +289,59 @@ void App::PushNetworkSetupCall(int port, int telnet_port, bool enable_telnet,
void App::PushPurchaseAckCall(const std::string& purchase, void App::PushPurchaseAckCall(const std::string& purchase,
const std::string& order_id) { const std::string& order_id) {
PushCall( thread()->PushCall(
[purchase, order_id] { g_platform->PurchaseAck(purchase, order_id); }); [purchase, order_id] { g_platform->PurchaseAck(purchase, order_id); });
} }
void App::PushGetScoresToBeatCall(const std::string& level, void App::PushGetScoresToBeatCall(const std::string& level,
const std::string& config, const std::string& config,
void* py_callback) { void* py_callback) {
PushCall([level, config, py_callback] { thread()->PushCall([level, config, py_callback] {
assert(InMainThread()); assert(InMainThread());
g_platform->GetScoresToBeat(level, config, py_callback); g_platform->GetScoresToBeat(level, config, py_callback);
}); });
} }
void App::PushPurchaseCall(const std::string& item) { void App::PushPurchaseCall(const std::string& item) {
PushCall([item] { thread()->PushCall([item] {
assert(InMainThread()); assert(InMainThread());
g_platform->Purchase(item); g_platform->Purchase(item);
}); });
} }
void App::PushRestorePurchasesCall() { void App::PushRestorePurchasesCall() {
PushCall([] { thread()->PushCall([] {
assert(InMainThread()); assert(InMainThread());
g_platform->RestorePurchases(); g_platform->RestorePurchases();
}); });
} }
void App::PushOpenURLCall(const std::string& url) { void App::PushOpenURLCall(const std::string& url) {
PushCall([url] { g_platform->OpenURL(url); }); thread()->PushCall([url] { g_platform->OpenURL(url); });
} }
void App::PushGetFriendScoresCall(const std::string& game, void App::PushGetFriendScoresCall(const std::string& game,
const std::string& game_version, void* data) { const std::string& game_version, void* data) {
PushCall([game, game_version, data] { thread()->PushCall([game, game_version, data] {
g_platform->GetFriendScores(game, game_version, data); g_platform->GetFriendScores(game, game_version, data);
}); });
} }
void App::PushSubmitScoreCall(const std::string& game, void App::PushSubmitScoreCall(const std::string& game,
const std::string& game_version, int64_t score) { const std::string& game_version, int64_t score) {
PushCall([game, game_version, score] { thread()->PushCall([game, game_version, score] {
g_platform->SubmitScore(game, game_version, score); g_platform->SubmitScore(game, game_version, score);
}); });
} }
void App::PushAchievementReportCall(const std::string& achievement) { void App::PushAchievementReportCall(const std::string& achievement) {
PushCall([achievement] { g_platform->ReportAchievement(achievement); }); thread()->PushCall(
[achievement] { g_platform->ReportAchievement(achievement); });
} }
void App::PushStringEditCall(const std::string& name, const std::string& value, void App::PushStringEditCall(const std::string& name, const std::string& value,
int max_chars) { int max_chars) {
PushCall([name, value, max_chars] { thread()->PushCall([name, value, max_chars] {
static millisecs_t last_edit_time = 0; static millisecs_t last_edit_time = 0;
millisecs_t t = GetRealTime(); millisecs_t t = GetRealTime();
@ -357,13 +357,13 @@ void App::PushStringEditCall(const std::string& name, const std::string& value,
} }
void App::PushSetStressTestingCall(bool enable, int player_count) { void App::PushSetStressTestingCall(bool enable, int player_count) {
PushCall([this, enable, player_count] { thread()->PushCall([this, enable, player_count] {
stress_test_->SetStressTesting(enable, player_count); stress_test_->SetStressTesting(enable, player_count);
}); });
} }
void App::PushResetAchievementsCall() { void App::PushResetAchievementsCall() {
PushCall([] { g_platform->ResetAchievements(); }); thread()->PushCall([] { g_platform->ResetAchievements(); });
} }
void App::OnBootstrapComplete() { void App::OnBootstrapComplete() {
@ -386,7 +386,7 @@ void App::OnBootstrapComplete() {
} }
void App::PushCursorUpdate(bool vis) { void App::PushCursorUpdate(bool vis) {
PushCall([vis] { thread()->PushCall([vis] {
assert(InMainThread()); assert(InMainThread());
g_platform->SetHardwareCursorVisible(vis); g_platform->SetHardwareCursorVisible(vis);
}); });

View File

@ -8,17 +8,17 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include "ballistica/core/module.h" #include "ballistica/app/stress_test.h"
#include "ballistica/ballistica.h"
namespace ballistica { namespace ballistica {
/// Our high level app interface module. /// Our high level app interface module.
/// It runs in the main thread and is what platform wrappers /// It runs in the main thread and is what platform wrappers
/// should primarily interact with. /// should primarily interact with.
class App : public Module { class App {
public: public:
explicit App(Thread* thread); explicit App(Thread* thread);
~App() override;
/// This gets run after the constructor completes. /// This gets run after the constructor completes.
/// Any setup that may trigger a virtual method/etc. should go here. /// Any setup that may trigger a virtual method/etc. should go here.
@ -124,12 +124,14 @@ class App : public Module {
auto PushNetworkSetupCall(int port, int telnet_port, bool enable_telnet, auto PushNetworkSetupCall(int port, int telnet_port, bool enable_telnet,
const std::string& telnet_password) -> void; const std::string& telnet_password) -> void;
auto PushShutdownCompleteCall() -> void; auto PushShutdownCompleteCall() -> void;
auto thread() const -> Thread* { return thread_; }
private: private:
auto UpdatePauseResume() -> void; auto UpdatePauseResume() -> void;
auto OnPause() -> void; auto OnPause() -> void;
auto OnResume() -> void; auto OnResume() -> void;
auto ShutdownComplete() -> void; auto ShutdownComplete() -> void;
Thread* thread_{};
bool done_{}; bool done_{};
bool server_wrapper_managed_{}; bool server_wrapper_managed_{};
bool sys_paused_app_{}; bool sys_paused_app_{};

View File

@ -31,6 +31,7 @@ class AppGlobals {
/// Program argument values (on applicable platforms). /// Program argument values (on applicable platforms).
char** argv{}; char** argv{};
bool threads_paused{};
std::unordered_map<std::string, NodeType*> node_types; std::unordered_map<std::string, NodeType*> node_types;
std::unordered_map<int, NodeType*> node_types_by_id; std::unordered_map<int, NodeType*> node_types_by_id;
std::unordered_map<std::string, NodeMessageType> node_message_types; std::unordered_map<std::string, NodeMessageType> node_message_types;

View File

@ -13,10 +13,10 @@ HeadlessApp::HeadlessApp(Thread* thread) : App(thread) {
// Handle a few misc things like stress-test updates. // Handle a few misc things like stress-test updates.
// (SDL builds set up a similar timer so we need to also). // (SDL builds set up a similar timer so we need to also).
// This can probably go away at some point. // This can probably go away at some point.
NewThreadTimer(10, true, NewLambdaRunnable([this] { this->thread()->NewTimer(10, true, NewLambdaRunnable([this] {
assert(g_app); assert(g_app);
g_app->RunEvents(); g_app->RunEvents();
})); }));
} }
} // namespace ballistica } // namespace ballistica

View File

@ -3,6 +3,7 @@
#include "ballistica/app/vr_app.h" #include "ballistica/app/vr_app.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/graphics/graphics_server.h" #include "ballistica/graphics/graphics_server.h"
#include "ballistica/graphics/renderer.h" #include "ballistica/graphics/renderer.h"
@ -13,7 +14,7 @@ VRApp::VRApp(Thread* thread) : App(thread) {}
auto VRApp::PushVRSimpleRemoteStateCall(const VRSimpleRemoteState& state) auto VRApp::PushVRSimpleRemoteStateCall(const VRSimpleRemoteState& state)
-> void { -> void {
PushCall([this, state] { thread()->PushCall([this, state] {
// Convert this to a full hands state, adding in some simple elbow // Convert this to a full hands state, adding in some simple elbow
// positioning of our own and left/right. // positioning of our own and left/right.
VRHandsState s; VRHandsState s;

View File

@ -4,6 +4,7 @@
#include "ballistica/audio/audio_server.h" #include "ballistica/audio/audio_server.h"
#include "ballistica/audio/audio_source.h" #include "ballistica/audio/audio_source.h"
#include "ballistica/core/thread.h"
#include "ballistica/media/data/sound_data.h" #include "ballistica/media/data/sound_data.h"
namespace ballistica { namespace ballistica {
@ -40,11 +41,12 @@ void Audio::SetListenerOrientation(const Vector3f& forward,
// This stops a particular sound play ID only. // This stops a particular sound play ID only.
void Audio::PushSourceStopSoundCall(uint32_t play_id) { void Audio::PushSourceStopSoundCall(uint32_t play_id) {
g_audio_server->PushCall([play_id] { g_audio_server->StopSound(play_id); }); g_audio_server->thread()->PushCall(
[play_id] { g_audio_server->StopSound(play_id); });
} }
void Audio::PushSourceFadeOutCall(uint32_t play_id, uint32_t time) { void Audio::PushSourceFadeOutCall(uint32_t play_id, uint32_t time) {
g_audio_server->PushCall( g_audio_server->thread()->PushCall(
[play_id, time] { g_audio_server->FadeSoundOut(play_id, time); }); [play_id, time] { g_audio_server->FadeSoundOut(play_id, time); });
} }
@ -60,7 +62,7 @@ auto Audio::SourceBeginNew() -> AudioSource* {
// Got to make sure to hold this until we've locked the source. // Got to make sure to hold this until we've locked the source.
// Otherwise, theoretically, the audio thread could make our source // Otherwise, theoretically, the audio thread could make our source
// available again before we can use it. // available again before we can use it.
std::lock_guard<std::mutex> lock(available_sources_mutex_); std::lock_guard lock(available_sources_mutex_);
// If there's an available source, reserve and return it. // If there's an available source, reserve and return it.
auto i = available_sources_.begin(); auto i = available_sources_.begin();

View File

@ -7,7 +7,6 @@
#include <mutex> #include <mutex>
#include <vector> #include <vector>
#include "ballistica/core/module.h"
#include "ballistica/core/object.h" #include "ballistica/core/object.h"
namespace ballistica { namespace ballistica {

View File

@ -8,6 +8,7 @@
#include "ballistica/audio/audio_source.h" #include "ballistica/audio/audio_source.h"
#include "ballistica/audio/audio_streamer.h" #include "ballistica/audio/audio_streamer.h"
#include "ballistica/audio/ogg_stream.h" #include "ballistica/audio/ogg_stream.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/generic/timer.h" #include "ballistica/generic/timer.h"
#include "ballistica/math/vector3f.h" #include "ballistica/math/vector3f.h"
@ -195,7 +196,7 @@ void AudioServer::SetPaused(bool pause) {
} }
void AudioServer::PushSourceSetIsMusicCall(uint32_t play_id, bool val) { void AudioServer::PushSourceSetIsMusicCall(uint32_t play_id, bool val) {
PushCall([this, play_id, val] { thread()->PushCall([this, play_id, val] {
ThreadSource* s = GetPlayingSound(play_id); ThreadSource* s = GetPlayingSound(play_id);
if (s) { if (s) {
s->SetIsMusic(val); s->SetIsMusic(val);
@ -204,7 +205,7 @@ void AudioServer::PushSourceSetIsMusicCall(uint32_t play_id, bool val) {
} }
void AudioServer::PushSourceSetPositionalCall(uint32_t play_id, bool val) { void AudioServer::PushSourceSetPositionalCall(uint32_t play_id, bool val) {
PushCall([this, play_id, val] { thread()->PushCall([this, play_id, val] {
ThreadSource* s = GetPlayingSound(play_id); ThreadSource* s = GetPlayingSound(play_id);
if (s) { if (s) {
s->SetPositional(val); s->SetPositional(val);
@ -214,7 +215,7 @@ void AudioServer::PushSourceSetPositionalCall(uint32_t play_id, bool val) {
void AudioServer::PushSourceSetPositionCall(uint32_t play_id, void AudioServer::PushSourceSetPositionCall(uint32_t play_id,
const Vector3f& p) { const Vector3f& p) {
PushCall([this, play_id, p] { thread()->PushCall([this, play_id, p] {
ThreadSource* s = GetPlayingSound(play_id); ThreadSource* s = GetPlayingSound(play_id);
if (s) { if (s) {
s->SetPosition(p.x, p.y, p.z); s->SetPosition(p.x, p.y, p.z);
@ -223,7 +224,7 @@ void AudioServer::PushSourceSetPositionCall(uint32_t play_id,
} }
void AudioServer::PushSourceSetGainCall(uint32_t play_id, float val) { void AudioServer::PushSourceSetGainCall(uint32_t play_id, float val) {
PushCall([this, play_id, val] { thread()->PushCall([this, play_id, val] {
ThreadSource* s = GetPlayingSound(play_id); ThreadSource* s = GetPlayingSound(play_id);
if (s) { if (s) {
s->SetGain(val); s->SetGain(val);
@ -232,7 +233,7 @@ void AudioServer::PushSourceSetGainCall(uint32_t play_id, float val) {
} }
void AudioServer::PushSourceSetFadeCall(uint32_t play_id, float val) { void AudioServer::PushSourceSetFadeCall(uint32_t play_id, float val) {
PushCall([this, play_id, val] { thread()->PushCall([this, play_id, val] {
ThreadSource* s = GetPlayingSound(play_id); ThreadSource* s = GetPlayingSound(play_id);
if (s) { if (s) {
s->SetFade(val); s->SetFade(val);
@ -241,7 +242,7 @@ void AudioServer::PushSourceSetFadeCall(uint32_t play_id, float val) {
} }
void AudioServer::PushSourceSetLoopingCall(uint32_t play_id, bool val) { void AudioServer::PushSourceSetLoopingCall(uint32_t play_id, bool val) {
PushCall([this, play_id, val] { thread()->PushCall([this, play_id, val] {
ThreadSource* s = GetPlayingSound(play_id); ThreadSource* s = GetPlayingSound(play_id);
if (s) { if (s) {
s->SetLooping(val); s->SetLooping(val);
@ -251,7 +252,7 @@ void AudioServer::PushSourceSetLoopingCall(uint32_t play_id, bool val) {
void AudioServer::PushSourcePlayCall(uint32_t play_id, void AudioServer::PushSourcePlayCall(uint32_t play_id,
Object::Ref<SoundData>* sound) { Object::Ref<SoundData>* sound) {
PushCall([this, play_id, sound] { thread()->PushCall([this, play_id, sound] {
ThreadSource* s = GetPlayingSound(play_id); ThreadSource* s = GetPlayingSound(play_id);
// If this play command is valid, pass it along. // If this play command is valid, pass it along.
@ -271,7 +272,7 @@ void AudioServer::PushSourcePlayCall(uint32_t play_id,
} }
void AudioServer::PushSourceStopCall(uint32_t play_id) { void AudioServer::PushSourceStopCall(uint32_t play_id) {
PushCall([this, play_id] { thread()->PushCall([this, play_id] {
ThreadSource* s = GetPlayingSound(play_id); ThreadSource* s = GetPlayingSound(play_id);
if (s) { if (s) {
s->Stop(); s->Stop();
@ -280,7 +281,7 @@ void AudioServer::PushSourceStopCall(uint32_t play_id) {
} }
void AudioServer::PushSourceEndCall(uint32_t play_id) { void AudioServer::PushSourceEndCall(uint32_t play_id) {
PushCall([this, play_id] { thread()->PushCall([this, play_id] {
ThreadSource* s = GetPlayingSound(play_id); ThreadSource* s = GetPlayingSound(play_id);
assert(s); assert(s);
s->client_source()->Lock(5); s->client_source()->Lock(5);
@ -292,11 +293,11 @@ void AudioServer::PushSourceEndCall(uint32_t play_id) {
} }
void AudioServer::PushResetCall() { void AudioServer::PushResetCall() {
PushCall([this] { Reset(); }); thread()->PushCall([this] { Reset(); });
} }
void AudioServer::PushSetListenerPositionCall(const Vector3f& p) { void AudioServer::PushSetListenerPositionCall(const Vector3f& p) {
PushCall([this, p] { thread()->PushCall([this, p] {
#if BA_ENABLE_AUDIO #if BA_ENABLE_AUDIO
if (!paused_) { if (!paused_) {
ALfloat lpos[3] = {p.x, p.y, p.z}; ALfloat lpos[3] = {p.x, p.y, p.z};
@ -309,7 +310,7 @@ void AudioServer::PushSetListenerPositionCall(const Vector3f& p) {
void AudioServer::PushSetListenerOrientationCall(const Vector3f& forward, void AudioServer::PushSetListenerOrientationCall(const Vector3f& forward,
const Vector3f& up) { const Vector3f& up) {
PushCall([this, forward, up] { thread()->PushCall([this, forward, up] {
#if BA_ENABLE_AUDIO #if BA_ENABLE_AUDIO
if (!paused_) { if (!paused_) {
ALfloat lorient[6] = {forward.x, forward.y, forward.z, up.x, up.y, up.z}; ALfloat lorient[6] = {forward.x, forward.y, forward.z, up.x, up.y, up.z};
@ -321,17 +322,17 @@ void AudioServer::PushSetListenerOrientationCall(const Vector3f& forward,
} }
AudioServer::AudioServer(Thread* thread) AudioServer::AudioServer(Thread* thread)
: Module("audio", thread), : thread_(thread), impl_{new AudioServer::Impl()} {
impl_{new AudioServer::Impl()}
// impl_{std::make_unique<AudioServer::Impl>()}
{
// we're a singleton... // we're a singleton...
assert(g_audio_server == nullptr); assert(g_audio_server == nullptr);
g_audio_server = this; g_audio_server = this;
thread->AddPauseCallback(NewLambdaRunnableRaw([this] { OnThreadPause(); }));
thread->AddResumeCallback(NewLambdaRunnableRaw([this] { OnThreadResume(); }));
// Get our thread to give us periodic processing time. // Get our thread to give us periodic processing time.
process_timer_ = NewThreadTimer(kAudioProcessIntervalNormal, true, process_timer_ = thread->NewTimer(kAudioProcessIntervalNormal, true,
NewLambdaRunnable([this] { Process(); })); NewLambdaRunnable([this] { Process(); }));
#if BA_ENABLE_AUDIO #if BA_ENABLE_AUDIO
@ -782,8 +783,7 @@ void AudioServer::ThreadSource::UpdateAvailability() {
if (!busy) { if (!busy) {
if (g_audio->available_sources_mutex().try_lock()) { if (g_audio->available_sources_mutex().try_lock()) {
std::lock_guard<std::mutex> lock(g_audio->available_sources_mutex(), std::lock_guard lock(g_audio->available_sources_mutex(), std::adopt_lock);
std::adopt_lock);
Stop(); Stop();
Reset(); Reset();
#if BA_DEBUG_BUILD #if BA_DEBUG_BUILD
@ -1074,18 +1074,18 @@ void AudioServer::ThreadSource::UpdatePitch() {
} }
void AudioServer::PushSetVolumesCall(float music_volume, float sound_volume) { void AudioServer::PushSetVolumesCall(float music_volume, float sound_volume) {
PushCall([this, music_volume, sound_volume] { thread()->PushCall([this, music_volume, sound_volume] {
SetSoundVolume(sound_volume); SetSoundVolume(sound_volume);
SetMusicVolume(music_volume); SetMusicVolume(music_volume);
}); });
} }
void AudioServer::PushSetSoundPitchCall(float val) { void AudioServer::PushSetSoundPitchCall(float val) {
PushCall([this, val] { SetSoundPitch(val); }); thread()->PushCall([this, val] { SetSoundPitch(val); });
} }
void AudioServer::PushSetPausedCall(bool pause) { void AudioServer::PushSetPausedCall(bool pause) {
PushCall([this, pause] { thread()->PushCall([this, pause] {
if (g_buildconfig.ostype_android()) { if (g_buildconfig.ostype_android()) {
Log("Error: Shouldn't be getting SetPausedCall on android."); Log("Error: Shouldn't be getting SetPausedCall on android.");
} }
@ -1095,7 +1095,7 @@ void AudioServer::PushSetPausedCall(bool pause) {
void AudioServer::PushComponentUnloadCall( void AudioServer::PushComponentUnloadCall(
const std::vector<Object::Ref<MediaComponentData>*>& components) { const std::vector<Object::Ref<MediaComponentData>*>& components) {
PushCall([this, components] { thread()->PushCall([this, components] {
// Unload all components we were passed... // Unload all components we were passed...
for (auto&& i : components) { for (auto&& i : components) {
(**i).Unload(); (**i).Unload();
@ -1107,7 +1107,7 @@ void AudioServer::PushComponentUnloadCall(
} }
void AudioServer::PushHavePendingLoadsCall() { void AudioServer::PushHavePendingLoadsCall() {
PushCall([this] { thread()->PushCall([this] {
have_pending_loads_ = true; have_pending_loads_ = true;
UpdateTimerInterval(); UpdateTimerInterval();
}); });
@ -1115,16 +1115,16 @@ void AudioServer::PushHavePendingLoadsCall() {
void AudioServer::AddSoundRefDelete(const Object::Ref<SoundData>* c) { void AudioServer::AddSoundRefDelete(const Object::Ref<SoundData>* c) {
{ {
std::lock_guard<std::mutex> lock(sound_ref_delete_list_mutex_); std::scoped_lock lock(sound_ref_delete_list_mutex_);
sound_ref_delete_list_.push_back(c); sound_ref_delete_list_.push_back(c);
} }
// Now push a call to the game thread to do the deletes. // Now push a call to the game thread to do the deletes.
g_game->PushCall([] { g_audio_server->ClearSoundRefDeleteList(); }); g_game->thread()->PushCall([] { g_audio_server->ClearSoundRefDeleteList(); });
} }
void AudioServer::ClearSoundRefDeleteList() { void AudioServer::ClearSoundRefDeleteList() {
assert(InLogicThread()); assert(InLogicThread());
std::lock_guard<std::mutex> lock(sound_ref_delete_list_mutex_); std::scoped_lock lock(sound_ref_delete_list_mutex_);
for (const Object::Ref<SoundData>* i : sound_ref_delete_list_) { for (const Object::Ref<SoundData>* i : sound_ref_delete_list_) {
delete i; delete i;
} }
@ -1149,9 +1149,9 @@ void AudioServer::BeginInterruption() {
} }
} }
void AudioServer::HandleThreadPause() { SetPaused(true); } auto AudioServer::OnThreadPause() -> void { SetPaused(true); }
void AudioServer::HandleThreadResume() { SetPaused(false); } auto AudioServer::OnThreadResume() -> void { SetPaused(false); }
void AudioServer::EndInterruption() { void AudioServer::EndInterruption() {
assert(!InAudioThread()); assert(!InAudioThread());

View File

@ -4,14 +4,15 @@
#define BALLISTICA_AUDIO_AUDIO_SERVER_H_ #define BALLISTICA_AUDIO_AUDIO_SERVER_H_
#include <map> #include <map>
#include <mutex>
#include <vector> #include <vector>
#include "ballistica/core/module.h" #include "ballistica/core/object.h"
namespace ballistica { namespace ballistica {
/// A module that handles audio processing. /// A module that handles audio processing.
class AudioServer : public Module { class AudioServer {
public: public:
static auto source_id_from_play_id(uint32_t play_id) -> uint32_t { static auto source_id_from_play_id(uint32_t play_id) -> uint32_t {
return play_id & 0xFFFFu; return play_id & 0xFFFFu;
@ -27,9 +28,6 @@ class AudioServer : public Module {
void PushSetSoundPitchCall(float val); void PushSetSoundPitchCall(float val);
void PushSetPausedCall(bool pause); void PushSetPausedCall(bool pause);
void HandleThreadPause() override;
void HandleThreadResume() override;
static void BeginInterruption(); static void BeginInterruption();
static void EndInterruption(); static void EndInterruption();
@ -64,11 +62,16 @@ class AudioServer : public Module {
// Stop a sound from playing if it exists. // Stop a sound from playing if it exists.
void StopSound(uint32_t play_id); void StopSound(uint32_t play_id);
auto thread() const -> Thread* { return thread_; }
private: private:
class ThreadSource; class ThreadSource;
struct Impl; struct Impl;
~AudioServer() override; ~AudioServer();
auto OnThreadPause() -> void;
auto OnThreadResume() -> void;
void SetPaused(bool paused); void SetPaused(bool paused);
@ -104,6 +107,7 @@ class AudioServer : public Module {
// std::unique_ptr<Impl> impl_{}; // std::unique_ptr<Impl> impl_{};
Impl* impl_{}; Impl* impl_{};
Thread* thread_{};
Timer* process_timer_{}; Timer* process_timer_{};
bool have_pending_loads_{}; bool have_pending_loads_{};
bool paused_{}; bool paused_{};

View File

@ -22,7 +22,7 @@
namespace ballistica { namespace ballistica {
// These are set automatically via script; don't modify them here. // These are set automatically via script; don't modify them here.
const int kAppBuildNumber = 20778; const int kAppBuildNumber = 20794;
const char* kAppVersion = "1.7.7"; const char* kAppVersion = "1.7.7";
// Our standalone globals. // Our standalone globals.
@ -87,7 +87,7 @@ auto BallisticaMain(int argc, char** argv) -> int {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
g_app_globals = new AppGlobals(argc, argv); g_app_globals = new AppGlobals(argc, argv);
g_app_internal = CreateAppInternal(); g_app_internal = GetAppInternal();
g_platform = Platform::Create(); g_platform = Platform::Create();
g_account = new Account(); g_account = new Account();
g_utils = new Utils(); g_utils = new Utils();
@ -109,15 +109,21 @@ auto BallisticaMain(int argc, char** argv) -> int {
auto* network_write_thread = new Thread(ThreadIdentifier::kNetworkWrite); auto* network_write_thread = new Thread(ThreadIdentifier::kNetworkWrite);
g_app_globals->pausable_threads.push_back(network_write_thread); g_app_globals->pausable_threads.push_back(network_write_thread);
// And add our other standard modules to them. // Spin up our subsystems in those threads.
logic_thread->AddModule<Game>(); logic_thread->PushCallSynchronous(
network_write_thread->AddModule<NetworkWriteModule>(); [logic_thread] { new Game(logic_thread); });
media_thread->AddModule<MediaServer>(); network_write_thread->PushCallSynchronous([network_write_thread] {
g_main_thread->AddModule<GraphicsServer>(); new NetworkWriteModule(network_write_thread);
audio_thread->AddModule<AudioServer>(); });
media_thread->PushCallSynchronous(
[media_thread] { new MediaServer(media_thread); });
new GraphicsServer(g_main_thread);
audio_thread->PushCallSynchronous(
[audio_thread] { new AudioServer(audio_thread); });
// Now let the platform spin up any other threads/modules it uses. // Now let the platform spin up any other threads/modules it uses.
// (bg-dynamics in non-headless builds, stdin/stdout where applicable, etc.) // (bg-dynamics in non-headless builds, stdin/stdout where applicable,
// etc.)
g_platform->CreateAuxiliaryModules(); g_platform->CreateAuxiliaryModules();
// Ok at this point we can be considered up-and-running. // Ok at this point we can be considered up-and-running.
@ -148,8 +154,8 @@ auto BallisticaMain(int argc, char** argv) -> int {
// In this case we'll now simply return and let the OS feed us events // In this case we'll now simply return and let the OS feed us events
// until the app quits. // until the app quits.
// However, we may need to 'prime the pump' first. For instance, // However, we may need to 'prime the pump' first. For instance,
// if the main thread event loop is driven by frame draws, it may need to // if the main thread event loop is driven by frame draws, it may need
// manually pump events until drawing begins (otherwise it will never // to manually pump events until drawing begins (otherwise it will never
// process the 'create-screen' event and wind up deadlocked). // process the 'create-screen' event and wind up deadlocked).
g_app->PrimeEventPump(); g_app->PrimeEventPump();
} }
@ -184,7 +190,7 @@ auto GetRealTime() -> millisecs_t {
// If we're at a different time than our last query, do our funky math. // If we're at a different time than our last query, do our funky math.
if (t != g_app_globals->last_real_time_ticks) { if (t != g_app_globals->last_real_time_ticks) {
std::lock_guard<std::mutex> lock(g_app_globals->real_time_mutex); std::scoped_lock lock(g_app_globals->real_time_mutex);
millisecs_t passed = t - g_app_globals->last_real_time_ticks; millisecs_t passed = t - g_app_globals->last_real_time_ticks;
// GetTicks() is supposed to be monotonic, but I've seen 'passed' // GetTicks() is supposed to be monotonic, but I've seen 'passed'

View File

@ -4,6 +4,7 @@
#include "ballistica/app/app.h" #include "ballistica/app/app.h"
#include "ballistica/core/logging.h" #include "ballistica/core/logging.h"
#include "ballistica/core/thread.h"
#include "ballistica/internal/app_internal.h" #include "ballistica/internal/app_internal.h"
#include "ballistica/platform/platform.h" #include "ballistica/platform/platform.h"
@ -109,7 +110,7 @@ auto FatalError::DoBlockingFatalErrorDialog(const std::string& message)
bool finished{}; bool finished{};
bool* startedptr{&started}; bool* startedptr{&started};
bool* finishedptr{&finished}; bool* finishedptr{&finished};
g_app->PushCall([message, startedptr, finishedptr] { g_app->thread()->PushCall([message, startedptr, finishedptr] {
*startedptr = true; *startedptr = true;
g_platform->BlockingFatalErrorDialog(message); g_platform->BlockingFatalErrorDialog(message);
*finishedptr = true; *finishedptr = true;

View File

@ -69,7 +69,7 @@ void Logging::Log(const std::string& msg, bool to_stdout, bool to_server) {
// Add to our complete log. // Add to our complete log.
if (g_app_globals != nullptr) { if (g_app_globals != nullptr) {
std::lock_guard<std::mutex> lock(g_app_globals->log_mutex); std::scoped_lock lock(g_app_globals->log_mutex);
if (!g_app_globals->log_full) { if (!g_app_globals->log_full) {
(g_app_globals->log) += (msg + "\n"); (g_app_globals->log) += (msg + "\n");
if ((g_app_globals->log).size() > 10000) { if ((g_app_globals->log).size() > 10000) {

View File

@ -1,58 +0,0 @@
// Released under the MIT License. See LICENSE for details.
#include "ballistica/core/module.h"
#include "ballistica/core/thread.h"
namespace ballistica {
void Module::PushLocalRunnable(Runnable* runnable) {
assert(std::this_thread::get_id() == thread()->thread_id());
runnables_.push_back(runnable);
}
void Module::PushRunnable(Runnable* runnable) {
// If we're being called from the module's thread, just drop it in the list.
// otherwise send it as a message to the other thread.
if (std::this_thread::get_id() == thread()->thread_id()) {
PushLocalRunnable(runnable);
} else {
thread_->PushModuleRunnable(runnable, id_);
}
}
auto Module::CheckPushSafety() -> bool {
if (std::this_thread::get_id() == thread()->thread_id()) {
// behave the same as the thread-message safety check for
// module messages.
return (runnables_.size() < kThreadMessageSafetyThreshold);
} else {
return thread_->CheckPushModuleRunnableSafety();
}
}
Module::Module(std::string name_in, Thread* thread_in)
: thread_(thread_in), name_(std::move(name_in)) {
id_ = thread_->RegisterModule(name_, this);
}
Module::~Module() = default;
auto Module::NewThreadTimer(millisecs_t length, bool repeat,
const Object::Ref<Runnable>& runnable) -> Timer* {
return thread_->NewTimer(length, repeat, runnable);
}
void Module::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()->thread_id());
std::list<Runnable*> runnables;
runnables_.swap(runnables);
for (Runnable* i : runnables) {
i->Run();
delete i;
}
}
} // namespace ballistica

View File

@ -1,78 +0,0 @@
// Released under the MIT License. See LICENSE for details.
#ifndef BALLISTICA_CORE_MODULE_H_
#define BALLISTICA_CORE_MODULE_H_
#include <list>
#include <mutex>
#include <string>
#include <vector>
#include "ballistica/generic/lambda_runnable.h"
#include "ballistica/generic/runnable.h"
namespace ballistica {
/// A logical entity that can be added to a thread and make use of its
/// event loop.
class Module {
public:
/// Add a runnable to this module's queue.
/// Pass a Runnable that has been allocated with new().
/// There must be no existing strong refs to it.
/// It will be owned and disposed of by the module from this point.
auto PushRunnable(Runnable* runnable) -> void;
/// Convenience function to push a lambda as a runnable.
template <typename F>
auto PushCall(const F& lambda) -> void {
PushRunnable(NewLambdaRunnableRaw(lambda));
}
/// Returns true if there is plenty of buffer space available for
/// PushCall/PushRunnable; can be used to avoid buffer-full errors
/// by discarding non-essential calls. An example is calls scheduled
/// due to receiving unreliable network packets; without watching
/// buffer space it can be possible for an attacker to bring down
/// the app through a flood of packets.
auto CheckPushSafety() -> bool;
/// Return the thread this module is running on.
auto thread() const -> Thread* { return thread_; }
virtual ~Module();
/// Push a runnable from the same thread as the module.
auto PushLocalRunnable(Runnable* runnable) -> void;
/// Called for each module when its thread is about to be suspended
/// (on platforms such as mobile).
virtual auto HandleThreadPause() -> void {}
/// Called for each module when its thread is about to be resumed
/// (on platforms such as mobile).
virtual auto HandleThreadResume() -> void {}
/// Whether this module has pending runnables.
auto has_pending_runnables() const -> bool { return !runnables_.empty(); }
/// Used by the module's owner thread to let it do its thing.
auto RunPendingRunnables() -> void;
auto name() const -> const std::string& { return name_; }
protected:
Module(std::string name, Thread* thread);
auto NewThreadTimer(millisecs_t length, bool repeat,
const Object::Ref<Runnable>& runnable) -> Timer*;
private:
std::string name_;
int id_{};
std::list<Runnable*> runnables_;
Thread* thread_{};
};
} // namespace ballistica
#endif // BALLISTICA_CORE_MODULE_H_

View File

@ -15,7 +15,7 @@ void Object::PrintObjects() {
#if BA_DEBUG_BUILD #if BA_DEBUG_BUILD
std::string s; std::string s;
{ {
std::lock_guard<std::mutex> lock(g_app_globals->object_list_mutex); std::scoped_lock lock(g_app_globals->object_list_mutex);
s = std::to_string(g_app_globals->object_count) + " Objects at time " s = std::to_string(g_app_globals->object_count) + " Objects at time "
+ std::to_string(GetRealTime()) + ";"; + std::to_string(GetRealTime()) + ";";
@ -66,7 +66,7 @@ Object::Object() {
object_birth_time_ = GetRealTime(); object_birth_time_ = GetRealTime();
// Add ourself to the global object list. // Add ourself to the global object list.
std::lock_guard<std::mutex> lock(g_app_globals->object_list_mutex); std::scoped_lock lock(g_app_globals->object_list_mutex);
object_prev_ = nullptr; object_prev_ = nullptr;
object_next_ = g_app_globals->object_list_first; object_next_ = g_app_globals->object_list_first;
g_app_globals->object_list_first = this; g_app_globals->object_list_first = this;
@ -80,7 +80,7 @@ Object::Object() {
Object::~Object() { Object::~Object() {
#if BA_DEBUG_BUILD #if BA_DEBUG_BUILD
// Pull ourself from the global obj list. // Pull ourself from the global obj list.
std::lock_guard<std::mutex> lock(g_app_globals->object_list_mutex); std::scoped_lock lock(g_app_globals->object_list_mutex);
if (object_next_) { if (object_next_) {
object_next_->object_prev_ = object_prev_; object_next_->object_prev_ = object_prev_;
} }
@ -96,20 +96,19 @@ Object::~Object() {
// Avoiding Log for these low level errors; can lead to deadlock. // Avoiding Log for these low level errors; can lead to deadlock.
printf( printf(
"Warning: Object is dying with non-zero ref-count; this is bad. " "Warning: Object is dying with non-zero ref-count; this is bad. "
"(this " "(this might mean the object raised an exception in its constructor"
"might mean the object raised an exception in its constructor after " " after being strong-referenced first).\n");
"being strong-referenced first).\n");
} }
#endif // BA_DEBUG_BUILD #endif // BA_DEBUG_BUILD
// Invalidate all our weak refs. // Invalidate all our weak refs.
// We could call Release() on each but we'd have to deactivate the // We could call Release() on each but we'd have to deactivate the
// thread-check since virtual functions won't work right in a destructor. // thread-check since virtual functions won't work as expected in a
// Also we can take a few shortcuts here since we know we're deleting the // destructor. Also we can take a few shortcuts here since we know
// entire list, not just one object. // we're deleting the entire list, not just one object.
while (object_weak_refs_) { while (object_weak_refs_) {
auto tmp = object_weak_refs_; auto tmp{object_weak_refs_};
object_weak_refs_ = tmp->next_; object_weak_refs_ = tmp->next_;
tmp->prev_ = nullptr; tmp->prev_ = nullptr;
tmp->next_ = nullptr; tmp->next_ = nullptr;
@ -127,19 +126,19 @@ auto Object::GetObjectDescription() const -> std::string {
+ ">"; + ">";
} }
auto Object::GetDefaultOwnerThread() const -> ThreadIdentifier {
return ThreadIdentifier::kLogic;
}
auto Object::GetThreadOwnership() const -> Object::ThreadOwnership { auto Object::GetThreadOwnership() const -> Object::ThreadOwnership {
#if BA_DEBUG_BUILD #if BA_DEBUG_BUILD
return thread_ownership_; return thread_ownership_;
#else #else
// Not used in release build so doesn't matter. FatalError("Should not be called in release builds.");
return ThreadOwnership::kAny; return ThreadOwnership::kClassDefault;
#endif #endif
} }
auto Object::GetDefaultOwnerThread() const -> ThreadIdentifier {
return ThreadIdentifier::kLogic;
}
#if BA_DEBUG_BUILD #if BA_DEBUG_BUILD
static auto GetCurrentThreadIdentifier() -> ThreadIdentifier { static auto GetCurrentThreadIdentifier() -> ThreadIdentifier {
@ -161,22 +160,23 @@ static auto GetCurrentThreadIdentifier() -> ThreadIdentifier {
} }
} }
void Object::ObjectThreadCheck() { auto Object::ObjectUpdateForAcquire() -> void {
ThreadOwnership thread_ownership = GetThreadOwnership();
// If we're set to use the next-referencing thread and haven't set one
// yet, do so.
if (thread_ownership == ThreadOwnership::kNextReferencing
&& owner_thread_ == ThreadIdentifier::kInvalid) {
owner_thread_ = GetCurrentThreadIdentifier();
}
}
auto Object::ObjectThreadCheck() -> void {
if (!thread_checks_enabled_) { if (!thread_checks_enabled_) {
return; return;
} }
ThreadOwnership thread_ownership = GetThreadOwnership(); ThreadOwnership thread_ownership = GetThreadOwnership();
if (thread_ownership == ThreadOwnership::kAny) {
return;
}
// If we're set to use the next-referencing thread
// and haven't set that yet, do so.
if (thread_ownership == ThreadOwnership::kNextReferencing
&& owner_thread_ == ThreadIdentifier::kInvalid) {
owner_thread_ = GetCurrentThreadIdentifier();
}
ThreadIdentifier t; ThreadIdentifier t;
if (thread_ownership == ThreadOwnership::kClassDefault) { if (thread_ownership == ThreadOwnership::kClassDefault) {

View File

@ -34,23 +34,23 @@ class Object {
// type-name plus address. // type-name plus address.
virtual auto GetObjectDescription() const -> std::string; virtual auto GetObjectDescription() const -> std::string;
// This is called when adding or removing a reference to an Object;
// it can perform sanity-tests to make sure references are not being
// added at incorrect times or from incorrect threads.
// The default implementation uses the per-object
// ThreadOwnership/ThreadIdentifier values accessible below. NOTE: this
// check runs only in the debug build so don't add any logical side-effects!
#if BA_DEBUG_BUILD
virtual void ObjectThreadCheck();
#endif
enum class ThreadOwnership { enum class ThreadOwnership {
kClassDefault, // Uses class' GetDefaultOwnerThread() call. kClassDefault, // Uses class' GetDefaultOwnerThread() call.
kNextReferencing, // Uses whichever thread next acquires/accesses a ref. kNextReferencing // Uses whichever thread next acquires/accesses a ref.
kCustom, // Always use a specific thread.
kAny // Any thread is fine.
}; };
#if BA_DEBUG_BUILD
/// This is called when adding or removing a reference to an Object;
/// it can perform sanity-tests to make sure references are not being
/// added at incorrect times or from incorrect threads.
/// The default implementation uses the per-object
/// ThreadOwnership/ThreadIdentifier values accessible below. NOTE: this
/// check runs only in the debug build so don't add any logical side-effects!
virtual void ObjectThreadCheck();
#endif
/// Called by the default ObjectThreadCheck() to determine ThreadOwnership /// Called by the default ObjectThreadCheck() to determine ThreadOwnership
/// for an Object. The default uses the object's individual value /// for an Object. The default uses the object's individual value
/// (which defaults to ThreadOwnership::kClassDefault and can be set via /// (which defaults to ThreadOwnership::kClassDefault and can be set via
@ -62,21 +62,12 @@ class Object {
/// Default returns ThreadIdentifier::kLogic /// Default returns ThreadIdentifier::kLogic
virtual auto GetDefaultOwnerThread() const -> ThreadIdentifier; virtual auto GetDefaultOwnerThread() const -> ThreadIdentifier;
/// Set thread ownership values for an individual object. /// Set thread ownership for an individual object.
/// Note that these values may be ignored if ObjectThreadCheck() is void SetThreadOwnership(ThreadOwnership ownership) {
/// overridden, and thread_identifier is only relevant when ownership is
/// ThreadOwnership::kCustom.
/// UPDATE: turning off per-object controls; gonna see if we can get by
/// with just set_thread_checks_enabled() for temp special cases...
void SetThreadOwnership(
ThreadOwnership ownership,
ThreadIdentifier thread_identifier = ThreadIdentifier::kLogic) {
#if BA_DEBUG_BUILD #if BA_DEBUG_BUILD
thread_ownership_ = ownership; thread_ownership_ = ownership;
if (thread_ownership_ == ThreadOwnership::kNextReferencing) { if (thread_ownership_ == ThreadOwnership::kNextReferencing) {
owner_thread_ = ThreadIdentifier::kInvalid; owner_thread_ = ThreadIdentifier::kInvalid;
} else {
owner_thread_ = thread_identifier;
} }
#endif #endif
} }
@ -128,9 +119,9 @@ class Object {
} }
private: private:
Object* obj_ = nullptr; Object* obj_{};
WeakRefBase* prev_ = nullptr; WeakRefBase* prev_{};
WeakRefBase* next_ = nullptr; WeakRefBase* next_{};
friend class Object; friend class Object;
}; // WeakRefBase }; // WeakRefBase
@ -278,8 +269,8 @@ class Object {
if (obj == nullptr) { if (obj == nullptr) {
throw Exception("Acquiring invalid ptr of " + static_type_name<T>()); throw Exception("Acquiring invalid ptr of " + static_type_name<T>());
} }
#if BA_DEBUG_BUILD
#if BA_DEBUG_BUILD
// Seems like it'd be a good idea to prevent creation of weak-refs to // Seems like it'd be a good idea to prevent creation of weak-refs to
// objects in their destructors, but it turns out we're currently // objects in their destructors, but it turns out we're currently
// doing this (session points contexts at itself as it dies, etc.) // doing this (session points contexts at itself as it dies, etc.)
@ -287,13 +278,14 @@ class Object {
obj->ObjectThreadCheck(); obj->ObjectThreadCheck();
assert(obj_ == nullptr && next_ == nullptr && prev_ == nullptr); assert(obj_ == nullptr && next_ == nullptr && prev_ == nullptr);
#endif #endif
if (obj->object_weak_refs_) { if (obj->object_weak_refs_) {
obj->object_weak_refs_->prev_ = this; obj->object_weak_refs_->prev_ = this;
next_ = obj->object_weak_refs_; next_ = obj->object_weak_refs_;
} }
obj->object_weak_refs_ = this; obj->object_weak_refs_ = this;
// Sanity checking: We make the assumption that static-casting our pointer // Sanity check: We make the assumption that static-casting our pointer
// to/from Object gives the same results as reinterpret-casting it; let's // to/from Object gives the same results as reinterpret-casting it; let's
// be certain that's the case. In some cases involving multiple // be certain that's the case. In some cases involving multiple
// inheritance this might not be true, but we avoid those cases in our // inheritance this might not be true, but we avoid those cases in our
@ -431,6 +423,7 @@ class Object {
} }
#if BA_DEBUG_BUILD #if BA_DEBUG_BUILD
obj->ObjectUpdateForAcquire();
obj->ObjectThreadCheck(); obj->ObjectThreadCheck();
// Obvs shouldn't be referencing dead stuff. // Obvs shouldn't be referencing dead stuff.
@ -444,10 +437,10 @@ class Object {
// Log only to system log for these low-level errors; // Log only to system log for these low-level errors;
// console or server can cause deadlock due to recursive // console or server can cause deadlock due to recursive
// ref-list locks. // ref-list locks.
printf( FatalError(
"Incorrectly creating initial strong-ref to %s; use " "Incorrectly creating initial strong-ref; use "
"New() or MakeRefCounted()\n", "New() or MakeRefCounted(): "
obj->GetObjectDescription().c_str()); + obj->GetObjectDescription());
} }
obj->object_has_strong_ref_ = true; obj->object_has_strong_ref_ = true;
#endif // BA_DEBUG_BUILD #endif // BA_DEBUG_BUILD
@ -474,7 +467,7 @@ class Object {
} }
} }
} }
T* obj_ = nullptr; T* obj_{};
}; };
/// Object::New<Type>(): The preferred way to create ref-counted Objects. /// Object::New<Type>(): The preferred way to create ref-counted Objects.
@ -490,17 +483,16 @@ class Object {
#if BA_DEBUG_BUILD #if BA_DEBUG_BUILD
if (ptr->object_creating_strong_reffed_) { if (ptr->object_creating_strong_reffed_) {
// Avoiding Log for these low level errors; can lead to deadlock. // Avoiding Log for these low level errors; can lead to deadlock.
printf("Object already set up as reffed in New: %s\n", FatalError("ballistica::Object already set up as reffed in New: "
ptr->GetObjectDescription().c_str()); + ptr->GetObjectDescription());
} }
if (ptr->object_strong_ref_count_ > 0) { if (ptr->object_strong_ref_count_ > 0) {
// TODO(ericf): make this an error once its cleared out FatalError("ballistica::Object hs strong-ref in constructor: "
printf("Obj strong-ref in constructor: %s\n", + ptr->GetObjectDescription());
ptr->GetObjectDescription().c_str());
} }
ptr->object_in_constructor_ = false; ptr->object_in_constructor_ = false;
ptr->object_creating_strong_reffed_ = true; ptr->object_creating_strong_reffed_ = true;
#endif // BA_DEBUG_BUILD #endif
return Object::Ref<TRETURN>(ptr); return Object::Ref<TRETURN>(ptr);
} }
@ -517,8 +509,8 @@ class Object {
T* ptr = new T(std::forward<ARGS>(args)...); T* ptr = new T(std::forward<ARGS>(args)...);
#if BA_DEBUG_BUILD #if BA_DEBUG_BUILD
if (ptr->object_strong_ref_count_ > 0) { if (ptr->object_strong_ref_count_ > 0) {
printf("Obj strong-ref in constructor: %s\n", FatalError("ballistica::Object has strong-ref in constructor: "
ptr->GetObjectDescription().c_str()); + ptr->GetObjectDescription());
} }
ptr->object_in_constructor_ = false; ptr->object_in_constructor_ = false;
#endif #endif
@ -531,9 +523,9 @@ class Object {
// Make sure we're operating on a fresh object. // Make sure we're operating on a fresh object.
assert(ptr->object_strong_ref_count_ == 0); assert(ptr->object_strong_ref_count_ == 0);
if (ptr->object_creating_strong_reffed_) { if (ptr->object_creating_strong_reffed_) {
// Avoiding Log for these low level errors; can lead to deadlock. FatalError(
printf("Object already set up as reffed in MakeRefCounted: %s\n", "ballistica::Object already set up as reffed in MakeRefCounted: "
ptr->GetObjectDescription().c_str()); + ptr->GetObjectDescription());
} }
ptr->object_creating_strong_reffed_ = true; ptr->object_creating_strong_reffed_ = true;
#endif #endif
@ -554,15 +546,13 @@ class Object {
} }
private: private:
#if BA_DEBUG_BUILD
// Making operator new private here to help ensure all of our dynamic // Making operator new private here to help ensure all of our dynamic
// allocation/deallocation goes through our special functions (New(), // allocation/deallocation goes through our special functions (New(),
// NewDeferred(), etc.). However, sticking with original new for release // NewDeferred(), etc.). However, sticking with original new for release
// builds since it may handle corner cases this does not. // builds since it may handle corner cases this does not.
#if BA_DEBUG_BUILD
auto operator new(size_t size) -> void* { return new char[size]; } auto operator new(size_t size) -> void* { return new char[size]; }
#endif auto ObjectUpdateForAcquire() -> void;
#if BA_DEBUG_BUILD
bool object_has_strong_ref_{}; bool object_has_strong_ref_{};
bool object_creating_strong_reffed_{}; bool object_creating_strong_reffed_{};
bool object_is_dead_{}; bool object_is_dead_{};

View File

@ -9,28 +9,14 @@
namespace ballistica { namespace ballistica {
bool Thread::threads_paused_ = false; void Thread::SetInternalThreadName(const std::string& name) {
std::scoped_lock lock(g_app_globals->thread_name_map_mutex);
void Thread::AddCurrentThreadName(const std::string& name) {
std::lock_guard<std::mutex> lock(g_app_globals->thread_name_map_mutex);
std::thread::id thread_id = std::this_thread::get_id(); std::thread::id thread_id = std::this_thread::get_id();
auto i = g_app_globals->thread_name_map.find(thread_id); g_app_globals->thread_name_map[std::this_thread::get_id()] = name;
std::string s;
if (i != g_app_globals->thread_name_map.end()) {
s = i->second;
}
if (!strstr(s.c_str(), name.c_str())) {
if (s.empty()) {
s = name;
} else {
s = s + "+" + name;
}
}
g_app_globals->thread_name_map[std::this_thread::get_id()] = s;
} }
void Thread::ClearCurrentThreadName() { void Thread::ClearCurrentThreadName() {
std::lock_guard<std::mutex> lock(g_app_globals->thread_name_map_mutex); std::scoped_lock lock(g_app_globals->thread_name_map_mutex);
auto i = g_app_globals->thread_name_map.find(std::this_thread::get_id()); auto i = g_app_globals->thread_name_map.find(std::this_thread::get_id());
if (i != g_app_globals->thread_name_map.end()) { if (i != g_app_globals->thread_name_map.end()) {
g_app_globals->thread_name_map.erase(i); g_app_globals->thread_name_map.erase(i);
@ -50,27 +36,10 @@ void Thread::UpdateMainThreadID() {
} }
} }
void Thread::KillModule(const Module& module) { // These are all exactly the same; its just a way to try and clarify
for (auto i = modules_.begin(); i != modules_.end(); i++) { // in stack traces which thread is running in case it is not otherwise
if (*i == &module) { // evident.
delete *i;
modules_.erase(i);
return;
}
}
throw Exception("Module not found on this thread");
}
void Thread::KillModules() {
for (auto i : modules_) {
delete i;
}
modules_.clear();
}
// These are all exactly the same, but by running different ones for
// different thread groups makes its easy to see which thread is which
// in profilers, backtraces, etc.
auto Thread::RunLogicThread(void* data) -> int { auto Thread::RunLogicThread(void* data) -> int {
return static_cast<Thread*>(data)->ThreadMain(); return static_cast<Thread*>(data)->ThreadMain();
} }
@ -108,13 +77,11 @@ void Thread::WaitForNextEvent(bool single_cycle) {
return; return;
} }
// We also never wait if any of our modules have pending runnables. // We also never wait if we have pending runnables.
// (we run all existing runnables in each loop cycle, but one of those // (we run all existing runnables in each loop cycle, but one of those
// may have enqueued more). // may have enqueued more).
for (auto&& i : modules_) { if (has_pending_runnables()) {
if (i->has_pending_runnables()) { return;
return;
}
} }
// While we're waiting, allow other python threads to run. // While we're waiting, allow other python threads to run.
@ -124,7 +91,7 @@ void Thread::WaitForNextEvent(bool single_cycle) {
// If we've got active timers, wait for messages with a timeout so we can // If we've got active timers, wait for messages with a timeout so we can
// run the next timer payload. // run the next timer payload.
if ((!paused_) && timers_.active_timer_count() > 0) { if (!paused_ && timers_.active_timer_count() > 0) {
millisecs_t real_time = GetRealTime(); millisecs_t real_time = GetRealTime();
millisecs_t wait_time = timers_.GetTimeToNextExpire(real_time); millisecs_t wait_time = timers_.GetTimeToNextExpire(real_time);
if (wait_time > 0) { if (wait_time > 0) {
@ -185,76 +152,44 @@ auto Thread::RunEventLoop(bool single_cycle) -> int {
GetThreadMessages(&thread_messages); GetThreadMessages(&thread_messages);
for (auto& thread_message : thread_messages) { for (auto& thread_message : thread_messages) {
switch (thread_message.type) { switch (thread_message.type) {
case ThreadMessage::Type::kNewModule: {
// Launch a new module and unlock.
ModuleLauncher* tl;
tl = static_cast<ModuleLauncher*>(thread_message.pval);
tl->Launch(this);
auto cmd =
static_cast<uint32_t>(ThreadMessage::Type::kNewModuleConfirm);
WriteToOwner(&cmd, sizeof(cmd));
break;
}
case ThreadMessage::Type::kRunnable: { case ThreadMessage::Type::kRunnable: {
auto module_id = thread_message.ival; PushLocalRunnable(thread_message.runnable,
Module* t = GetModule(module_id); thread_message.completion_flag);
assert(t);
auto e = static_cast<Runnable*>(thread_message.pval);
// Add the event to our list.
t->PushLocalRunnable(e);
RunnablesWhilePausedSanityCheck(e);
break; break;
} }
case ThreadMessage::Type::kShutdown: { case ThreadMessage::Type::kShutdown: {
// Shutdown; die!
done_ = true; done_ = true;
break; break;
} }
case ThreadMessage::Type::kResume: {
assert(paused_);
// Let all modules do pause-related stuff.
for (auto&& i : modules_) {
i->HandleThreadResume();
}
paused_ = false;
break;
}
case ThreadMessage::Type::kPause: { case ThreadMessage::Type::kPause: {
assert(!paused_); assert(!paused_);
RunPauseCallbacks();
// Let all modules do pause-related stuff.
for (auto&& i : modules_) {
i->HandleThreadPause();
}
paused_ = true; paused_ = true;
last_pause_time_ = GetRealTime(); last_pause_time_ = GetRealTime();
messages_since_paused_ = 0; messages_since_paused_ = 0;
break; break;
} }
case ThreadMessage::Type::kResume: {
assert(paused_);
RunResumeCallbacks();
paused_ = false;
break;
}
default: { default: {
throw Exception(); throw Exception();
} }
} }
// If the thread is going down.
if (done_) { if (done_) {
break; break;
} }
} }
// Run timers && queued module runnables unless we're paused.
if (!paused_) { if (!paused_) {
// Run timers.
timers_.Run(GetRealTime()); timers_.Run(GetRealTime());
RunPendingRunnables();
// Run module-messages.
for (auto& module_entry : modules_) {
module_entry->RunPendingRunnables();
}
} }
if (done_ || single_cycle) { if (done_ || single_cycle) {
break; break;
} }
@ -262,17 +197,6 @@ auto Thread::RunEventLoop(bool single_cycle) -> int {
return 0; return 0;
} }
void Thread::RunnablesWhilePausedSanityCheck(Runnable* e) {
// We generally shouldn't be getting messages while paused..
// (check both our pause-state and the global one; wanna ignore things
// that might slip through if some just-unlocked thread msgs us but we
// haven't been unlocked yet)
// UPDATE - we are migrating away from distinct message classes and towards
// LambdaRunnables for everything, which means that we can't easily
// see details of what is coming through. Disabling this check for now.
}
void Thread::GetThreadMessages(std::list<ThreadMessage>* messages) { void Thread::GetThreadMessages(std::list<ThreadMessage>* messages) {
assert(messages); assert(messages);
assert(std::this_thread::get_id() == thread_id()); assert(std::this_thread::get_id() == thread_id());
@ -323,13 +247,12 @@ Thread::Thread(ThreadIdentifier identifier_in, ThreadType type_in)
// Let 'er rip. // Let 'er rip.
thread_ = new std::thread(func, this); thread_ = new std::thread(func, this);
// The thread lets us know when its up and running. // Block until the thread is bootstrapped.
std::unique_lock<std::mutex> lock(data_to_client_mutex_); // (maybe not necessary, but let's be cautious in case we'd
// try to use things like thread_id before they're known).
std::unique_lock lock(client_listener_mutex_);
client_listener_cv_.wait(lock, [this] { return bootstrapped_; });
uint32_t cmd;
ReadFromThread(&lock, &cmd, sizeof(cmd));
assert(static_cast<ThreadMessage::Type>(cmd)
== ThreadMessage::Type::kNewThreadConfirm);
break; break;
} }
case ThreadType::kMain: { case ThreadType::kMain: {
@ -338,8 +261,11 @@ Thread::Thread(ThreadIdentifier identifier_in, ThreadType type_in)
assert(std::this_thread::get_id() == g_app_globals->main_thread_id); assert(std::this_thread::get_id() == g_app_globals->main_thread_id);
thread_id_ = std::this_thread::get_id(); thread_id_ = std::this_thread::get_id();
// Hmmm we might want to set our thread name here, // Set our own thread-id-to-name mapping.
// as we do for other threads? SetInternalThreadName("main");
// Hmmm we might want to set our OS thread name here,
// as we do for other threads? (SetCurrentThreadName).
// However on linux that winds up being what we see in top/etc // However on linux that winds up being what we see in top/etc
// so maybe shouldn't. // so maybe shouldn't.
break; break;
@ -351,46 +277,60 @@ auto Thread::ThreadMain() -> int {
try { try {
assert(type_ == ThreadType::kStandard); assert(type_ == ThreadType::kStandard);
thread_id_ = std::this_thread::get_id(); thread_id_ = std::this_thread::get_id();
const char* name;
const char* id_string; const char* id_string;
switch (identifier_) { switch (identifier_) {
case ThreadIdentifier::kLogic: case ThreadIdentifier::kLogic:
name = "logic";
id_string = "ballistica logic"; id_string = "ballistica logic";
break; break;
case ThreadIdentifier::kStdin: case ThreadIdentifier::kStdin:
name = "stdin";
id_string = "ballistica stdin"; id_string = "ballistica stdin";
break; break;
case ThreadIdentifier::kMedia: case ThreadIdentifier::kMedia:
name = "media";
id_string = "ballistica media"; id_string = "ballistica media";
break; break;
case ThreadIdentifier::kFileOut: case ThreadIdentifier::kFileOut:
name = "fileout";
id_string = "ballistica file-out"; id_string = "ballistica file-out";
break; break;
case ThreadIdentifier::kMain: case ThreadIdentifier::kMain:
name = "main";
id_string = "ballistica main"; id_string = "ballistica main";
break; break;
case ThreadIdentifier::kAudio: case ThreadIdentifier::kAudio:
name = "audio";
id_string = "ballistica audio"; id_string = "ballistica audio";
break; break;
case ThreadIdentifier::kBGDynamics: case ThreadIdentifier::kBGDynamics:
name = "bgdynamics";
id_string = "ballistica bg-dynamics"; id_string = "ballistica bg-dynamics";
break; break;
case ThreadIdentifier::kNetworkWrite: case ThreadIdentifier::kNetworkWrite:
name = "networkwrite";
id_string = "ballistica network writing"; id_string = "ballistica network writing";
break; break;
default: default:
throw Exception(); throw Exception();
} }
assert(name && id_string);
SetInternalThreadName(name);
g_platform->SetCurrentThreadName(id_string); g_platform->SetCurrentThreadName(id_string);
// Send our owner a confirmation that we're alive. // Mark ourself as bootstrapped and signal listeners so
auto cmd = static_cast<uint32_t>(ThreadMessage::Type::kNewThreadConfirm); // anyone waiting for us to spin up can move along.
WriteToOwner(&cmd, sizeof(cmd)); {
std::scoped_lock lock(client_listener_mutex_);
bootstrapped_ = true;
}
client_listener_cv_.notify_all();
// Now just run our loop until we die. // Now just run our loop until we die.
int result = RunEventLoop(); int result = RunEventLoop();
KillModules();
ClearCurrentThreadName(); ClearCurrentThreadName();
return result; return result;
} catch (const std::exception& e) { } catch (const std::exception& e) {
@ -453,15 +393,6 @@ void Thread::LogThreadMessageTally() {
case ThreadMessage::Type::kRunnable: case ThreadMessage::Type::kRunnable:
s += "kRunnable"; s += "kRunnable";
break; break;
case ThreadMessage::Type::kNewModule:
s += "kNewModule";
break;
case ThreadMessage::Type::kNewModuleConfirm:
s += "kNewModuleConfirm";
break;
case ThreadMessage::Type::kNewThreadConfirm:
s += "kNewThreadConfirm";
break;
case ThreadMessage::Type::kPause: case ThreadMessage::Type::kPause:
s += "kPause"; s += "kPause";
break; break;
@ -473,8 +404,8 @@ void Thread::LogThreadMessageTally() {
break; break;
} }
if (m.type == ThreadMessage::Type::kRunnable) { if (m.type == ThreadMessage::Type::kRunnable) {
std::string m_name = g_platform->DemangleCXXSymbol( std::string m_name =
typeid(*(static_cast<Runnable*>(m.pval))).name()); g_platform->DemangleCXXSymbol(typeid(*(m.runnable)).name());
s += std::string(": ") + m_name; s += std::string(": ") + m_name;
} }
auto j = tally.find(s); auto j = tally.find(s);
@ -550,55 +481,15 @@ void Thread::PushThreadMessage(const ThreadMessage& t) {
thread_message_cv_.notify_all(); thread_message_cv_.notify_all();
} }
#pragma clang diagnostic push
#pragma ide diagnostic ignored "ConstantParameter"
void Thread::WriteToOwner(const void* data, uint32_t size) {
assert(std::this_thread::get_id() == thread_id());
{
std::unique_lock<std::mutex> lock(data_to_client_mutex_);
data_to_client_.emplace_back(size);
memcpy(&(data_to_client_.back()[0]), data, size);
}
data_to_client_cv_.notify_all();
}
void Thread::ReadFromThread(std::unique_lock<std::mutex>* lock, void* buffer,
uint32_t size) {
// Threads cant read from themselves.. could load to lock-deadlock.
assert(std::this_thread::get_id() != thread_id());
data_to_client_cv_.wait(*lock, [this] {
// Go back to sleep on spurious wakeups
// (if we didn't wind up with any new messages)
return (!data_to_client_.empty());
});
// Read the oldest thing on our in-data list.
assert(!data_to_client_.empty());
assert(data_to_client_.front().size() == size);
memcpy(buffer, &(data_to_client_.front()[0]), size);
data_to_client_.pop_front();
}
#pragma clang diagnostic pop
void Thread::SetThreadsPaused(bool paused) { void Thread::SetThreadsPaused(bool paused) {
threads_paused_ = paused; g_app_globals->threads_paused = paused;
for (auto&& i : g_app_globals->pausable_threads) { for (auto&& i : g_app_globals->pausable_threads) {
i->SetPaused(paused); i->SetPaused(paused);
} }
} }
auto Thread::AreThreadsPaused() -> bool { return threads_paused_; } auto Thread::AreThreadsPaused() -> bool {
return g_app_globals->threads_paused;
auto Thread::RegisterModule(const std::string& name, Module* module) -> int {
AddCurrentThreadName(name);
// This should assure we were properly launched.
// (the ModuleLauncher will set the index to what ours will be)
int index = static_cast<int>(modules_.size());
// module_entries_.emplace_back(module, name, index);
modules_.push_back(module);
return index;
} }
auto Thread::NewTimer(millisecs_t length, bool repeat, auto Thread::NewTimer(millisecs_t length, bool repeat,
@ -613,12 +504,14 @@ auto Thread::GetCurrentThreadName() -> std::string {
return "unknown(not-yet-inited)"; return "unknown(not-yet-inited)";
} }
{ {
std::lock_guard<std::mutex> lock(g_app_globals->thread_name_map_mutex); std::scoped_lock lock(g_app_globals->thread_name_map_mutex);
auto i = g_app_globals->thread_name_map.find(std::this_thread::get_id()); auto i = g_app_globals->thread_name_map.find(std::this_thread::get_id());
if (i != g_app_globals->thread_name_map.end()) { if (i != g_app_globals->thread_name_map.end()) {
return i->second; return i->second;
} }
} }
// FIXME - move this to platform.
#if BA_OSTYPE_MACOS || BA_OSTYPE_IOS_TVOS || BA_OSTYPE_LINUX #if BA_OSTYPE_MACOS || BA_OSTYPE_IOS_TVOS || BA_OSTYPE_LINUX
std::string name = "unknown (sys-name="; std::string name = "unknown (sys-name=";
char buffer[256]; char buffer[256];
@ -634,4 +527,99 @@ auto Thread::GetCurrentThreadName() -> std::string {
#endif #endif
} }
void Thread::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());
std::list<std::pair<Runnable*, bool*>> runnables;
runnables_.swap(runnables);
bool do_notify_listeners{};
for (auto&& i : runnables) {
i.first->Run();
delete i.first;
// If this runnable wanted to be flagged when done, set its flag
// and make a note to wake all client listeners.
if (i.second != nullptr) {
*(i.second) = true;
do_notify_listeners = true;
}
}
if (do_notify_listeners) {
client_listener_cv_.notify_all();
}
}
void Thread::RunPauseCallbacks() {
for (Runnable* i : pause_callbacks_) {
i->Run();
}
}
void Thread::RunResumeCallbacks() {
for (Runnable* i : resume_callbacks_) {
i->Run();
}
}
void Thread::PushLocalRunnable(Runnable* runnable, bool* completion_flag) {
assert(std::this_thread::get_id() == thread_id());
runnables_.push_back(std::make_pair(runnable, completion_flag));
}
void Thread::PushCrossThreadRunnable(Runnable* runnable,
bool* completion_flag) {
PushThreadMessage(Thread::ThreadMessage(
Thread::ThreadMessage::Type::kRunnable, runnable, completion_flag));
}
void Thread::AddPauseCallback(Runnable* runnable) {
assert(std::this_thread::get_id() == thread_id());
pause_callbacks_.push_back(runnable);
}
void Thread::AddResumeCallback(Runnable* runnable) {
assert(std::this_thread::get_id() == thread_id());
resume_callbacks_.push_back(runnable);
}
void Thread::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);
} else {
PushCrossThreadRunnable(runnable, nullptr);
}
}
void Thread::PushRunnableSynchronous(Runnable* runnable) {
bool complete{};
bool* complete_ptr{&complete};
if (std::this_thread::get_id() == thread_id()) {
FatalError(
"PushRunnableSynchronous called from target thread;"
" would deadlock.");
} else {
PushCrossThreadRunnable(runnable, &complete);
}
// Now listen until our completion flag gets set.
std::unique_lock lock(client_listener_mutex_);
client_listener_cv_.wait(lock, [complete_ptr] {
// Go back to sleep on spurious wakeups
// (if we're not actually complete yet).
return *complete_ptr;
});
}
auto Thread::CheckPushSafety() -> bool {
if (std::this_thread::get_id() == thread_id()) {
// behave the same as the thread-message safety check.
return (runnables_.size() < kThreadMessageSafetyThreshold);
} else {
return CheckPushRunnableSafety();
}
}
} // namespace ballistica } // namespace ballistica

View File

@ -12,6 +12,7 @@
#include "ballistica/app/app_globals.h" #include "ballistica/app/app_globals.h"
#include "ballistica/ballistica.h" #include "ballistica/ballistica.h"
#include "ballistica/generic/lambda_runnable.h"
#include "ballistica/generic/timer_list.h" #include "ballistica/generic/timer_list.h"
#include "ballistica/platform/min_sdl.h" #include "ballistica/platform/min_sdl.h"
@ -26,18 +27,14 @@ class Thread {
ThreadType type_in = ThreadType::kStandard); ThreadType type_in = ThreadType::kStandard);
virtual ~Thread(); virtual ~Thread();
/// Register a name for the current thread (should generally describe its auto ClearCurrentThreadName() -> void;
/// purpose). If called multiple times, names will be combined with a '+'. ie:
/// "graphics+animation+audio".
void AddCurrentThreadName(const std::string& name);
void ClearCurrentThreadName();
static auto GetCurrentThreadName() -> std::string; static auto GetCurrentThreadName() -> std::string;
/// Call this if the main thread changes. /// Call this if the main thread changes.
static void UpdateMainThreadID(); static auto UpdateMainThreadID() -> void;
static void SetThreadsPaused(bool enable); static auto SetThreadsPaused(bool enable) -> void;
static auto AreThreadsPaused() -> bool; static auto AreThreadsPaused() -> bool;
auto IsCurrent() const -> bool { auto IsCurrent() const -> bool {
@ -47,105 +44,8 @@ class Thread {
// Used to quit the main thread. // Used to quit the main thread.
void Quit(); void Quit();
struct ModuleLauncher {
virtual void Launch(Thread* g) = 0;
virtual ~ModuleLauncher() = default;
};
template <class MODULETYPE>
struct ModuleLauncherTemplate : public ModuleLauncher {
void Launch(Thread* g) override { new MODULETYPE(g); }
};
template <class MODULETYPE, class ARGTYPE>
struct ModuleLauncherArgTemplate : public ModuleLauncher {
explicit ModuleLauncherArgTemplate(ARGTYPE arg_in) : arg(arg_in) {}
ARGTYPE arg;
void Launch(Thread* g) override { new MODULETYPE(g, arg); }
};
void SetOwnsPython(); void SetOwnsPython();
// Add a new module to a thread. This doesn't return anything. If you need
// a pointer to the module, have it store itself somewhere in its constructor
// or whatnot. Returning a pointer made it too easy to introduce race
// conditions with the thread trying to access itself via this pointer
// before it was set up.
template <class THREADTYPE>
void AddModule() {
switch (type_) {
case ThreadType::kStandard: {
// Launching a module in the current thread: do it immediately.
if (IsCurrent()) {
ModuleLauncherTemplate<THREADTYPE> launcher;
launcher.Launch(this);
} else {
// Launching a module in another thread;
// send a module-launcher and wait for the confirmation.
ModuleLauncherTemplate<THREADTYPE> launcher;
ModuleLauncher* tl = &launcher;
PushThreadMessage(
ThreadMessage(ThreadMessage::Type::kNewModule, 0, tl));
std::unique_lock<std::mutex> lock(data_to_client_mutex_);
uint32_t cmd;
ReadFromThread(&lock, &cmd, sizeof(cmd));
assert(static_cast<ThreadMessage::Type>(cmd)
== ThreadMessage::Type::kNewModuleConfirm);
}
break;
}
case ThreadType::kMain: {
assert(std::this_thread::get_id() == g_app_globals->main_thread_id);
new THREADTYPE(this);
break;
}
default: {
throw Exception();
}
}
}
// An alternate version of AddModule that passes an argument along
// to the thread's constructor.
template <class THREADTYPE, class ARGTYPE>
void AddModule(ARGTYPE arg) {
switch (type_) {
case ThreadType::kStandard: {
// Launching a module in the current thread: do it immediately.
if (IsCurrent()) {
ModuleLauncherArgTemplate<THREADTYPE, ARGTYPE> launcher(arg);
launcher.Launch(this);
} else {
// Launching a module in another thread;
// send a module-launcher and wait for the confirmation.
ModuleLauncherArgTemplate<THREADTYPE, ARGTYPE> launcher(arg);
ModuleLauncher* tl = &launcher;
PushThreadMessage(
ThreadMessage(ThreadMessage::Type::kNewModule, 0, tl));
std::unique_lock<std::mutex> lock(data_to_client_mutex_);
uint32_t cmd;
ReadFromThread(&lock, &cmd, sizeof(cmd));
assert(static_cast<ThreadMessage::Type>(cmd)
== ThreadMessage::Type::kNewModuleConfirm);
}
break;
}
case ThreadType::kMain: {
assert(std::this_thread::get_id() == g_app_globals->main_thread_id);
new THREADTYPE(this, arg);
break;
}
default: {
throw Exception();
}
}
}
void KillModule(const Module& module);
void SetPaused(bool paused); void SetPaused(bool paused);
auto thread_id() const -> std::thread::id { return thread_id_; } auto thread_id() const -> std::thread::id { return thread_id_; }
@ -157,14 +57,7 @@ class Thread {
auto RunEventLoop(bool single_cycle = false) -> int; auto RunEventLoop(bool single_cycle = false) -> int;
auto identifier() const -> ThreadIdentifier { return identifier_; } auto identifier() const -> ThreadIdentifier { return identifier_; }
// For use by modules. auto CheckPushRunnableSafety() -> bool {
auto RegisterModule(const std::string& name, Module* module) -> int;
void PushModuleRunnable(Runnable* runnable, int module_index) {
PushThreadMessage(Thread::ThreadMessage(
Thread::ThreadMessage::Type::kRunnable, module_index, runnable));
}
auto CheckPushModuleRunnableSafety() -> bool {
// We first complain when we get to 1000 queued messages so // We first complain when we get to 1000 queued messages so
// let's consider things unsafe when we're halfway there. // let's consider things unsafe when we're halfway there.
return (thread_message_count_ < kThreadMessageSafetyThreshold); return (thread_message_count_ < kThreadMessageSafetyThreshold);
@ -174,52 +67,83 @@ class Thread {
auto NewTimer(millisecs_t length, bool repeat, auto NewTimer(millisecs_t length, bool repeat,
const Object::Ref<Runnable>& runnable) -> Timer*; const Object::Ref<Runnable>& runnable) -> Timer*;
/// Add a runnable to this thread's event-loop.
/// Pass a Runnable that has been allocated with new().
/// There must be no existing strong refs to it.
/// It will be owned by the thread
auto PushRunnable(Runnable* runnable) -> void;
/// Convenience function to push a lambda as a runnable.
template <typename F>
auto PushCall(const F& lambda) -> void {
PushRunnable(NewLambdaRunnableRaw(lambda));
}
/// Add a runnable to this thread's event-loop and wait until it completes.
auto PushRunnableSynchronous(Runnable* runnable) -> void;
/// Convenience function to push a lambda as a runnable.
template <typename F>
auto PushCallSynchronous(const F& lambda) -> void {
PushRunnableSynchronous(NewLambdaRunnableRaw(lambda));
}
/// Add a callback to be run on event-loop pauses.
auto AddPauseCallback(Runnable* runnable) -> void;
/// Add a callback to be run on event-loop resumes.
auto AddResumeCallback(Runnable* runnable) -> void;
auto has_pending_runnables() const -> bool { return !runnables_.empty(); }
/// Returns true if there is plenty of buffer space available for
/// PushCall/PushRunnable; can be used to avoid buffer-full errors
/// by discarding non-essential calls. An example is calls scheduled
/// due to receiving unreliable network packets; without watching
/// buffer space it can be possible for an attacker to bring down
/// the app through a flood of packets.
auto CheckPushSafety() -> bool;
private: private:
struct ThreadMessage { struct ThreadMessage {
enum class Type { enum class Type { kShutdown = 999, kRunnable, kPause, kResume };
kShutdown = 999,
kRunnable,
kNewModule,
kNewModuleConfirm,
kNewThreadConfirm,
kPause,
kResume
};
Type type; Type type;
void* pval; union {
int ival; Runnable* runnable;
explicit ThreadMessage(Type type_in, int ival_in = 0, };
void* pval_in = nullptr) bool* completion_flag{};
: type(type_in), ival(ival_in), pval(pval_in) {} 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} {}
}; };
static void RunnablesWhilePausedSanityCheck(Runnable* r);
void WaitForNextEvent(bool single_cycle);
void LoopUpkeep(bool once);
void LogThreadMessageTally();
void ReadFromThread(std::unique_lock<std::mutex>* lock, void* buffer,
uint32_t size);
void WriteToOwner(const void* data, uint32_t size); auto SetInternalThreadName(const std::string& name) -> void;
bool writing_tally_ = false; auto WaitForNextEvent(bool single_cycle) -> void;
bool paused_ = false; auto LoopUpkeep(bool once) -> void;
millisecs_t last_pause_time_ = 0; auto LogThreadMessageTally() -> void;
int messages_since_paused_ = 0; auto PushLocalRunnable(Runnable* runnable, bool* completion_flag) -> void;
millisecs_t last_paused_message_report_time_ = 0; auto PushCrossThreadRunnable(Runnable* runnable, bool* completion_flag)
bool done_ = false; -> void;
auto NotifyClientListeners() -> void;
bool writing_tally_{};
bool paused_{};
millisecs_t last_pause_time_{};
int messages_since_paused_{};
millisecs_t last_paused_message_report_time_{};
bool done_{};
ThreadType type_; ThreadType type_;
int listen_sd_ = 0; int listen_sd_{};
std::thread::id thread_id_{}; std::thread::id thread_id_{};
ThreadIdentifier identifier_ = ThreadIdentifier::kInvalid; ThreadIdentifier identifier_{ThreadIdentifier::kInvalid};
millisecs_t last_complaint_time_ = 0; millisecs_t last_complaint_time_{};
bool owns_python_ = false; bool owns_python_{};
// FIXME: Should generalize this to some sort of PlatformThreadData class. // FIXME: Should generalize this to some sort of PlatformThreadData class.
#if BA_XCODE_BUILD #if BA_XCODE_BUILD
void* auto_release_pool_ = nullptr; void* auto_release_pool_ = nullptr;
#endif #endif
void KillModules();
// These are all exactly the same, but by running different ones for // These are all exactly the same, but by running different ones for
// different thread groups makes its easy to see which thread is which // different thread groups makes its easy to see which thread is which
// in profilers, backtraces, etc. // in profilers, backtraces, etc.
@ -231,25 +155,26 @@ class Thread {
static auto RunMediaThread(void* data) -> int; static auto RunMediaThread(void* data) -> int;
auto ThreadMain() -> int; auto ThreadMain() -> int;
std::thread* thread_; auto GetThreadMessages(std::list<ThreadMessage>* messages) -> void;
void GetThreadMessages(std::list<ThreadMessage>* messages); auto PushThreadMessage(const ThreadMessage& t) -> void;
void PushThreadMessage(const ThreadMessage& t);
auto RunPendingRunnables() -> void;
auto RunPauseCallbacks() -> void;
auto RunResumeCallbacks() -> void;
int thread_message_count_{};
bool bootstrapped_{};
std::list<std::pair<Runnable*, bool*>> runnables_;
std::list<Runnable*> pause_callbacks_;
std::list<Runnable*> resume_callbacks_;
std::thread* thread_{};
std::condition_variable thread_message_cv_; std::condition_variable thread_message_cv_;
std::mutex thread_message_mutex_; std::mutex thread_message_mutex_;
std::list<ThreadMessage> thread_messages_; std::list<ThreadMessage> thread_messages_;
int thread_message_count_ = 0; std::condition_variable client_listener_cv_;
std::condition_variable data_to_client_cv_; std::mutex client_listener_mutex_;
std::mutex data_to_client_mutex_; std::list<std::vector<char>> data_to_client_;
std::list<std::vector<char> > data_to_client_;
std::vector<Module*> modules_;
auto GetModule(int id) -> Module* {
assert(id >= 0 && id < static_cast<int>(modules_.size()));
return modules_[id];
}
// Complete list of all timers created by this group's modules.
TimerList timers_; TimerList timers_;
static bool threads_paused_;
}; };
} // namespace ballistica } // namespace ballistica

View File

@ -70,8 +70,7 @@ void BGDynamics::Step(const Vector3f& cam_pos) {
{ // Shadows. { // Shadows.
BA_DEBUG_TIME_CHECK_BEGIN(bg_dynamic_shadow_list_lock); BA_DEBUG_TIME_CHECK_BEGIN(bg_dynamic_shadow_list_lock);
{ {
std::lock_guard<std::mutex> lock( std::scoped_lock lock(g_bg_dynamics_server->shadow_list_mutex_);
g_bg_dynamics_server->shadow_list_mutex_);
auto size = g_bg_dynamics_server->shadows_.size(); auto size = g_bg_dynamics_server->shadows_.size();
d->shadow_step_data_.resize(size); d->shadow_step_data_.resize(size);
if (size > 0) { if (size > 0) {
@ -90,8 +89,7 @@ void BGDynamics::Step(const Vector3f& cam_pos) {
{ // Volume lights. { // Volume lights.
BA_DEBUG_TIME_CHECK_BEGIN(bg_dynamic_volumelights_list_lock); BA_DEBUG_TIME_CHECK_BEGIN(bg_dynamic_volumelights_list_lock);
{ {
std::lock_guard<std::mutex> lock( std::scoped_lock lock(g_bg_dynamics_server->volume_light_list_mutex_);
g_bg_dynamics_server->volume_light_list_mutex_);
auto size = g_bg_dynamics_server->volume_lights_.size(); auto size = g_bg_dynamics_server->volume_lights_.size();
d->volume_light_step_data_.resize(size); d->volume_light_step_data_.resize(size);
if (size > 0) { if (size > 0) {
@ -116,7 +114,7 @@ void BGDynamics::Step(const Vector3f& cam_pos) {
{ // Fuses. { // Fuses.
BA_DEBUG_TIME_CHECK_BEGIN(bg_dynamic_fuse_list_lock); BA_DEBUG_TIME_CHECK_BEGIN(bg_dynamic_fuse_list_lock);
{ {
std::lock_guard<std::mutex> lock(g_bg_dynamics_server->fuse_list_mutex_); std::scoped_lock lock(g_bg_dynamics_server->fuse_list_mutex_);
auto size = g_bg_dynamics_server->fuses_.size(); auto size = g_bg_dynamics_server->fuses_.size();
d->fuse_step_data_.resize(size); d->fuse_step_data_.resize(size);
if (size > 0) { if (size > 0) {
@ -137,7 +135,7 @@ void BGDynamics::Step(const Vector3f& cam_pos) {
// Increase our step count and ship it. // Increase our step count and ship it.
{ {
std::lock_guard<std::mutex> lock(g_bg_dynamics_server->step_count_mutex_); std::scoped_lock lock(g_bg_dynamics_server->step_count_mutex_);
g_bg_dynamics_server->step_count_++; g_bg_dynamics_server->step_count_++;
} }

View File

@ -2,6 +2,7 @@
#include "ballistica/dynamics/bg/bg_dynamics_server.h" #include "ballistica/dynamics/bg/bg_dynamics_server.h"
#include "ballistica/core/thread.h"
#include "ballistica/dynamics/bg/bg_dynamics_draw_snapshot.h" #include "ballistica/dynamics/bg/bg_dynamics_draw_snapshot.h"
#include "ballistica/dynamics/bg/bg_dynamics_fuse_data.h" #include "ballistica/dynamics/bg/bg_dynamics_fuse_data.h"
#include "ballistica/dynamics/bg/bg_dynamics_height_cache.h" #include "ballistica/dynamics/bg/bg_dynamics_height_cache.h"
@ -86,7 +87,7 @@ class BGDynamicsServer::Terrain {
// back to the main thread to get freed. // back to the main thread to get freed.
if (collide_model_) { if (collide_model_) {
Object::Ref<CollideModelData>* ref = collide_model_; Object::Ref<CollideModelData>* ref = collide_model_;
g_game->PushCall([ref] { g_game->thread()->PushCall([ref] {
(**ref).set_last_used_time(GetRealTime()); (**ref).set_last_used_time(GetRealTime());
delete ref; delete ref;
}); });
@ -660,7 +661,7 @@ void BGDynamicsServer::ParticleSet::UpdateAndCreateSnapshot(
} }
BGDynamicsServer::BGDynamicsServer(Thread* thread) BGDynamicsServer::BGDynamicsServer(Thread* thread)
: Module("bgDynamics", thread), : thread_(thread),
height_cache_(new BGDynamicsHeightCache()), height_cache_(new BGDynamicsHeightCache()),
collision_cache_(new CollisionCache) { collision_cache_(new CollisionCache) {
BA_PRECONDITION(g_bg_dynamics_server == nullptr); BA_PRECONDITION(g_bg_dynamics_server == nullptr);
@ -682,8 +683,6 @@ BGDynamicsServer::BGDynamicsServer(Thread* thread)
assert(ode_contact_group_); assert(ode_contact_group_);
} }
BGDynamicsServer::~BGDynamicsServer() = default;
BGDynamicsServer::Tendril::~Tendril() { BGDynamicsServer::Tendril::~Tendril() {
// If we have a controller, tell them not to call us anymore. // If we have a controller, tell them not to call us anymore.
if (controller_) { if (controller_) {
@ -992,7 +991,7 @@ void BGDynamicsServer::Clear() {
} }
void BGDynamicsServer::PushEmitCall(const BGDynamicsEmission& def) { void BGDynamicsServer::PushEmitCall(const BGDynamicsEmission& def) {
PushCall([this, def] { Emit(def); }); thread()->PushCall([this, def] { Emit(def); });
} }
void BGDynamicsServer::Emit(const BGDynamicsEmission& def) { void BGDynamicsServer::Emit(const BGDynamicsEmission& def) {
@ -1364,7 +1363,7 @@ void BGDynamicsServer::Emit(const BGDynamicsEmission& def) {
} }
void BGDynamicsServer::PushRemoveTerrainCall(CollideModelData* collide_model) { void BGDynamicsServer::PushRemoveTerrainCall(CollideModelData* collide_model) {
PushCall([this, collide_model] { thread()->PushCall([this, collide_model] {
assert(collide_model != nullptr); assert(collide_model != nullptr);
bool found = false; bool found = false;
for (auto i = terrains_.begin(); i != terrains_.end(); ++i) { for (auto i = terrains_.begin(); i != terrains_.end(); ++i) {
@ -1394,19 +1393,19 @@ void BGDynamicsServer::PushRemoveTerrainCall(CollideModelData* collide_model) {
} }
void BGDynamicsServer::PushAddShadowCall(BGDynamicsShadowData* shadow_data) { void BGDynamicsServer::PushAddShadowCall(BGDynamicsShadowData* shadow_data) {
PushCall([this, shadow_data] { thread()->PushCall([this, shadow_data] {
assert(InBGDynamicsThread()); assert(InBGDynamicsThread());
std::lock_guard<std::mutex> lock(shadow_list_mutex_); std::scoped_lock lock(shadow_list_mutex_);
shadows_.push_back(shadow_data); shadows_.push_back(shadow_data);
}); });
} }
void BGDynamicsServer::PushRemoveShadowCall(BGDynamicsShadowData* shadow_data) { void BGDynamicsServer::PushRemoveShadowCall(BGDynamicsShadowData* shadow_data) {
PushCall([this, shadow_data] { thread()->PushCall([this, shadow_data] {
assert(InBGDynamicsThread()); assert(InBGDynamicsThread());
bool found = false; bool found = false;
{ {
std::lock_guard<std::mutex> lock(shadow_list_mutex_); std::scoped_lock lock(shadow_list_mutex_);
for (auto i = shadows_.begin(); i != shadows_.end(); ++i) { for (auto i = shadows_.begin(); i != shadows_.end(); ++i) {
if ((*i) == shadow_data) { if ((*i) == shadow_data) {
found = true; found = true;
@ -1422,20 +1421,20 @@ void BGDynamicsServer::PushRemoveShadowCall(BGDynamicsShadowData* shadow_data) {
void BGDynamicsServer::PushAddVolumeLightCall( void BGDynamicsServer::PushAddVolumeLightCall(
BGDynamicsVolumeLightData* volume_light_data) { BGDynamicsVolumeLightData* volume_light_data) {
PushCall([this, volume_light_data] { thread()->PushCall([this, volume_light_data] {
// Add to our internal list. // Add to our internal list.
std::lock_guard<std::mutex> lock(volume_light_list_mutex_); std::scoped_lock lock(volume_light_list_mutex_);
volume_lights_.push_back(volume_light_data); volume_lights_.push_back(volume_light_data);
}); });
} }
void BGDynamicsServer::PushRemoveVolumeLightCall( void BGDynamicsServer::PushRemoveVolumeLightCall(
BGDynamicsVolumeLightData* volume_light_data) { BGDynamicsVolumeLightData* volume_light_data) {
PushCall([this, volume_light_data] { thread()->PushCall([this, volume_light_data] {
// Remove from our list and kill. // Remove from our list and kill.
bool found = false; bool found = false;
{ {
std::lock_guard<std::mutex> lock(volume_light_list_mutex_); std::scoped_lock lock(volume_light_list_mutex_);
for (auto i = volume_lights_.begin(); i != volume_lights_.end(); ++i) { for (auto i = volume_lights_.begin(); i != volume_lights_.end(); ++i) {
if ((*i) == volume_light_data) { if ((*i) == volume_light_data) {
found = true; found = true;
@ -1450,17 +1449,17 @@ void BGDynamicsServer::PushRemoveVolumeLightCall(
} }
void BGDynamicsServer::PushAddFuseCall(BGDynamicsFuseData* fuse_data) { void BGDynamicsServer::PushAddFuseCall(BGDynamicsFuseData* fuse_data) {
PushCall([this, fuse_data] { thread()->PushCall([this, fuse_data] {
std::lock_guard<std::mutex> lock(fuse_list_mutex_); std::scoped_lock lock(fuse_list_mutex_);
fuses_.push_back(fuse_data); fuses_.push_back(fuse_data);
}); });
} }
void BGDynamicsServer::PushRemoveFuseCall(BGDynamicsFuseData* fuse_data) { void BGDynamicsServer::PushRemoveFuseCall(BGDynamicsFuseData* fuse_data) {
PushCall([this, fuse_data] { thread()->PushCall([this, fuse_data] {
bool found = false; bool found = false;
{ {
std::lock_guard<std::mutex> lock(fuse_list_mutex_); std::scoped_lock lock(fuse_list_mutex_);
for (auto i = fuses_.begin(); i != fuses_.end(); i++) { for (auto i = fuses_.begin(); i != fuses_.end(); i++) {
if ((*i) == fuse_data) { if ((*i) == fuse_data) {
found = true; found = true;
@ -1475,11 +1474,11 @@ void BGDynamicsServer::PushRemoveFuseCall(BGDynamicsFuseData* fuse_data) {
} }
void BGDynamicsServer::PushSetDebrisFrictionCall(float friction) { void BGDynamicsServer::PushSetDebrisFrictionCall(float friction) {
PushCall([this, friction] { debris_friction_ = friction; }); thread()->PushCall([this, friction] { debris_friction_ = friction; });
} }
void BGDynamicsServer::PushSetDebrisKillHeightCall(float height) { void BGDynamicsServer::PushSetDebrisKillHeightCall(float height) {
PushCall([this, height] { debris_kill_height_ = height; }); thread()->PushCall([this, height] { debris_kill_height_ = height; });
} }
auto BGDynamicsServer::CreateDrawSnapshot() -> BGDynamicsDrawSnapshot* { auto BGDynamicsServer::CreateDrawSnapshot() -> BGDynamicsDrawSnapshot* {
@ -2215,7 +2214,7 @@ auto BGDynamicsServer::CreateDrawSnapshot() -> BGDynamicsDrawSnapshot* {
} // NOLINT (yes this should be shorter) } // NOLINT (yes this should be shorter)
void BGDynamicsServer::PushTooSlowCall() { void BGDynamicsServer::PushTooSlowCall() {
PushCall([this] { thread()->PushCall([this] {
if (chunk_count_ > 0 || tendril_count_thick_ > 0 if (chunk_count_ > 0 || tendril_count_thick_ > 0
|| tendril_count_thin_ > 0) { || tendril_count_thin_ > 0) {
// Ok lets kill a small percentage of our oldest chunks. // Ok lets kill a small percentage of our oldest chunks.
@ -2313,7 +2312,7 @@ void BGDynamicsServer::Step(StepData* step_data) {
// Now generate a snapshot of our state and send it to the game thread, // Now generate a snapshot of our state and send it to the game thread,
// so they can draw us. // so they can draw us.
BGDynamicsDrawSnapshot* snapshot = CreateDrawSnapshot(); BGDynamicsDrawSnapshot* snapshot = CreateDrawSnapshot();
g_game->PushCall([snapshot] { g_game->thread()->PushCall([snapshot] {
snapshot->SetLogicThreadOwnership(); snapshot->SetLogicThreadOwnership();
g_bg_dynamics->SetDrawSnapshot(snapshot); g_bg_dynamics->SetDrawSnapshot(snapshot);
}); });
@ -2326,19 +2325,19 @@ void BGDynamicsServer::Step(StepData* step_data) {
// Job's done! // Job's done!
{ {
std::lock_guard<std::mutex> lock(step_count_mutex_); std::scoped_lock lock(step_count_mutex_);
step_count_--; step_count_--;
} }
assert(step_count_ >= 0); assert(step_count_ >= 0);
} }
void BGDynamicsServer::PushStepCall(StepData* data) { void BGDynamicsServer::PushStepCall(StepData* data) {
PushCall([this, data] { Step(data); }); thread()->PushCall([this, data] { Step(data); });
} }
void BGDynamicsServer::PushAddTerrainCall( void BGDynamicsServer::PushAddTerrainCall(
Object::Ref<CollideModelData>* collide_model) { Object::Ref<CollideModelData>* collide_model) {
PushCall([this, collide_model] { thread()->PushCall([this, collide_model] {
assert(InBGDynamicsThread()); assert(InBGDynamicsThread());
assert(collide_model != nullptr); assert(collide_model != nullptr);
@ -2639,7 +2638,7 @@ void BGDynamicsServer::UpdateShadows() {
{ {
BA_DEBUG_TIME_CHECK_BEGIN(bg_dynamic_shadow_list_lock); BA_DEBUG_TIME_CHECK_BEGIN(bg_dynamic_shadow_list_lock);
{ {
std::lock_guard<std::mutex> lock(shadow_list_mutex_); std::scoped_lock lock(shadow_list_mutex_);
for (auto&& s : shadows_) { for (auto&& s : shadows_) {
s->UpdateClientData(); s->UpdateClientData();
} }

View File

@ -5,10 +5,10 @@
#include <list> #include <list>
#include <memory> #include <memory>
#include <mutex>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "ballistica/core/module.h"
#include "ballistica/dynamics/bg/bg_dynamics.h" #include "ballistica/dynamics/bg/bg_dynamics.h"
#include "ballistica/math/matrix44f.h" #include "ballistica/math/matrix44f.h"
#include "ballistica/math/vector3f.h" #include "ballistica/math/vector3f.h"
@ -16,7 +16,7 @@
namespace ballistica { namespace ballistica {
class BGDynamicsServer : public Module { class BGDynamicsServer {
public: public:
struct Particle { struct Particle {
float x; float x;
@ -81,7 +81,7 @@ class BGDynamicsServer : public Module {
}; };
explicit BGDynamicsServer(Thread* thread); explicit BGDynamicsServer(Thread* thread);
~BGDynamicsServer() override;
auto time() const -> uint32_t { return time_; } auto time() const -> uint32_t { return time_; }
auto graphics_quality() const -> GraphicsQuality { return graphics_quality_; } auto graphics_quality() const -> GraphicsQuality { return graphics_quality_; }
@ -98,6 +98,7 @@ class BGDynamicsServer : public Module {
return spark_particles_.get(); return spark_particles_.get();
} }
auto step_count() const -> int { return step_count_; } auto step_count() const -> int { return step_count_; }
auto thread() const -> Thread* { return thread_; }
private: private:
class Terrain; class Terrain;
@ -121,6 +122,8 @@ class BGDynamicsServer : public Module {
void UpdateFuses(); void UpdateFuses();
void UpdateShadows(); void UpdateShadows();
auto CreateDrawSnapshot() -> BGDynamicsDrawSnapshot*; auto CreateDrawSnapshot() -> BGDynamicsDrawSnapshot*;
Thread* thread_{};
BGDynamicsChunkType cb_type_ = BGDynamicsChunkType::kRock; BGDynamicsChunkType cb_type_ = BGDynamicsChunkType::kRock;
dBodyID cb_body_{}; dBodyID cb_body_{};
float cb_cfm_{0.0f}; float cb_cfm_{0.0f};

View File

@ -92,32 +92,32 @@ auto Account::AccountTypeToIconString(V1AccountType type) -> std::string {
Account::Account() = default; Account::Account() = default;
auto Account::GetLoginName() -> std::string { auto Account::GetLoginName() -> std::string {
std::lock_guard<std::mutex> lock(mutex_); std::scoped_lock lock(mutex_);
return login_name_; return login_name_;
} }
auto Account::GetLoginID() -> std::string { auto Account::GetLoginID() -> std::string {
std::lock_guard<std::mutex> lock(mutex_); std::scoped_lock lock(mutex_);
return login_id_; return login_id_;
} }
auto Account::GetToken() -> std::string { auto Account::GetToken() -> std::string {
std::lock_guard<std::mutex> lock(mutex_); std::scoped_lock lock(mutex_);
return token_; return token_;
} }
auto Account::GetExtra() -> std::string { auto Account::GetExtra() -> std::string {
std::lock_guard<std::mutex> lock(mutex_); std::scoped_lock lock(mutex_);
return extra_; return extra_;
} }
auto Account::GetExtra2() -> std::string { auto Account::GetExtra2() -> std::string {
std::lock_guard<std::mutex> lock(mutex_); std::scoped_lock lock(mutex_);
return extra_2_; return extra_2_;
} }
auto Account::GetLoginState(int* state_num) -> V1LoginState { auto Account::GetLoginState(int* state_num) -> V1LoginState {
std::lock_guard<std::mutex> lock(mutex_); std::scoped_lock lock(mutex_);
if (state_num) { if (state_num) {
*state_num = login_state_num_; *state_num = login_state_num_;
} }
@ -125,18 +125,18 @@ auto Account::GetLoginState(int* state_num) -> V1LoginState {
} }
void Account::SetExtra(const std::string& extra) { void Account::SetExtra(const std::string& extra) {
std::lock_guard<std::mutex> lock(mutex_); std::scoped_lock lock(mutex_);
extra_ = extra; extra_ = extra;
} }
void Account::SetExtra2(const std::string& extra) { void Account::SetExtra2(const std::string& extra) {
std::lock_guard<std::mutex> lock(mutex_); std::scoped_lock lock(mutex_);
extra_2_ = extra; extra_2_ = extra;
} }
void Account::SetToken(const std::string& account_id, void Account::SetToken(const std::string& account_id,
const std::string& token) { const std::string& token) {
std::lock_guard<std::mutex> lock(mutex_); std::scoped_lock lock(mutex_);
// Hmm, does this compare logic belong in here? // Hmm, does this compare logic belong in here?
if (login_id_ == account_id) { if (login_id_ == account_id) {
token_ = token; token_ = token;
@ -148,7 +148,7 @@ void Account::SetLogin(V1AccountType account_type, V1LoginState login_state,
const std::string& login_id) { const std::string& login_id) {
bool call_login_did_change = false; bool call_login_did_change = false;
{ {
std::lock_guard<std::mutex> lock(mutex_); std::scoped_lock lock(mutex_);
// We call out to Python so need to be in game thread. // We call out to Python so need to be in game thread.
assert(InLogicThread()); assert(InLogicThread());
@ -183,7 +183,7 @@ void Account::SetLogin(V1AccountType account_type, V1LoginState login_state,
} }
void Account::SetProductsPurchased(const std::vector<std::string>& products) { void Account::SetProductsPurchased(const std::vector<std::string>& products) {
std::lock_guard<std::mutex> lock(mutex_); std::scoped_lock lock(mutex_);
std::unordered_map<std::string, bool> purchases_old = product_purchases_; std::unordered_map<std::string, bool> purchases_old = product_purchases_;
product_purchases_.clear(); product_purchases_.clear();
for (auto&& i : products) { for (auto&& i : products) {
@ -195,7 +195,7 @@ void Account::SetProductsPurchased(const std::vector<std::string>& products) {
} }
auto Account::GetProductPurchased(const std::string& product) -> bool { auto Account::GetProductPurchased(const std::string& product) -> bool {
std::lock_guard<std::mutex> lock(mutex_); std::scoped_lock lock(mutex_);
auto i = product_purchases_.find(product); auto i = product_purchases_.find(product);
if (i == product_purchases_.end()) { if (i == product_purchases_.end()) {
return false; return false;

View File

@ -2,6 +2,7 @@
#include "ballistica/game/connection/connection_set.h" #include "ballistica/game/connection/connection_set.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/connection/connection_to_client_udp.h" #include "ballistica/game/connection/connection_to_client_udp.h"
#include "ballistica/game/connection/connection_to_host_udp.h" #include "ballistica/game/connection/connection_to_host_udp.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
@ -215,14 +216,15 @@ void ConnectionSet::PushUDPConnectionPacketCall(
const std::vector<uint8_t>& data, const SockAddr& addr) { const std::vector<uint8_t>& data, const SockAddr& addr) {
// Avoid buffer-full errors if something is causing us to write too often; // Avoid buffer-full errors if something is causing us to write too often;
// these are unreliable messages so its ok to just drop them. // these are unreliable messages so its ok to just drop them.
if (!g_game->CheckPushSafety()) { if (!g_game->thread()->CheckPushSafety()) {
BA_LOG_ONCE( BA_LOG_ONCE(
"Ignoring excessive udp-connection input packets; (could this be a " "Ignoring excessive udp-connection input packets; (could this be a "
"flood attack?)."); "flood attack?).");
return; return;
} }
g_game->PushCall([this, data, addr] { UDPConnectionPacket(data, addr); }); g_game->thread()->PushCall(
[this, data, addr] { UDPConnectionPacket(data, addr); });
} }
auto ConnectionSet::Shutdown() -> void { auto ConnectionSet::Shutdown() -> void {
@ -352,11 +354,11 @@ auto ConnectionSet::DisconnectClient(int client_id, int ban_seconds) -> bool {
} }
void ConnectionSet::PushClientDisconnectedCall(int id) { void ConnectionSet::PushClientDisconnectedCall(int id) {
g_game->PushCall([this, id] { HandleClientDisconnected(id); }); g_game->thread()->PushCall([this, id] { HandleClientDisconnected(id); });
} }
void ConnectionSet::PushDisconnectedFromHostCall() { void ConnectionSet::PushDisconnectedFromHostCall() {
g_game->PushCall([this] { g_game->thread()->PushCall([this] {
if (connection_to_host_.exists()) { if (connection_to_host_.exists()) {
bool was_connected = connection_to_host_->can_communicate(); bool was_connected = connection_to_host_->can_communicate();
connection_to_host_.Clear(); connection_to_host_.Clear();
@ -377,7 +379,7 @@ void ConnectionSet::PushDisconnectedFromHostCall() {
void ConnectionSet::PushHostConnectedUDPCall(const SockAddr& addr, void ConnectionSet::PushHostConnectedUDPCall(const SockAddr& addr,
bool print_connect_progress) { bool print_connect_progress) {
g_game->PushCall([this, addr, print_connect_progress] { g_game->thread()->PushCall([this, addr, print_connect_progress] {
// Attempt to disconnect any clients we have, turn off public-party // Attempt to disconnect any clients we have, turn off public-party
// advertising, etc. // advertising, etc.
g_game->CleanUpBeforeConnectingToHost(); g_game->CleanUpBeforeConnectingToHost();
@ -389,7 +391,7 @@ void ConnectionSet::PushHostConnectedUDPCall(const SockAddr& addr,
} }
void ConnectionSet::PushDisconnectFromHostCall() { void ConnectionSet::PushDisconnectFromHostCall() {
g_game->PushCall([this] { g_game->thread()->PushCall([this] {
if (connection_to_host_.exists()) { if (connection_to_host_.exists()) {
connection_to_host_->RequestDisconnect(); connection_to_host_->RequestDisconnect();
} }

View File

@ -66,7 +66,7 @@ const int kMaxChatMessages = 40;
const int kKickBanSeconds = 5 * 60; const int kKickBanSeconds = 5 * 60;
Game::Game(Thread* thread) Game::Game(Thread* thread)
: Module("game", thread), : thread_(thread),
game_roster_(cJSON_CreateArray()), game_roster_(cJSON_CreateArray()),
realtimers_(new TimerList()), realtimers_(new TimerList()),
connections_(std::make_unique<ConnectionSet>()) { connections_(std::make_unique<ConnectionSet>()) {
@ -89,6 +89,9 @@ Game::Game(Thread* thread)
Context::Init(); Context::Init();
// We want to be informed when our thread is pausing.
thread->AddPauseCallback(NewLambdaRunnableRaw([this] { OnThreadPause(); }));
// Waaah does UI need to be a bs::Object? // Waaah does UI need to be a bs::Object?
// Update: yes it does in order to be a context target. // Update: yes it does in order to be a context target.
// (need to be able to create weak-refs to it). // (need to be able to create weak-refs to it).
@ -116,7 +119,7 @@ Game::Game(Thread* thread)
// This way it is more likely we can show a fatal error dialog // This way it is more likely we can show a fatal error dialog
// since the main thread won't be blocking waiting for us to init. // since the main thread won't be blocking waiting for us to init.
std::string what = e.what(); std::string what = e.what();
PushCall([what] { this->thread()->PushCall([what] {
// Just throw a standard exception since our what already // Just throw a standard exception since our what already
// contains a stack trace; if we throw an Exception we wind // contains a stack trace; if we throw an Exception we wind
// up with a useless second one. // up with a useless second one.
@ -125,8 +128,16 @@ Game::Game(Thread* thread)
} }
} }
auto Game::OnThreadPause() -> void {
ScopedSetContext cp(GetUIContextTarget());
// Let Python and internal layers do their thing.
g_python->obj(Python::ObjID::kOnAppPauseCall).Call();
g_app_internal->OnLogicThreadPause();
}
void Game::InitSpecialChars() { void Game::InitSpecialChars() {
std::lock_guard<std::mutex> lock(special_char_mutex_); std::scoped_lock lock(special_char_mutex_);
special_char_strings_[SpecialChar::kDownArrow] = "\xee\x80\x84"; special_char_strings_[SpecialChar::kDownArrow] = "\xee\x80\x84";
special_char_strings_[SpecialChar::kUpArrow] = "\xee\x80\x83"; special_char_strings_[SpecialChar::kUpArrow] = "\xee\x80\x83";
@ -244,13 +255,13 @@ void Game::ResetActivityTracking() {
#if BA_VR_BUILD #if BA_VR_BUILD
void Game::PushVRHandsState(const VRHandsState& state) { void Game::PushVRHandsState(const VRHandsState& state) {
PushCall([this, state] { vr_hands_state_ = state; }); thread()->PushCall([this, state] { vr_hands_state_ = state; });
} }
#endif // BA_VR_BUILD #endif // BA_VR_BUILD
void Game::PushMediaPruneCall(int level) { void Game::PushMediaPruneCall(int level) {
PushCall([level] { thread()->PushCall([level] {
assert(InLogicThread()); assert(InLogicThread());
g_media->Prune(level); g_media->Prune(level);
}); });
@ -260,13 +271,14 @@ void Game::PushSetV1LoginCall(V1AccountType account_type,
V1LoginState account_state, V1LoginState account_state,
const std::string& account_name, const std::string& account_name,
const std::string& account_id) { const std::string& account_id) {
PushCall([this, account_type, account_state, account_name, account_id] { thread()->PushCall([this, account_type, account_state, account_name,
account_id] {
g_account->SetLogin(account_type, account_state, account_name, account_id); g_account->SetLogin(account_type, account_state, account_name, account_id);
}); });
} }
void Game::PushInitialScreenCreatedCall() { void Game::PushInitialScreenCreatedCall() {
PushCall([this] { InitialScreenCreated(); }); thread()->PushCall([this] { InitialScreenCreated(); });
} }
void Game::InitialScreenCreated() { void Game::InitialScreenCreated() {
@ -288,22 +300,22 @@ void Game::InitialScreenCreated() {
// Set up our timers. // Set up our timers.
process_timer_ = process_timer_ =
NewThreadTimer(0, true, NewLambdaRunnable([this] { Process(); })); thread()->NewTimer(0, true, NewLambdaRunnable([this] { Process(); }));
media_prune_timer_ = media_prune_timer_ = thread()->NewTimer(
NewThreadTimer(2345, true, NewLambdaRunnable([this] { Prune(); })); 2345, true, NewLambdaRunnable([this] { PruneMedia(); }));
// Normally we schedule updates when we're asked to draw a frame. // Normally we schedule updates when we're asked to draw a frame.
// In headless mode, however, we're not drawing, so we need a dedicated // In headless mode, however, we're not drawing, so we need a dedicated
// timer to take its place. // timer to take its place.
if (HeadlessMode()) { if (HeadlessMode()) {
headless_update_timer_ = headless_update_timer_ =
NewThreadTimer(8, true, NewLambdaRunnable([this] { Update(); })); thread()->NewTimer(8, true, NewLambdaRunnable([this] { Update(); }));
} }
RunAppLaunchCommands(); RunAppLaunchCommands();
} }
void Game::Prune() { g_media->Prune(); } void Game::PruneMedia() { g_media->Prune(); }
// Launch into main menu or whatever else. // Launch into main menu or whatever else.
void Game::RunAppLaunchCommands() { void Game::RunAppLaunchCommands() {
@ -334,8 +346,6 @@ void Game::RunAppLaunchCommands() {
UpdateProcessTimer(); UpdateProcessTimer();
} }
Game::~Game() = default;
// Set up our sleeping based on what we're doing. // Set up our sleeping based on what we're doing.
void Game::UpdateProcessTimer() { void Game::UpdateProcessTimer() {
assert(InLogicThread()); assert(InLogicThread());
@ -497,7 +507,7 @@ void Game::HandleQuitOnIdle() {
if (!idle_exiting_ && idle_seconds > (idle_exit_minutes_.value() * 60.0f)) { if (!idle_exiting_ && idle_seconds > (idle_exit_minutes_.value() * 60.0f)) {
idle_exiting_ = true; idle_exiting_ = true;
PushCall([this, idle_seconds] { thread()->PushCall([this, idle_seconds] {
assert(InLogicThread()); assert(InLogicThread());
// Just go through _ba.quit() // Just go through _ba.quit()
@ -708,7 +718,7 @@ auto Game::IsInUIContext() const -> bool {
} }
void Game::PushShowURLCall(const std::string& url) { void Game::PushShowURLCall(const std::string& url) {
PushCall([url] { thread()->PushCall([url] {
assert(InLogicThread()); assert(InLogicThread());
assert(g_python); assert(g_python);
g_python->ShowURL(url); g_python->ShowURL(url);
@ -725,7 +735,7 @@ auto Game::GetForegroundContext() -> Context {
} }
void Game::PushBackButtonCall(InputDevice* input_device) { void Game::PushBackButtonCall(InputDevice* input_device) {
PushCall([this, input_device] { thread()->PushCall([this, input_device] {
assert(InLogicThread()); assert(InLogicThread());
// Ignore if UI isn't up yet. // Ignore if UI isn't up yet.
@ -747,7 +757,7 @@ void Game::PushBackButtonCall(InputDevice* input_device) {
} }
void Game::PushStringEditSetCall(const std::string& value) { void Game::PushStringEditSetCall(const std::string& value) {
PushCall([value] { thread()->PushCall([value] {
if (!g_ui) { if (!g_ui) {
Log("Error: No ui on StringEditSetEvent."); Log("Error: No ui on StringEditSetEvent.");
return; return;
@ -764,7 +774,7 @@ void Game::PushStringEditSetCall(const std::string& value) {
} }
void Game::PushStringEditCancelCall() { void Game::PushStringEditCancelCall() {
PushCall([] { thread()->PushCall([] {
if (!g_ui) { if (!g_ui) {
Log("Error: No ui in PushStringEditCancelCall."); Log("Error: No ui in PushStringEditCancelCall.");
return; return;
@ -903,7 +913,7 @@ void Game::RunMainMenu() {
// in the current visible context. // in the current visible context.
void Game::PushInGameConsoleScriptCommand(const std::string& command) { void Game::PushInGameConsoleScriptCommand(const std::string& command) {
PushCall([this, command] { thread()->PushCall([this, command] {
// These are always run in whichever context is 'visible'. // These are always run in whichever context is 'visible'.
ScopedSetContext cp(GetForegroundContext()); ScopedSetContext cp(GetForegroundContext());
PythonCommand cmd(command, "<in-game-console>"); PythonCommand cmd(command, "<in-game-console>");
@ -932,7 +942,7 @@ void Game::PushInGameConsoleScriptCommand(const std::string& command) {
// Commands run via stdin. // Commands run via stdin.
void Game::PushStdinScriptCommand(const std::string& command) { void Game::PushStdinScriptCommand(const std::string& command) {
PushCall([this, command] { thread()->PushCall([this, command] {
// These are always run in whichever context is 'visible'. // These are always run in whichever context is 'visible'.
ScopedSetContext cp(GetForegroundContext()); ScopedSetContext cp(GetForegroundContext());
PythonCommand cmd(command, "<stdin>"); PythonCommand cmd(command, "<stdin>");
@ -966,7 +976,7 @@ void Game::PushStdinScriptCommand(const std::string& command) {
} }
void Game::PushInterruptSignalCall() { void Game::PushInterruptSignalCall() {
PushCall([this] { thread()->PushCall([this] {
assert(InLogicThread()); assert(InLogicThread());
// Special case; when running under the server-wrapper, we completely // Special case; when running under the server-wrapper, we completely
@ -981,26 +991,18 @@ void Game::PushInterruptSignalCall() {
} }
void Game::PushAskUserForTelnetAccessCall() { void Game::PushAskUserForTelnetAccessCall() {
PushCall([this] { thread()->PushCall([this] {
assert(InLogicThread()); assert(InLogicThread());
ScopedSetContext cp(GetUIContext()); ScopedSetContext cp(GetUIContext());
g_python->obj(Python::ObjID::kTelnetAccessRequestCall).Call(); g_python->obj(Python::ObjID::kTelnetAccessRequestCall).Call();
}); });
} }
void Game::HandleThreadPause() {
ScopedSetContext cp(GetUIContextTarget());
// Let Python and internal layers do their thing.
g_python->obj(Python::ObjID::kOnAppPauseCall).Call();
g_app_internal->OnLogicThreadPause();
}
void Game::PushPythonCall(const Object::Ref<PythonContextCall>& call) { void Game::PushPythonCall(const Object::Ref<PythonContextCall>& call) {
// Since we're mucking with refs, need to limit to game thread. // Since we're mucking with refs, need to limit to game thread.
BA_PRECONDITION(InLogicThread()); BA_PRECONDITION(InLogicThread());
BA_PRECONDITION(call->object_strong_ref_count() > 0); BA_PRECONDITION(call->object_strong_ref_count() > 0);
PushCall([call] { thread()->PushCall([call] {
assert(call.exists()); assert(call.exists());
call->Run(); call->Run();
}); });
@ -1011,7 +1013,7 @@ void Game::PushPythonCallArgs(const Object::Ref<PythonContextCall>& call,
// Since we're mucking with refs, need to limit to game thread. // Since we're mucking with refs, need to limit to game thread.
BA_PRECONDITION(InLogicThread()); BA_PRECONDITION(InLogicThread());
BA_PRECONDITION(call->object_strong_ref_count() > 0); BA_PRECONDITION(call->object_strong_ref_count() > 0);
PushCall([call, args] { thread()->PushCall([call, args] {
assert(call.exists()); assert(call.exists());
call->Run(args.get()); call->Run(args.get());
}); });
@ -1025,7 +1027,7 @@ void Game::PushPythonWeakCall(const Object::WeakRef<PythonContextCall>& call) {
// object to be passed in. // object to be passed in.
assert(call.exists() && call->object_strong_ref_count() > 0); assert(call.exists() && call->object_strong_ref_count() > 0);
PushCall([call] { thread()->PushCall([call] {
if (call.exists()) { if (call.exists()) {
Python::ScopedCallLabel label("PythonWeakCallMessage"); Python::ScopedCallLabel label("PythonWeakCallMessage");
call->Run(); call->Run();
@ -1042,13 +1044,13 @@ void Game::PushPythonWeakCallArgs(
// object to be passed in. // object to be passed in.
assert(call.exists() && call->object_strong_ref_count() > 0); assert(call.exists() && call->object_strong_ref_count() > 0);
PushCall([call, args] { thread()->PushCall([call, args] {
if (call.exists()) call->Run(args.get()); if (call.exists()) call->Run(args.get());
}); });
} }
void Game::PushPythonRawCallable(PyObject* callable) { void Game::PushPythonRawCallable(PyObject* callable) {
PushCall([this, callable] { thread()->PushCall([this, callable] {
assert(InLogicThread()); assert(InLogicThread());
// Lets run this in the UI context. // Lets run this in the UI context.
@ -1065,7 +1067,8 @@ void Game::PushPythonRawCallable(PyObject* callable) {
void Game::PushScreenMessage(const std::string& message, void Game::PushScreenMessage(const std::string& message,
const Vector3f& color) { const Vector3f& color) {
PushCall([message, color] { g_graphics->AddScreenMessage(message, color); }); thread()->PushCall(
[message, color] { g_graphics->AddScreenMessage(message, color); });
} }
void Game::SetReplaySpeedExponent(int val) { void Game::SetReplaySpeedExponent(int val) {
@ -1113,19 +1116,19 @@ auto Game::GetUIContext() const -> Context {
} }
void Game::PushToggleManualCameraCall() { void Game::PushToggleManualCameraCall() {
PushCall([] { g_graphics->ToggleManualCamera(); }); thread()->PushCall([] { g_graphics->ToggleManualCamera(); });
} }
void Game::PushToggleDebugInfoDisplayCall() { void Game::PushToggleDebugInfoDisplayCall() {
PushCall([] { g_graphics->ToggleNetworkDebugDisplay(); }); thread()->PushCall([] { g_graphics->ToggleNetworkDebugDisplay(); });
} }
void Game::PushToggleCollisionGeometryDisplayCall() { void Game::PushToggleCollisionGeometryDisplayCall() {
PushCall([] { g_graphics->ToggleDebugDraw(); }); thread()->PushCall([] { g_graphics->ToggleDebugDraw(); });
} }
void Game::PushMainMenuPressCall(InputDevice* device) { void Game::PushMainMenuPressCall(InputDevice* device) {
PushCall([this, device] { MainMenuPress(device); }); thread()->PushCall([this, device] { MainMenuPress(device); });
} }
void Game::MainMenuPress(InputDevice* device) { void Game::MainMenuPress(InputDevice* device) {
@ -1135,7 +1138,7 @@ void Game::MainMenuPress(InputDevice* device) {
void Game::PushScreenResizeCall(float virtual_width, float virtual_height, void Game::PushScreenResizeCall(float virtual_width, float virtual_height,
float pixel_width, float pixel_height) { float pixel_width, float pixel_height) {
PushCall([=] { thread()->PushCall([=] {
ScreenResize(virtual_width, virtual_height, pixel_width, pixel_height); ScreenResize(virtual_width, virtual_height, pixel_width, pixel_height);
}); });
} }
@ -1158,7 +1161,8 @@ void Game::ScreenResize(float virtual_width, float virtual_height,
void Game::PushGameServiceAchievementListCall( void Game::PushGameServiceAchievementListCall(
const std::set<std::string>& achievements) { const std::set<std::string>& achievements) {
PushCall([this, achievements] { GameServiceAchievementList(achievements); }); thread()->PushCall(
[this, achievements] { GameServiceAchievementList(achievements); });
} }
void Game::GameServiceAchievementList( void Game::GameServiceAchievementList(
@ -1171,7 +1175,7 @@ void Game::GameServiceAchievementList(
void Game::PushScoresToBeatResponseCall(bool success, void Game::PushScoresToBeatResponseCall(bool success,
const std::list<ScoreToBeat>& scores, const std::list<ScoreToBeat>& scores,
void* py_callback) { void* py_callback) {
PushCall([this, success, scores, py_callback] { thread()->PushCall([this, success, scores, py_callback] {
ScoresToBeatResponse(success, scores, py_callback); ScoresToBeatResponse(success, scores, py_callback);
}); });
} }
@ -1185,15 +1189,16 @@ void Game::ScoresToBeatResponse(bool success,
} }
void Game::PushPlaySoundCall(SystemSoundID sound) { void Game::PushPlaySoundCall(SystemSoundID sound) {
PushCall([sound] { g_audio->PlaySound(g_media->GetSound(sound)); }); thread()->PushCall([sound] { g_audio->PlaySound(g_media->GetSound(sound)); });
} }
void Game::PushFriendScoreSetCall(const FriendScoreSet& score_set) { void Game::PushFriendScoreSetCall(const FriendScoreSet& score_set) {
PushCall([score_set] { g_python->HandleFriendScoresCB(score_set); }); thread()->PushCall(
[score_set] { g_python->HandleFriendScoresCB(score_set); });
} }
void Game::PushConfirmQuitCall() { void Game::PushConfirmQuitCall() {
PushCall([this] { thread()->PushCall([this] {
assert(InLogicThread()); assert(InLogicThread());
if (HeadlessMode()) { if (HeadlessMode()) {
Log("PushConfirmQuitCall() unhandled on headless."); Log("PushConfirmQuitCall() unhandled on headless.");
@ -1256,11 +1261,11 @@ void Game::Draw() {
} }
void Game::PushFrameDefRequest() { void Game::PushFrameDefRequest() {
PushCall([this] { Draw(); }); thread()->PushCall([this] { Draw(); });
} }
void Game::PushOnAppResumeCall() { void Game::PushOnAppResumeCall() {
PushCall([] { thread()->PushCall([] {
// Wipe out whatever input device was in control of the UI. // Wipe out whatever input device was in control of the UI.
assert(g_ui); assert(g_ui);
g_ui->SetUIInputDevice(nullptr); g_ui->SetUIInputDevice(nullptr);
@ -1348,7 +1353,7 @@ void Game::ApplyConfig() {
// FIXME: this should exist either on the client or the server; not both. // FIXME: this should exist either on the client or the server; not both.
// (and should be communicated via frameldefs/etc.) // (and should be communicated via frameldefs/etc.)
bool tv_border = g_app_config->Resolve(AppConfig::BoolID::kTVBorder); bool tv_border = g_app_config->Resolve(AppConfig::BoolID::kTVBorder);
g_graphics_server->PushCall( g_graphics_server->thread()->PushCall(
[tv_border] { g_graphics_server->set_tv_border(tv_border); }); [tv_border] { g_graphics_server->set_tv_border(tv_border); });
g_graphics->set_tv_border(tv_border); g_graphics->set_tv_border(tv_border);
@ -1427,11 +1432,11 @@ void Game::ApplyConfig() {
} }
void Game::PushApplyConfigCall() { void Game::PushApplyConfigCall() {
PushCall([this] { ApplyConfig(); }); thread()->PushCall([this] { ApplyConfig(); });
} }
void Game::PushRemoveGraphicsServerRenderHoldCall() { void Game::PushRemoveGraphicsServerRenderHoldCall() {
PushCall([] { thread()->PushCall([] {
// This call acts as a flush of sorts; when it goes through, // This call acts as a flush of sorts; when it goes through,
// we push a call to the graphics server saying its ok for it // we push a call to the graphics server saying its ok for it
// to start rendering again. Thus any already-queued-up // to start rendering again. Thus any already-queued-up
@ -1442,7 +1447,7 @@ void Game::PushRemoveGraphicsServerRenderHoldCall() {
void Game::PushFreeMediaComponentRefsCall( void Game::PushFreeMediaComponentRefsCall(
const std::vector<Object::Ref<MediaComponentData>*>& components) { const std::vector<Object::Ref<MediaComponentData>*>& components) {
PushCall([components] { thread()->PushCall([components] {
for (auto&& i : components) { for (auto&& i : components) {
delete i; delete i;
} }
@ -1450,7 +1455,7 @@ void Game::PushFreeMediaComponentRefsCall(
} }
void Game::PushHavePendingLoadsDoneCall() { void Game::PushHavePendingLoadsDoneCall() {
PushCall([] { g_media->ClearPendingLoadsDoneList(); }); thread()->PushCall([] { g_media->ClearPendingLoadsDoneList(); });
} }
void Game::ToggleConsole() { void Game::ToggleConsole() {
@ -1461,7 +1466,7 @@ void Game::ToggleConsole() {
} }
void Game::PushConsolePrintCall(const std::string& msg) { void Game::PushConsolePrintCall(const std::string& msg) {
PushCall([msg] { thread()->PushCall([msg] {
// Send them to the console if its been created or store them // Send them to the console if its been created or store them
// for when it is (unless we're headless in which case it never will). // for when it is (unless we're headless in which case it never will).
if (auto console = g_app_globals->console) { if (auto console = g_app_globals->console) {
@ -1473,14 +1478,14 @@ void Game::PushConsolePrintCall(const std::string& msg) {
} }
void Game::PushHavePendingLoadsCall() { void Game::PushHavePendingLoadsCall() {
PushCall([this] { thread()->PushCall([this] {
have_pending_loads_ = true; have_pending_loads_ = true;
UpdateProcessTimer(); UpdateProcessTimer();
}); });
} }
void Game::PushShutdownCall(bool soft) { void Game::PushShutdownCall(bool soft) {
PushCall([this, soft] { Shutdown(soft); }); thread()->PushCall([this, soft] { Shutdown(soft); });
} }
void Game::Shutdown(bool soft) { void Game::Shutdown(bool soft) {
@ -1499,7 +1504,7 @@ void Game::Shutdown(bool soft) {
// Let's do the same stuff we do when our thread is pausing. (committing // Let's do the same stuff we do when our thread is pausing. (committing
// account-client to disk, etc). // account-client to disk, etc).
HandleThreadPause(); OnThreadPause();
// Attempt to report/store outstanding log stuff. // Attempt to report/store outstanding log stuff.
g_app_internal->PutLog(false); g_app_internal->PutLog(false);
@ -1553,7 +1558,7 @@ void Game::SetLanguageKeys(
const std::unordered_map<std::string, std::string>& language) { const std::unordered_map<std::string, std::string>& language) {
assert(InLogicThread()); assert(InLogicThread());
{ {
std::lock_guard<std::mutex> lock(language_mutex_); std::scoped_lock lock(language_mutex_);
language_ = language; language_ = language;
} }
@ -1797,7 +1802,7 @@ auto Game::CompileResourceString(const std::string& s, const std::string& loc,
auto Game::GetResourceString(const std::string& key) -> std::string { auto Game::GetResourceString(const std::string& key) -> std::string {
std::string val; std::string val;
{ {
std::lock_guard<std::mutex> lock(language_mutex_); std::scoped_lock lock(language_mutex_);
auto i = language_.find(key); auto i = language_.find(key);
if (i != language_.end()) { if (i != language_.end()) {
val = i->second; val = i->second;
@ -1807,7 +1812,7 @@ auto Game::GetResourceString(const std::string& key) -> std::string {
} }
auto Game::CharStr(SpecialChar id) -> std::string { auto Game::CharStr(SpecialChar id) -> std::string {
std::lock_guard<std::mutex> lock(special_char_mutex_); std::scoped_lock lock(special_char_mutex_);
std::string val; std::string val;
auto i = special_char_strings_.find(id); auto i = special_char_strings_.find(id);
if (i != special_char_strings_.end()) { if (i != special_char_strings_.end()) {

View File

@ -13,7 +13,7 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "ballistica/core/module.h" #include "ballistica/core/object.h"
namespace ballistica { namespace ballistica {
@ -22,10 +22,10 @@ const int kMaxPartyNameCombinedSize = 25;
/// The Game Module generally runs on a dedicated thread; it manages /// The Game Module generally runs on a dedicated thread; it manages
/// all game logic, builds frame_defs to send to the graphics-server for /// all game logic, builds frame_defs to send to the graphics-server for
/// rendering, etc. /// rendering, etc.
class Game : public Module { class Game {
public: public:
explicit Game(Thread* thread); explicit Game(Thread* thread);
~Game() override;
auto LaunchHostSession(PyObject* session_type_obj, auto LaunchHostSession(PyObject* session_type_obj,
BenchmarkType benchmark_type = BenchmarkType::kNone) BenchmarkType benchmark_type = BenchmarkType::kNone)
-> void; -> void;
@ -102,7 +102,7 @@ class Game : public Module {
auto ChangeGameSpeed(int offs) -> void; auto ChangeGameSpeed(int offs) -> void;
auto ResetInput() -> void; auto ResetInput() -> void;
auto RunMainMenu() -> void; auto RunMainMenu() -> void;
auto HandleThreadPause() -> void override; auto OnThreadPause() -> void;
#if BA_VR_BUILD #if BA_VR_BUILD
auto PushVRHandsState(const VRHandsState& state) -> void; auto PushVRHandsState(const VRHandsState& state) -> void;
@ -244,6 +244,7 @@ class Game : public Module {
return connections_.get(); return connections_.get();
} }
auto mark_game_roster_dirty() -> void { game_roster_dirty_ = true; } auto mark_game_roster_dirty() -> void { game_roster_dirty_ = true; }
auto thread() const -> Thread* { return thread_; }
private: private:
auto HandleQuitOnIdle() -> void; auto HandleQuitOnIdle() -> void;
@ -258,7 +259,7 @@ class Game : public Module {
auto ScoresToBeatResponse(bool success, const std::list<ScoreToBeat>& scores, auto ScoresToBeatResponse(bool success, const std::list<ScoreToBeat>& scores,
void* py_callback) -> void; void* py_callback) -> void;
auto Prune() -> void; // Periodic pruning of dead stuff. auto PruneMedia() -> void;
auto Update() -> void; auto Update() -> void;
auto Process() -> void; auto Process() -> void;
auto UpdateKickVote() -> void; auto UpdateKickVote() -> void;
@ -277,6 +278,7 @@ class Game : public Module {
int rift_step_index_{}; int rift_step_index_{};
#endif #endif
Thread* thread_{};
std::unique_ptr<ConnectionSet> connections_; std::unique_ptr<ConnectionSet> connections_;
std::list<std::pair<millisecs_t, PlayerSpec> > banned_players_; std::list<std::pair<millisecs_t, PlayerSpec> > banned_players_;
std::list<std::string> chat_messages_; std::list<std::string> chat_messages_;

View File

@ -6,6 +6,7 @@
#include "ballistica/game/game_stream.h" #include "ballistica/game/game_stream.h"
#include "ballistica/game/player.h" #include "ballistica/game/player.h"
#include "ballistica/game/session/host_session.h" #include "ballistica/game/session/host_session.h"
#include "ballistica/generic/lambda_runnable.h"
#include "ballistica/generic/timer.h" #include "ballistica/generic/timer.h"
#include "ballistica/input/device/input_device.h" #include "ballistica/input/device/input_device.h"
#include "ballistica/media/component/collide_model.h" #include "ballistica/media/component/collide_model.h"

View File

@ -5,6 +5,7 @@
#include "ballistica/game/game_stream.h" #include "ballistica/game/game_stream.h"
#include "ballistica/game/host_activity.h" #include "ballistica/game/host_activity.h"
#include "ballistica/game/player.h" #include "ballistica/game/player.h"
#include "ballistica/generic/lambda_runnable.h"
#include "ballistica/generic/timer.h" #include "ballistica/generic/timer.h"
#include "ballistica/graphics/graphics.h" #include "ballistica/graphics/graphics.h"
#include "ballistica/input/device/input_device.h" #include "ballistica/input/device/input_device.h"

View File

@ -13,8 +13,8 @@ class Runnable : public Object {
public: public:
virtual void Run() = 0; virtual void Run() = 0;
// these are used on lots of threads; lets // these are used on lots of threads; we lock to whichever
// lock to wherever we're first referenced // thread first creates a reference to us.
auto GetThreadOwnership() const -> ThreadOwnership override; auto GetThreadOwnership() const -> ThreadOwnership override;
}; };

View File

@ -858,7 +858,7 @@ auto Graphics::GetEmptyFrameDef() -> FrameDef* {
void Graphics::ClearFrameDefDeleteList() { void Graphics::ClearFrameDefDeleteList() {
assert(InLogicThread()); assert(InLogicThread());
std::lock_guard<std::mutex> lock(frame_def_delete_list_mutex_); std::scoped_lock lock(frame_def_delete_list_mutex_);
for (auto& i : frame_def_delete_list_) { for (auto& i : frame_def_delete_list_) {
// We recycle our frame_defs so we don't have to reallocate all those // We recycle our frame_defs so we don't have to reallocate all those
@ -1422,7 +1422,7 @@ void Graphics::ClearScreenMessageTranslations() {
} }
void Graphics::ReturnCompletedFrameDef(FrameDef* frame_def) { void Graphics::ReturnCompletedFrameDef(FrameDef* frame_def) {
std::lock_guard<std::mutex> lock(frame_def_delete_list_mutex_); std::scoped_lock lock(frame_def_delete_list_mutex_);
g_graphics->frame_def_delete_list_.push_back(frame_def); g_graphics->frame_def_delete_list_.push_back(frame_def);
} }

View File

@ -30,15 +30,15 @@ void GraphicsServer::FullscreenCheck() {
} }
#endif #endif
GraphicsServer::GraphicsServer(Thread* thread) : Module("graphics", thread) { GraphicsServer::GraphicsServer(Thread* thread) : thread_(thread) {
// We're a singleton. // We're a singleton.
assert(g_graphics_server == nullptr); assert(g_graphics_server == nullptr);
g_graphics_server = this; g_graphics_server = this;
// For janky old non-event-push mode, just fall back on a timer for rendering. // For janky old non-event-push mode, just fall back on a timer for rendering.
if (!g_platform->IsEventPushMode()) { if (!g_platform->IsEventPushMode()) {
render_timer_ = NewThreadTimer(1000 / 60, true, render_timer_ = this->thread()->NewTimer(
NewLambdaRunnable([this] { TryRender(); })); 1000 / 60, true, NewLambdaRunnable([this] { TryRender(); }));
} }
} }
@ -198,7 +198,7 @@ void GraphicsServer::ReloadMedia() {
// Now tell the game thread to kick off loads for everything, flip on // Now tell the game thread to kick off loads for everything, flip on
// progress bar drawing, and then tell the graphics thread to stop ignoring // progress bar drawing, and then tell the graphics thread to stop ignoring
// frame-defs. // frame-defs.
g_game->PushCall([this] { g_game->thread()->PushCall([this] {
g_media->MarkAllMediaForLoad(); g_media->MarkAllMediaForLoad();
g_graphics->EnableProgressBar(false); g_graphics->EnableProgressBar(false);
PushRemoveRenderHoldCall(); PushRemoveRenderHoldCall();
@ -249,7 +249,7 @@ void GraphicsServer::RebuildLostContext() {
// Now tell the game thread to kick off loads for everything, flip on progress // Now tell the game thread to kick off loads for everything, flip on progress
// bar drawing, and then tell the graphics thread to stop ignoring frame-defs. // bar drawing, and then tell the graphics thread to stop ignoring frame-defs.
g_game->PushCall([this] { g_game->thread()->PushCall([this] {
g_media->MarkAllMediaForLoad(); g_media->MarkAllMediaForLoad();
g_graphics->EnableProgressBar(false); g_graphics->EnableProgressBar(false);
PushRemoveRenderHoldCall(); PushRemoveRenderHoldCall();
@ -343,8 +343,8 @@ void GraphicsServer::SetScreen(bool fullscreen, int width, int height,
#if BA_OSTYPE_MACOS && BA_XCODE_BUILD && BA_SDL_BUILD #if BA_OSTYPE_MACOS && BA_XCODE_BUILD && BA_SDL_BUILD
if (create_fullscreen_check_timer) { if (create_fullscreen_check_timer) {
NewThreadTimer(1000, false, thread()->NewTimer(1000, false,
NewLambdaRunnable([this] { FullscreenCheck(); })); NewLambdaRunnable([this] { FullscreenCheck(); }));
} }
#endif // BA_OSTYPE_MACOS #endif // BA_OSTYPE_MACOS
@ -463,7 +463,7 @@ void GraphicsServer::HandleFullContextScreenRebuild(
// Now tell the game thread to kick off loads for everything, flip on // Now tell the game thread to kick off loads for everything, flip on
// progress bar drawing, and then tell the graphics thread to stop ignoring // progress bar drawing, and then tell the graphics thread to stop ignoring
// frame-defs. // frame-defs.
g_game->PushCall([this] { g_game->thread()->PushCall([this] {
g_media->MarkAllMediaForLoad(); g_media->MarkAllMediaForLoad();
g_graphics->set_internal_components_inited(false); g_graphics->set_internal_components_inited(false);
g_graphics->EnableProgressBar(false); g_graphics->EnableProgressBar(false);
@ -708,18 +708,18 @@ void GraphicsServer::PushSetScreenCall(bool fullscreen, int width, int height,
TextureQuality texture_quality, TextureQuality texture_quality,
GraphicsQuality graphics_quality, GraphicsQuality graphics_quality,
const std::string& android_res) { const std::string& android_res) {
PushCall([=] { thread()->PushCall([=] {
SetScreen(fullscreen, width, height, texture_quality, graphics_quality, SetScreen(fullscreen, width, height, texture_quality, graphics_quality,
android_res); android_res);
}); });
} }
void GraphicsServer::PushReloadMediaCall() { void GraphicsServer::PushReloadMediaCall() {
PushCall([this] { ReloadMedia(); }); thread()->PushCall([this] { ReloadMedia(); });
} }
void GraphicsServer::PushSetScreenGammaCall(float gamma) { void GraphicsServer::PushSetScreenGammaCall(float gamma) {
PushCall([this, gamma] { thread()->PushCall([this, gamma] {
assert(InGraphicsThread()); assert(InGraphicsThread());
if (!renderer_) { if (!renderer_) {
return; return;
@ -729,7 +729,7 @@ void GraphicsServer::PushSetScreenGammaCall(float gamma) {
} }
void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) { void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) {
PushCall([this, pixel_scale] { thread()->PushCall([this, pixel_scale] {
assert(InGraphicsThread()); assert(InGraphicsThread());
if (!renderer_) { if (!renderer_) {
return; return;
@ -739,7 +739,7 @@ void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) {
} }
void GraphicsServer::PushSetVSyncCall(bool sync, bool auto_sync) { void GraphicsServer::PushSetVSyncCall(bool sync, bool auto_sync) {
PushCall([this, sync, auto_sync] { thread()->PushCall([this, sync, auto_sync] {
assert(InGraphicsThread()); assert(InGraphicsThread());
#if BA_SDL_BUILD #if BA_SDL_BUILD
@ -769,7 +769,7 @@ void GraphicsServer::PushSetVSyncCall(bool sync, bool auto_sync) {
void GraphicsServer::PushComponentUnloadCall( void GraphicsServer::PushComponentUnloadCall(
const std::vector<Object::Ref<MediaComponentData>*>& components) { const std::vector<Object::Ref<MediaComponentData>*>& components) {
PushCall([this, components] { thread()->PushCall([this, components] {
// Unload all components we were passed. // Unload all components we were passed.
for (auto&& i : components) { for (auto&& i : components) {
(**i).Unload(); (**i).Unload();
@ -781,7 +781,7 @@ void GraphicsServer::PushComponentUnloadCall(
} }
void GraphicsServer::PushRemoveRenderHoldCall() { void GraphicsServer::PushRemoveRenderHoldCall() {
PushCall([this] { thread()->PushCall([this] {
assert(render_hold_); assert(render_hold_);
render_hold_--; render_hold_--;
if (render_hold_ < 0) { if (render_hold_ < 0) {

View File

@ -9,7 +9,6 @@
#include <vector> #include <vector>
#include "ballistica/ballistica.h" #include "ballistica/ballistica.h"
#include "ballistica/core/module.h"
#include "ballistica/core/object.h" #include "ballistica/core/object.h"
#include "ballistica/math/matrix44f.h" #include "ballistica/math/matrix44f.h"
@ -17,7 +16,7 @@ namespace ballistica {
// Runs in the main thread and renders frame_defs shipped to it by the // Runs in the main thread and renders frame_defs shipped to it by the
// Graphics // Graphics
class GraphicsServer : public Module { class GraphicsServer {
public: public:
explicit GraphicsServer(Thread* thread); explicit GraphicsServer(Thread* thread);
auto PushSetScreenGammaCall(float gamma) -> void; auto PushSetScreenGammaCall(float gamma) -> void;
@ -170,7 +169,7 @@ class GraphicsServer : public Module {
} }
auto RebuildLostContext() -> void; auto RebuildLostContext() -> void;
~GraphicsServer() override; ~GraphicsServer();
auto renderer() { return renderer_; } auto renderer() { return renderer_; }
auto quality() const -> GraphicsQuality { auto quality() const -> GraphicsQuality {
@ -240,6 +239,7 @@ class GraphicsServer : public Module {
auto texture_quality_requested() const { return texture_quality_requested_; } auto texture_quality_requested() const { return texture_quality_requested_; }
auto renderer() const { return renderer_; } auto renderer() const { return renderer_; }
auto initial_screen_created() const { return initial_screen_created_; } auto initial_screen_created() const { return initial_screen_created_; }
auto thread() const -> Thread* { return thread_; }
private: private:
auto HandleFullscreenToggling(bool do_set_existing_fs, bool do_toggle_fs, auto HandleFullscreenToggling(bool do_set_existing_fs, bool do_toggle_fs,
@ -273,6 +273,7 @@ class GraphicsServer : public Module {
#if BA_ENABLE_OPENGL #if BA_ENABLE_OPENGL
std::unique_ptr<GLContext> gl_context_; std::unique_ptr<GLContext> gl_context_;
#endif #endif
Thread* thread_{};
float res_x_{}; float res_x_{};
float res_y_{}; float res_y_{};
float res_x_virtual_{0.0f}; float res_x_virtual_{0.0f};

View File

@ -826,7 +826,7 @@ auto TextGraphics::GetBigCharIndex(int c) -> int {
} }
void TextGraphics::LoadGlyphPage(uint32_t index) { void TextGraphics::LoadGlyphPage(uint32_t index) {
std::lock_guard<std::mutex> lock(glyph_load_mutex_); std::scoped_lock lock(glyph_load_mutex_);
// Its possible someone else coulda loaded it since we last checked. // Its possible someone else coulda loaded it since we last checked.
if (g_glyph_pages[index] == nullptr) { if (g_glyph_pages[index] == nullptr) {

View File

@ -5,6 +5,7 @@
#include "ballistica/app/app.h" #include "ballistica/app/app.h"
#include "ballistica/app/app_globals.h" #include "ballistica/app/app_globals.h"
#include "ballistica/audio/audio.h" #include "ballistica/audio/audio.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/connection/connection_set.h" #include "ballistica/game/connection/connection_set.h"
#include "ballistica/game/player.h" #include "ballistica/game/player.h"
#include "ballistica/graphics/renderer.h" #include "ballistica/graphics/renderer.h"
@ -305,7 +306,7 @@ Joystick::~Joystick() {
#if BA_ENABLE_SDL_JOYSTICKS #if BA_ENABLE_SDL_JOYSTICKS
assert(g_app); assert(g_app);
auto joystick = sdl_joystick_; auto joystick = sdl_joystick_;
g_app->PushCall([joystick] { SDL_JoystickClose(joystick); }); g_app->thread()->PushCall([joystick] { SDL_JoystickClose(joystick); });
sdl_joystick_ = nullptr; sdl_joystick_ = nullptr;
#else #else
Log("sdl_joystick_ set in non-sdl-joystick build destructor."); Log("sdl_joystick_ set in non-sdl-joystick build destructor.");

View File

@ -5,6 +5,7 @@
#include "ballistica/app/app_config.h" #include "ballistica/app/app_config.h"
#include "ballistica/app/app_globals.h" #include "ballistica/app/app_globals.h"
#include "ballistica/audio/audio.h" #include "ballistica/audio/audio.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/player.h" #include "ballistica/game/player.h"
#include "ballistica/graphics/camera.h" #include "ballistica/graphics/camera.h"
#include "ballistica/input/device/joystick.h" #include "ballistica/input/device/joystick.h"
@ -330,7 +331,7 @@ Input::Input() {
} }
void Input::PushCreateKeyboardInputDevices() { void Input::PushCreateKeyboardInputDevices() {
g_game->PushCall([this] { CreateKeyboardInputDevices(); }); g_game->thread()->PushCall([this] { CreateKeyboardInputDevices(); });
} }
void Input::CreateKeyboardInputDevices() { void Input::CreateKeyboardInputDevices() {
@ -346,7 +347,7 @@ void Input::CreateKeyboardInputDevices() {
} }
void Input::PushDestroyKeyboardInputDevices() { void Input::PushDestroyKeyboardInputDevices() {
g_game->PushCall([this] { DestroyKeyboardInputDevices(); }); g_game->thread()->PushCall([this] { DestroyKeyboardInputDevices(); });
} }
void Input::DestroyKeyboardInputDevices() { void Input::DestroyKeyboardInputDevices() {
@ -554,7 +555,7 @@ void Input::ShowStandardInputDeviceDisconnectedMessage(InputDevice* j) {
void Input::PushAddInputDeviceCall(InputDevice* input_device, void Input::PushAddInputDeviceCall(InputDevice* input_device,
bool standard_message) { bool standard_message) {
g_game->PushCall([this, input_device, standard_message] { g_game->thread()->PushCall([this, input_device, standard_message] {
AddInputDevice(input_device, standard_message); AddInputDevice(input_device, standard_message);
}); });
} }
@ -616,7 +617,7 @@ void Input::AddInputDevice(InputDevice* input, bool standard_message) {
void Input::PushRemoveInputDeviceCall(InputDevice* input_device, void Input::PushRemoveInputDeviceCall(InputDevice* input_device,
bool standard_message) { bool standard_message) {
g_game->PushCall([this, input_device, standard_message] { g_game->thread()->PushCall([this, input_device, standard_message] {
RemoveInputDevice(input_device, standard_message); RemoveInputDevice(input_device, standard_message);
}); });
} }
@ -1087,7 +1088,7 @@ void Input::HandleBackPress(bool from_toolbar) {
} }
void Input::PushTextInputEvent(const std::string& text) { void Input::PushTextInputEvent(const std::string& text) {
g_game->PushCall([this, text] { g_game->thread()->PushCall([this, text] {
mark_input_active(); mark_input_active();
// Ignore if input is locked. // Ignore if input is locked.
@ -1105,7 +1106,7 @@ void Input::PushTextInputEvent(const std::string& text) {
auto Input::PushJoystickEvent(const SDL_Event& event, InputDevice* input_device) auto Input::PushJoystickEvent(const SDL_Event& event, InputDevice* input_device)
-> void { -> void {
g_game->PushCall([this, event, input_device] { g_game->thread()->PushCall([this, event, input_device] {
HandleJoystickEvent(event, input_device); HandleJoystickEvent(event, input_device);
}); });
} }
@ -1137,11 +1138,11 @@ void Input::HandleJoystickEvent(const SDL_Event& event,
} }
void Input::PushKeyPressEvent(const SDL_Keysym& keysym) { void Input::PushKeyPressEvent(const SDL_Keysym& keysym) {
g_game->PushCall([this, keysym] { HandleKeyPress(&keysym); }); g_game->thread()->PushCall([this, keysym] { HandleKeyPress(&keysym); });
} }
void Input::PushKeyReleaseEvent(const SDL_Keysym& keysym) { void Input::PushKeyReleaseEvent(const SDL_Keysym& keysym) {
g_game->PushCall([this, keysym] { HandleKeyRelease(&keysym); }); g_game->thread()->PushCall([this, keysym] { HandleKeyRelease(&keysym); });
} }
void Input::HandleKeyPress(const SDL_Keysym* keysym) { void Input::HandleKeyPress(const SDL_Keysym* keysym) {
@ -1392,7 +1393,7 @@ auto Input::UpdateModKeyStates(const SDL_Keysym* keysym, bool press) -> void {
} }
auto Input::PushMouseScrollEvent(const Vector2f& amount) -> void { auto Input::PushMouseScrollEvent(const Vector2f& amount) -> void {
g_game->PushCall([this, amount] { HandleMouseScroll(amount); }); g_game->thread()->PushCall([this, amount] { HandleMouseScroll(amount); });
} }
auto Input::HandleMouseScroll(const Vector2f& amount) -> void { auto Input::HandleMouseScroll(const Vector2f& amount) -> void {
@ -1425,7 +1426,7 @@ auto Input::HandleMouseScroll(const Vector2f& amount) -> void {
auto Input::PushSmoothMouseScrollEvent(const Vector2f& velocity, bool momentum) auto Input::PushSmoothMouseScrollEvent(const Vector2f& velocity, bool momentum)
-> void { -> void {
g_game->PushCall([this, velocity, momentum] { g_game->thread()->PushCall([this, velocity, momentum] {
HandleSmoothMouseScroll(velocity, momentum); HandleSmoothMouseScroll(velocity, momentum);
}); });
} }
@ -1460,7 +1461,7 @@ auto Input::HandleSmoothMouseScroll(const Vector2f& velocity, bool momentum)
} }
auto Input::PushMouseMotionEvent(const Vector2f& position) -> void { auto Input::PushMouseMotionEvent(const Vector2f& position) -> void {
g_game->PushCall([this, position] { HandleMouseMotion(position); }); g_game->thread()->PushCall([this, position] { HandleMouseMotion(position); });
} }
auto Input::HandleMouseMotion(const Vector2f& position) -> void { auto Input::HandleMouseMotion(const Vector2f& position) -> void {
@ -1511,7 +1512,7 @@ auto Input::HandleMouseMotion(const Vector2f& position) -> void {
} }
auto Input::PushMouseDownEvent(int button, const Vector2f& position) -> void { auto Input::PushMouseDownEvent(int button, const Vector2f& position) -> void {
g_game->PushCall( g_game->thread()->PushCall(
[this, button, position] { HandleMouseDown(button, position); }); [this, button, position] { HandleMouseDown(button, position); });
} }
@ -1588,7 +1589,7 @@ auto Input::HandleMouseDown(int button, const Vector2f& position) -> void {
} }
auto Input::PushMouseUpEvent(int button, const Vector2f& position) -> void { auto Input::PushMouseUpEvent(int button, const Vector2f& position) -> void {
g_game->PushCall( g_game->thread()->PushCall(
[this, button, position] { HandleMouseUp(button, position); }); [this, button, position] { HandleMouseUp(button, position); });
} }
@ -1637,7 +1638,7 @@ auto Input::HandleMouseUp(int button, const Vector2f& position) -> void {
} }
void Input::PushTouchEvent(const TouchEvent& e) { void Input::PushTouchEvent(const TouchEvent& e) {
g_game->PushCall([e, this] { HandleTouchEvent(e); }); g_game->thread()->PushCall([e, this] { HandleTouchEvent(e); });
} }
void Input::HandleTouchEvent(const TouchEvent& e) { void Input::HandleTouchEvent(const TouchEvent& e) {

View File

@ -62,7 +62,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt,
size_t msg_len = 1 + strlen(msg + 1); size_t msg_len = 1 + strlen(msg + 1);
// This needs to be locked during any sd changes/writes. // This needs to be locked during any sd changes/writes.
std::lock_guard<std::mutex> lock(g_network_reader->sd_mutex()); std::scoped_lock lock(g_network_reader->sd_mutex());
sendto(socket, msg, static_cast_check_fit<socket_send_length_t>(msg_len), sendto(socket, msg, static_cast_check_fit<socket_send_length_t>(msg_len),
0, addr, static_cast<socklen_t>(addr_len)); 0, addr, static_cast<socklen_t>(addr_len));
@ -91,7 +91,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt,
static_cast_check_fit<uint8_t>(RemoteError::kVersionMismatch)}; static_cast_check_fit<uint8_t>(RemoteError::kVersionMismatch)};
// This needs to be locked during any sd changes/writes. // This needs to be locked during any sd changes/writes.
std::lock_guard<std::mutex> lock(g_network_reader->sd_mutex()); std::scoped_lock lock(g_network_reader->sd_mutex());
sendto(socket, reinterpret_cast<char*>(data), sizeof(data), 0, addr, sendto(socket, reinterpret_cast<char*>(data), sizeof(data), 0, addr,
static_cast<socklen_t>(addr_len)); static_cast<socklen_t>(addr_len));
break; break;
@ -135,7 +135,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt,
static_cast<uint8_t>(protocol_response)}; static_cast<uint8_t>(protocol_response)};
// This needs to be locked during any sd changes/writes. // This needs to be locked during any sd changes/writes.
std::lock_guard<std::mutex> lock(g_network_reader->sd_mutex()); std::scoped_lock lock(g_network_reader->sd_mutex());
sendto(socket, reinterpret_cast<char*>(data), sizeof(data), 0, addr, sendto(socket, reinterpret_cast<char*>(data), sizeof(data), 0, addr,
static_cast<socklen_t>(addr_len)); static_cast<socklen_t>(addr_len));
} else { } else {
@ -145,7 +145,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt,
RemoteError::kNotAcceptingConnections)}; RemoteError::kNotAcceptingConnections)};
// This needs to be locked during any sd changes/writes. // This needs to be locked during any sd changes/writes.
std::lock_guard<std::mutex> lock(g_network_reader->sd_mutex()); std::scoped_lock lock(g_network_reader->sd_mutex());
sendto(socket, reinterpret_cast<char*>(data), sizeof(data), 0, addr, sendto(socket, reinterpret_cast<char*>(data), sizeof(data), 0, addr,
static_cast<socklen_t>(addr_len)); static_cast<socklen_t>(addr_len));
} }
@ -179,7 +179,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt,
uint8_t data[1] = {BA_PACKET_REMOTE_DISCONNECT_ACK}; uint8_t data[1] = {BA_PACKET_REMOTE_DISCONNECT_ACK};
// This needs to be locked during any sd changes/writes. // This needs to be locked during any sd changes/writes.
std::lock_guard<std::mutex> lock(g_network_reader->sd_mutex()); std::scoped_lock lock(g_network_reader->sd_mutex());
sendto(socket, reinterpret_cast<char*>(data), 1, 0, addr, sendto(socket, reinterpret_cast<char*>(data), 1, 0, addr,
static_cast<socklen_t>(addr_len)); static_cast<socklen_t>(addr_len));
} }
@ -204,7 +204,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt,
static_cast_check_fit<uint8_t>(RemoteError::kNotConnected)}; static_cast_check_fit<uint8_t>(RemoteError::kNotConnected)};
// This needs to be locked during any sd changes/writes. // This needs to be locked during any sd changes/writes.
std::lock_guard<std::mutex> lock(g_network_reader->sd_mutex()); std::scoped_lock lock(g_network_reader->sd_mutex());
sendto(socket, reinterpret_cast<char*>(data), sizeof(data), 0, addr, sendto(socket, reinterpret_cast<char*>(data), sizeof(data), 0, addr,
static_cast<socklen_t>(addr_len)); static_cast<socklen_t>(addr_len));
break; break;
@ -313,7 +313,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt,
uint8_t data[2] = {BA_PACKET_REMOTE_STATE_ACK, client->next_state_id}; uint8_t data[2] = {BA_PACKET_REMOTE_STATE_ACK, client->next_state_id};
// This needs to be locked during any sd changes/writes. // This needs to be locked during any sd changes/writes.
std::lock_guard<std::mutex> lock(g_network_reader->sd_mutex()); std::scoped_lock lock(g_network_reader->sd_mutex());
sendto(socket, reinterpret_cast<char*>(data), 2, 0, addr, sendto(socket, reinterpret_cast<char*>(data), 2, 0, addr,
static_cast<socklen_t>(addr_len)); static_cast<socklen_t>(addr_len));
@ -331,7 +331,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt,
static_cast_check_fit<uint8_t>(RemoteError::kVersionMismatch)}; static_cast_check_fit<uint8_t>(RemoteError::kVersionMismatch)};
// This needs to be locked during any sd changes/writes. // This needs to be locked during any sd changes/writes.
std::lock_guard<std::mutex> lock(g_network_reader->sd_mutex()); std::scoped_lock lock(g_network_reader->sd_mutex());
sendto(socket, reinterpret_cast<char*>(data), sizeof(data), 0, addr, sendto(socket, reinterpret_cast<char*>(data), sizeof(data), 0, addr,
static_cast<socklen_t>(addr_len)); static_cast<socklen_t>(addr_len));
break; break;

View File

@ -7,20 +7,19 @@
#endif #endif
#include "ballistica/app/app_globals.h" #include "ballistica/app/app_globals.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/platform/platform.h" #include "ballistica/platform/platform.h"
namespace ballistica { namespace ballistica {
StdInputModule::StdInputModule(Thread* thread) : Module("stdin", thread) { StdInputModule::StdInputModule(Thread* thread) : thread_(thread) {
assert(g_std_input_module == nullptr); assert(g_std_input_module == nullptr);
g_std_input_module = this; g_std_input_module = this;
} }
StdInputModule::~StdInputModule() = default;
void StdInputModule::PushBeginReadCall() { void StdInputModule::PushBeginReadCall() {
PushCall([this] { thread()->PushCall([this] {
bool stdin_is_terminal = g_platform->is_stdin_a_terminal(); bool stdin_is_terminal = g_platform->is_stdin_a_terminal();
while (true) { while (true) {
@ -28,7 +27,7 @@ void StdInputModule::PushBeginReadCall() {
// We send this to the game thread so it happens AFTER the // We send this to the game thread so it happens AFTER the
// results of the last script-command message we may have just sent. // results of the last script-command message we may have just sent.
if (stdin_is_terminal) { if (stdin_is_terminal) {
g_game->PushCall([] { g_game->thread()->PushCall([] {
if (!g_app_globals->shutting_down) { if (!g_app_globals->shutting_down) {
printf(">>> "); printf(">>> ");
fflush(stdout); fflush(stdout);

View File

@ -3,17 +3,18 @@
#ifndef BALLISTICA_INPUT_STD_INPUT_MODULE_H_ #ifndef BALLISTICA_INPUT_STD_INPUT_MODULE_H_
#define BALLISTICA_INPUT_STD_INPUT_MODULE_H_ #define BALLISTICA_INPUT_STD_INPUT_MODULE_H_
#include "ballistica/core/module.h" #include "ballistica/ballistica.h"
namespace ballistica { namespace ballistica {
class StdInputModule : public Module { class StdInputModule {
public: public:
explicit StdInputModule(Thread* thread); explicit StdInputModule(Thread* thread);
~StdInputModule() override;
void PushBeginReadCall(); void PushBeginReadCall();
auto thread() const -> Thread* { return thread_; }
private: private:
Thread* thread_{};
std::string pending_input_; std::string pending_input_;
}; };

View File

@ -10,7 +10,7 @@
namespace ballistica { namespace ballistica {
auto CreateAppInternal() -> AppInternal*; auto GetAppInternal() -> AppInternal*;
class AppInternal { class AppInternal {
public: public:

View File

@ -7,6 +7,7 @@
#endif #endif
#include "ballistica/audio/audio_server.h" #include "ballistica/audio/audio_server.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/generic/timer.h" #include "ballistica/generic/timer.h"
#include "ballistica/graphics/graphics_server.h" #include "ballistica/graphics/graphics_server.h"
@ -614,7 +615,8 @@ void Media::MarkComponentForLoad(MediaComponentData* c) {
// ClearPendingLoadsDoneList) // ClearPendingLoadsDoneList)
auto media_ptr = new Object::Ref<MediaComponentData>(c); auto media_ptr = new Object::Ref<MediaComponentData>(c);
g_media_server->PushRunnable(Object::NewDeferred<PreloadRunnable>(media_ptr)); g_media_server->thread()->PushRunnable(
Object::NewDeferred<PreloadRunnable>(media_ptr));
} }
#pragma clang diagnostic push #pragma clang diagnostic push
@ -756,7 +758,7 @@ auto Media::RunPendingLoadList(std::vector<Object::Ref<T>*>* c_list) -> bool {
std::vector<Object::Ref<T>*> l_unfinished; std::vector<Object::Ref<T>*> l_unfinished;
std::vector<Object::Ref<T>*> l_finished; std::vector<Object::Ref<T>*> l_finished;
{ {
std::lock_guard<std::mutex> lock(pending_load_list_mutex_); std::scoped_lock lock(pending_load_list_mutex_);
// If we're already out of time. // If we're already out of time.
if (!flush && GetRealTime() - starttime > PENDING_LOAD_PROCESS_TIME) { if (!flush && GetRealTime() - starttime > PENDING_LOAD_PROCESS_TIME) {
@ -807,7 +809,7 @@ auto Media::RunPendingLoadList(std::vector<Object::Ref<T>*>* c_list) -> bool {
// Now add unfinished ones back onto the original list and finished ones into // Now add unfinished ones back onto the original list and finished ones into
// the done list. // the done list.
{ {
std::lock_guard<std::mutex> lock(pending_load_list_mutex_); std::scoped_lock lock(pending_load_list_mutex_);
for (auto&& i : l) { for (auto&& i : l) {
c_list->push_back(i); c_list->push_back(i);
} }
@ -1173,14 +1175,14 @@ void Media::AddPendingLoad(Object::Ref<MediaComponentData>* c) {
case MediaType::kTexture: case MediaType::kTexture:
case MediaType::kModel: { case MediaType::kModel: {
// Tell the graphics thread there's pending loads... // Tell the graphics thread there's pending loads...
std::lock_guard<std::mutex> lock(pending_load_list_mutex_); std::scoped_lock lock(pending_load_list_mutex_);
pending_loads_graphics_.push_back(c); pending_loads_graphics_.push_back(c);
break; break;
} }
case MediaType::kSound: { case MediaType::kSound: {
// Tell the audio thread there's pending loads. // Tell the audio thread there's pending loads.
{ {
std::lock_guard<std::mutex> lock(pending_load_list_mutex_); std::scoped_lock lock(pending_load_list_mutex_);
pending_loads_sounds_.push_back(c); pending_loads_sounds_.push_back(c);
} }
g_audio_server->PushHavePendingLoadsCall(); g_audio_server->PushHavePendingLoadsCall();
@ -1189,7 +1191,7 @@ void Media::AddPendingLoad(Object::Ref<MediaComponentData>* c) {
default: { default: {
// Tell the game thread there's pending loads. // Tell the game thread there's pending loads.
{ {
std::lock_guard<std::mutex> lock(pending_load_list_mutex_); std::scoped_lock lock(pending_load_list_mutex_);
pending_loads_other_.push_back(c); pending_loads_other_.push_back(c);
} }
g_game->PushHavePendingLoadsCall(); g_game->PushHavePendingLoadsCall();
@ -1201,7 +1203,7 @@ void Media::AddPendingLoad(Object::Ref<MediaComponentData>* c) {
void Media::ClearPendingLoadsDoneList() { void Media::ClearPendingLoadsDoneList() {
assert(InLogicThread()); assert(InLogicThread());
std::lock_guard<std::mutex> lock(pending_load_list_mutex_); std::scoped_lock lock(pending_load_list_mutex_);
// Our explicitly-allocated reference pointer has made it back to us here in // Our explicitly-allocated reference pointer has made it back to us here in
// the game thread. // the game thread.

View File

@ -3,13 +3,14 @@
#ifndef BALLISTICA_MEDIA_MEDIA_H_ #ifndef BALLISTICA_MEDIA_MEDIA_H_
#define BALLISTICA_MEDIA_MEDIA_H_ #define BALLISTICA_MEDIA_MEDIA_H_
#include <mutex>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include "ballistica/core/context.h" #include "ballistica/core/context.h"
#include "ballistica/core/module.h"
#include "ballistica/core/object.h" #include "ballistica/core/object.h"
#include "ballistica/generic/runnable.h"
namespace ballistica { namespace ballistica {

View File

@ -2,6 +2,7 @@
#include "ballistica/media/media_server.h" #include "ballistica/media/media_server.h"
#include "ballistica/core/thread.h"
#include "ballistica/generic/huffman.h" #include "ballistica/generic/huffman.h"
#include "ballistica/generic/timer.h" #include "ballistica/generic/timer.h"
#include "ballistica/generic/utils.h" #include "ballistica/generic/utils.h"
@ -12,7 +13,7 @@
namespace ballistica { namespace ballistica {
MediaServer::MediaServer(Thread* thread) MediaServer::MediaServer(Thread* thread)
: Module("media", thread), : thread_(thread),
writing_replay_(false), writing_replay_(false),
replay_message_bytes_(0), replay_message_bytes_(0),
replays_broken_(false), replays_broken_(false),
@ -21,14 +22,14 @@ MediaServer::MediaServer(Thread* thread)
g_media_server = this; g_media_server = this;
// get our thread to give us periodic processing time... // get our thread to give us periodic processing time...
process_timer_ = process_timer_ = this->thread()->NewTimer(
NewThreadTimer(1000, true, NewLambdaRunnable([this] { Process(); })); 1000, true, NewLambdaRunnable([this] { Process(); }));
} }
MediaServer::~MediaServer() = default; MediaServer::~MediaServer() = default;
void MediaServer::PushBeginWriteReplayCall() { void MediaServer::PushBeginWriteReplayCall() {
PushCall([this] { thread()->PushCall([this] {
if (replays_broken_) { if (replays_broken_) {
return; return;
} }
@ -79,7 +80,7 @@ void MediaServer::PushBeginWriteReplayCall() {
} }
void MediaServer::PushAddMessageToReplayCall(const std::vector<uint8_t>& data) { void MediaServer::PushAddMessageToReplayCall(const std::vector<uint8_t>& data) {
PushCall([this, data] { thread()->PushCall([this, data] {
if (replays_broken_) { if (replays_broken_) {
return; return;
} }
@ -110,7 +111,7 @@ void MediaServer::PushAddMessageToReplayCall(const std::vector<uint8_t>& data) {
} }
void MediaServer::PushEndWriteReplayCall() { void MediaServer::PushEndWriteReplayCall() {
PushCall([this] { thread()->PushCall([this] {
if (replays_broken_) { if (replays_broken_) {
return; return;
} }

View File

@ -6,21 +6,23 @@
#include <list> #include <list>
#include <vector> #include <vector>
#include "ballistica/core/module.h" #include "ballistica/core/object.h"
namespace ballistica { namespace ballistica {
class MediaServer : public Module { class MediaServer {
public: public:
explicit MediaServer(Thread* thread); explicit MediaServer(Thread* thread);
~MediaServer() override; ~MediaServer();
void PushBeginWriteReplayCall(); void PushBeginWriteReplayCall();
void PushEndWriteReplayCall(); void PushEndWriteReplayCall();
void PushAddMessageToReplayCall(const std::vector<uint8_t>& data); void PushAddMessageToReplayCall(const std::vector<uint8_t>& data);
auto thread() const -> Thread* { return thread_; }
private: private:
void Process(); void Process();
void WriteReplayMessages(); void WriteReplayMessages();
Thread* thread_{};
FILE* replay_out_file_{}; FILE* replay_out_file_{};
size_t replay_bytes_written_{}; size_t replay_bytes_written_{};
bool writing_replay_{}; bool writing_replay_{};

View File

@ -239,7 +239,7 @@ auto NetworkReader::RunThread() -> int {
"happen"); "happen");
} else if (rresult == -1) { } else if (rresult == -1) {
// This needs to be locked during any sd changes/writes. // This needs to be locked during any sd changes/writes.
std::lock_guard<std::mutex> lock(sd_mutex_); std::scoped_lock lock(sd_mutex_);
// If either of our sockets goes down lets close *both* of // If either of our sockets goes down lets close *both* of
// them. // them.
@ -258,7 +258,7 @@ auto NetworkReader::RunThread() -> int {
// sockets (we ping ourself for this purpose). // sockets (we ping ourself for this purpose).
if (paused_) { if (paused_) {
// This needs to be locked during any sd changes/writes. // This needs to be locked during any sd changes/writes.
std::lock_guard<std::mutex> lock(sd_mutex_); std::scoped_lock lock(sd_mutex_);
if (sd4_ != -1) { if (sd4_ != -1) {
g_platform->CloseSocket(sd4_); g_platform->CloseSocket(sd4_);
sd4_ = -1; sd4_ = -1;
@ -274,7 +274,7 @@ auto NetworkReader::RunThread() -> int {
break; break;
case BA_PACKET_SIMPLE_PING: { case BA_PACKET_SIMPLE_PING: {
// This needs to be locked during any sd changes/writes. // This needs to be locked during any sd changes/writes.
std::lock_guard<std::mutex> lock(sd_mutex_); std::scoped_lock lock(sd_mutex_);
char msg[1] = {BA_PACKET_SIMPLE_PONG}; char msg[1] = {BA_PACKET_SIMPLE_PONG};
sendto(sd, msg, 1, 0, reinterpret_cast<sockaddr*>(&from), sendto(sd, msg, 1, 0, reinterpret_cast<sockaddr*>(&from),
from_size); from_size);
@ -290,7 +290,7 @@ auto NetworkReader::RunThread() -> int {
std::vector<char> msg(1 + response.size()); std::vector<char> msg(1 + response.size());
msg[0] = BA_PACKET_JSON_PONG; msg[0] = BA_PACKET_JSON_PONG;
memcpy(msg.data() + 1, response.c_str(), response.size()); memcpy(msg.data() + 1, response.c_str(), response.size());
std::lock_guard<std::mutex> lock(sd_mutex_); std::scoped_lock lock(sd_mutex_);
sendto( sendto(
sd, msg.data(), sd, msg.data(),
static_cast_check_fit<socket_send_length_t>(msg.size()), static_cast_check_fit<socket_send_length_t>(msg.size()),
@ -378,7 +378,7 @@ auto NetworkReader::RunThread() -> int {
auto NetworkReader::OpenSockets() -> void { auto NetworkReader::OpenSockets() -> void {
// This needs to be locked during any socket-descriptor changes/writes. // This needs to be locked during any socket-descriptor changes/writes.
std::lock_guard<std::mutex> lock(sd_mutex_); std::scoped_lock lock(sd_mutex_);
int result; int result;
int print_port_unavailable = false; int print_port_unavailable = false;

View File

@ -2,13 +2,13 @@
#include "ballistica/networking/network_write_module.h" #include "ballistica/networking/network_write_module.h"
#include "ballistica/core/thread.h"
#include "ballistica/networking/networking.h" #include "ballistica/networking/networking.h"
#include "ballistica/networking/sockaddr.h" #include "ballistica/networking/sockaddr.h"
namespace ballistica { namespace ballistica {
NetworkWriteModule::NetworkWriteModule(Thread* thread) NetworkWriteModule::NetworkWriteModule(Thread* thread) : thread_(thread) {
: Module("networkWrite", thread) {
// we're a singleton // we're a singleton
assert(g_network_write_module == nullptr); assert(g_network_write_module == nullptr);
g_network_write_module = this; g_network_write_module = this;
@ -18,11 +18,11 @@ void NetworkWriteModule::PushSendToCall(const std::vector<uint8_t>& msg,
const SockAddr& addr) { const SockAddr& addr) {
// Avoid buffer-full errors if something is causing us to write too often; // Avoid buffer-full errors if something is causing us to write too often;
// these are unreliable messages so its ok to just drop them. // these are unreliable messages so its ok to just drop them.
if (!CheckPushSafety()) { if (!thread()->CheckPushSafety()) {
BA_LOG_ONCE("Excessive send-to calls in net-write-module."); BA_LOG_ONCE("Excessive send-to calls in net-write-module.");
return; return;
} }
PushCall([this, msg, addr] { thread()->PushCall([this, msg, addr] {
assert(g_network_reader); assert(g_network_reader);
Networking::SendTo(msg, addr); Networking::SendTo(msg, addr);
}); });

View File

@ -5,15 +5,19 @@
#include <vector> #include <vector>
#include "ballistica/core/module.h" #include "ballistica/ballistica.h"
namespace ballistica { namespace ballistica {
// this thread handles network output and whatnot // this thread handles network output and whatnot
class NetworkWriteModule : public Module { class NetworkWriteModule {
public: public:
void PushSendToCall(const std::vector<uint8_t>& msg, const SockAddr& addr); void PushSendToCall(const std::vector<uint8_t>& msg, const SockAddr& addr);
explicit NetworkWriteModule(Thread* thread); explicit NetworkWriteModule(Thread* thread);
auto thread() const -> Thread* { return thread_; }
private:
Thread* thread_{};
}; };
} // namespace ballistica } // namespace ballistica

View File

@ -159,7 +159,7 @@ void Networking::HostScanCycle() {
// Add or modify an entry for this. // Add or modify an entry for this.
{ {
std::lock_guard<std::mutex> lock(scan_results_mutex_); std::scoped_lock lock(scan_results_mutex_);
// Ignore if it looks like its us. // Ignore if it looks like its us.
if (id != GetAppInstanceUUID()) { if (id != GetAppInstanceUUID()) {
@ -197,7 +197,7 @@ auto Networking::GetScanResults() -> std::vector<Networking::ScanResultsEntry> {
std::vector<ScanResultsEntry> results; std::vector<ScanResultsEntry> results;
results.resize(scan_results_.size()); results.resize(scan_results_.size());
{ {
std::lock_guard<std::mutex> lock(scan_results_mutex_); std::scoped_lock lock(scan_results_mutex_);
int out_num = 0; int out_num = 0;
for (auto&& i : scan_results_) { for (auto&& i : scan_results_) {
ScanResultsEntryPriv& in(i.second); ScanResultsEntryPriv& in(i.second);
@ -252,7 +252,7 @@ void Networking::SendTo(const std::vector<uint8_t>& buffer,
assert(!buffer.empty()); assert(!buffer.empty());
// This needs to be locked during any sd changes/writes. // This needs to be locked during any sd changes/writes.
std::lock_guard<std::mutex> lock(g_network_reader->sd_mutex()); std::scoped_lock lock(g_network_reader->sd_mutex());
// Only send if the relevant socket is currently up.. silently ignore // Only send if the relevant socket is currently up.. silently ignore
// otherwise. // otherwise.

View File

@ -4,6 +4,7 @@
#include "ballistica/app/app_globals.h" #include "ballistica/app/app_globals.h"
#include "ballistica/core/context.h" #include "ballistica/core/context.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/networking/networking.h" #include "ballistica/networking/networking.h"
#include "ballistica/networking/networking_sys.h" #include "ballistica/networking/networking_sys.h"
@ -188,7 +189,7 @@ void TelnetServer::PushTelnetScriptCommand(const std::string& command) {
if (g_game == nullptr) { if (g_game == nullptr) {
return; return;
} }
g_game->PushCall([this, command] { g_game->thread()->PushCall([this, command] {
// These are always run in whichever context is 'visible'. // These are always run in whichever context is 'visible'.
ScopedSetContext cp(g_game->GetForegroundContext()); ScopedSetContext cp(g_game->GetForegroundContext());
if (!g_app_globals->user_ran_commands) { if (!g_app_globals->user_ran_commands) {
@ -216,7 +217,7 @@ void TelnetServer::PushTelnetScriptCommand(const std::string& command) {
void TelnetServer::PushPrint(const std::string& s) { void TelnetServer::PushPrint(const std::string& s) {
assert(g_game); assert(g_game);
g_game->PushCall([this, s] { Print(s); }); g_game->thread()->PushCall([this, s] { Print(s); });
} }
void TelnetServer::Print(const std::string& s) { void TelnetServer::Print(const std::string& s) {

View File

@ -659,20 +659,20 @@ void Platform::CreateApp() {
#endif #endif
#if BA_HEADLESS_BUILD #if BA_HEADLESS_BUILD
g_main_thread->AddModule<HeadlessApp>(); new HeadlessApp(g_main_thread);
#elif BA_RIFT_BUILD #elif BA_RIFT_BUILD
// Rift build can spin up in either VR or regular mode. // Rift build can spin up in either VR or regular mode.
if (g_app_globals->vr_mode) { if (g_app_globals->vr_mode) {
g_main_thread->AddModule<VRApp>(); new VRApp(g_main_thread);
} else { } else {
g_main_thread->AddModule<SDLApp>(); new SDLApp(g_main_thread);
} }
#elif BA_CARDBOARD_BUILD #elif BA_CARDBOARD_BUILD
g_main_thread->AddModule<VRApp>(); new VRApp(g_main_thread);
#elif BA_SDL_BUILD #elif BA_SDL_BUILD
g_main_thread->AddModule<SDLApp>(); new SDLApp(g_main_thread);
#else #else
g_main_thread->AddModule<App>(); new App(g_main_thread);
#endif #endif
// Let app do any init it needs to after it is fully constructed. // Let app do any init it needs to after it is fully constructed.
@ -705,7 +705,8 @@ void Platform::CreateAuxiliaryModules() {
g_app_globals->pausable_threads.push_back(bg_dynamics_thread); g_app_globals->pausable_threads.push_back(bg_dynamics_thread);
#endif #endif
#if !BA_HEADLESS_BUILD #if !BA_HEADLESS_BUILD
bg_dynamics_thread->AddModule<BGDynamicsServer>(); bg_dynamics_thread->PushCallSynchronous(
[bg_dynamics_thread] { new BGDynamicsServer(bg_dynamics_thread); });
#endif #endif
if (g_buildconfig.use_stdin_thread()) { if (g_buildconfig.use_stdin_thread()) {
@ -713,7 +714,8 @@ void Platform::CreateAuxiliaryModules() {
// Note: this thread blocks indefinitely for input so we don't add it to the // Note: this thread blocks indefinitely for input so we don't add it to the
// pausable list. // pausable list.
auto* std_input_thread = new Thread(ThreadIdentifier::kStdin); auto* std_input_thread = new Thread(ThreadIdentifier::kStdin);
std_input_thread->AddModule<StdInputModule>(); std_input_thread->PushCallSynchronous(
[std_input_thread] { new StdInputModule(std_input_thread); });
g_std_input_module->PushBeginReadCall(); g_std_input_module->PushBeginReadCall();
} }
} }

View File

@ -4,6 +4,7 @@
#include "ballistica/platform/sdl/sdl_app.h" #include "ballistica/platform/sdl/sdl_app.h"
#include "ballistica/app/stress_test.h"
#include "ballistica/core/thread.h" #include "ballistica/core/thread.h"
#include "ballistica/dynamics/bg/bg_dynamics.h" #include "ballistica/dynamics/bg/bg_dynamics.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
@ -340,10 +341,10 @@ SDLApp::SDLApp(Thread* thread) : App(thread) {
// something is returned; In spirit, we're pretty much doing that same // something is returned; In spirit, we're pretty much doing that same
// thing, except that we're free to handle other matters concurrently // thing, except that we're free to handle other matters concurrently
// instead of being locked in a delay call. // instead of being locked in a delay call.
NewThreadTimer(10, true, NewLambdaRunnable([this] { this->thread()->NewTimer(10, true, NewLambdaRunnable([this] {
assert(g_app); assert(g_app);
g_app->RunEvents(); g_app->RunEvents();
})); }));
} }
} }

View File

@ -2,6 +2,7 @@
#include "ballistica/python/class/python_class_activity_data.h" #include "ballistica/python/class/python_class_activity_data.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/game/host_activity.h" #include "ballistica/game/host_activity.h"
#include "ballistica/game/session/host_session.h" #include "ballistica/game/session/host_session.h"
@ -84,7 +85,7 @@ void PythonClassActivityData::tp_dealloc(PythonClassActivityData* self) {
// it if need be; otherwise do it immediately. // it if need be; otherwise do it immediately.
if (!InLogicThread()) { if (!InLogicThread()) {
Object::WeakRef<HostActivity>* h = self->host_activity_; Object::WeakRef<HostActivity>* h = self->host_activity_;
g_game->PushCall([h] { delete h; }); g_game->thread()->PushCall([h] { delete h; });
} else { } else {
delete self->host_activity_; delete self->host_activity_;
} }

View File

@ -2,6 +2,7 @@
#include "ballistica/python/class/python_class_collide_model.h" #include "ballistica/python/class/python_class_collide_model.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/media/component/collide_model.h" #include "ballistica/media/component/collide_model.h"
#include "ballistica/python/python.h" #include "ballistica/python/python.h"
@ -104,7 +105,7 @@ void PythonClassCollideModel::tp_dealloc(PythonClassCollideModel* self) {
// be; otherwise do it immediately // be; otherwise do it immediately
if (!InLogicThread()) { if (!InLogicThread()) {
Object::Ref<CollideModel>* c = self->collide_model_; Object::Ref<CollideModel>* c = self->collide_model_;
g_game->PushCall([c] { Delete(c); }); g_game->thread()->PushCall([c] { Delete(c); });
} else { } else {
Delete(self->collide_model_); Delete(self->collide_model_);
} }

View File

@ -2,6 +2,7 @@
#include "ballistica/python/class/python_class_context.h" #include "ballistica/python/class/python_class_context.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/game/host_activity.h" #include "ballistica/game/host_activity.h"
#include "ballistica/game/session/host_session.h" #include "ballistica/game/session/host_session.h"
@ -187,7 +188,7 @@ void PythonClassContext::tp_dealloc(PythonClassContext* self) {
if (!InLogicThread()) { if (!InLogicThread()) {
Context* c = self->context_; Context* c = self->context_;
Context* c2 = self->context_prev_; Context* c2 = self->context_prev_;
g_game->PushCall([c, c2] { g_game->thread()->PushCall([c, c2] {
delete c; delete c;
delete c2; delete c2;
}); });

View File

@ -2,6 +2,7 @@
#include "ballistica/python/class/python_class_context_call.h" #include "ballistica/python/class/python_class_context_call.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/python/python.h" #include "ballistica/python/python.h"
#include "ballistica/python/python_context_call.h" #include "ballistica/python/python_context_call.h"
@ -118,7 +119,7 @@ void PythonClassContextCall::tp_dealloc(PythonClassContextCall* self) {
// be; otherwise do it immediately // be; otherwise do it immediately
if (!InLogicThread()) { if (!InLogicThread()) {
Object::Ref<PythonContextCall>* c = self->context_call_; Object::Ref<PythonContextCall>* c = self->context_call_;
g_game->PushCall([c] { delete c; }); g_game->thread()->PushCall([c] { delete c; });
} else { } else {
delete self->context_call_; delete self->context_call_;
} }

View File

@ -2,6 +2,7 @@
#include "ballistica/python/class/python_class_data.h" #include "ballistica/python/class/python_class_data.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/media/component/data.h" #include "ballistica/media/component/data.h"
#include "ballistica/python/python.h" #include "ballistica/python/python.h"
@ -101,7 +102,7 @@ void PythonClassData::tp_dealloc(PythonClassData* self) {
// be; otherwise do it immediately // be; otherwise do it immediately
if (!InLogicThread()) { if (!InLogicThread()) {
Object::Ref<Data>* s = self->data_; Object::Ref<Data>* s = self->data_;
g_game->PushCall([s] { Delete(s); }); g_game->thread()->PushCall([s] { Delete(s); });
} else { } else {
Delete(self->data_); Delete(self->data_);
} }

View File

@ -2,6 +2,7 @@
#include "ballistica/python/class/python_class_input_device.h" #include "ballistica/python/class/python_class_input_device.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/player.h" #include "ballistica/game/player.h"
#include "ballistica/input/device/input_device.h" #include "ballistica/input/device/input_device.h"
#include "ballistica/python/python.h" #include "ballistica/python/python.h"
@ -142,7 +143,7 @@ void PythonClassInputDevice::tp_dealloc(PythonClassInputDevice* self) {
// until the delete goes through; could that ever be a problem? // until the delete goes through; could that ever be a problem?
if (!InLogicThread()) { if (!InLogicThread()) {
Object::WeakRef<InputDevice>* d = self->input_device_; Object::WeakRef<InputDevice>* d = self->input_device_;
g_game->PushCall([d] { delete d; }); g_game->thread()->PushCall([d] { delete d; });
} else { } else {
delete self->input_device_; delete self->input_device_;
} }

View File

@ -2,6 +2,7 @@
#include "ballistica/python/class/python_class_material.h" #include "ballistica/python/class/python_class_material.h"
#include "ballistica/core/thread.h"
#include "ballistica/dynamics/material/impact_sound_material_action.h" #include "ballistica/dynamics/material/impact_sound_material_action.h"
#include "ballistica/dynamics/material/material.h" #include "ballistica/dynamics/material/material.h"
#include "ballistica/dynamics/material/material_component.h" #include "ballistica/dynamics/material/material_component.h"
@ -147,7 +148,7 @@ void PythonClassMaterial::tp_dealloc(PythonClassMaterial* self) {
// need be.. otherwise do it immediately. // need be.. otherwise do it immediately.
if (!InLogicThread()) { if (!InLogicThread()) {
Object::Ref<Material>* ptr = self->material_; Object::Ref<Material>* ptr = self->material_;
g_game->PushCall([ptr] { Delete(ptr); }); g_game->thread()->PushCall([ptr] { Delete(ptr); });
} else { } else {
Delete(self->material_); Delete(self->material_);
} }

View File

@ -2,6 +2,7 @@
#include "ballistica/python/class/python_class_model.h" #include "ballistica/python/class/python_class_model.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/media/component/model.h" #include "ballistica/media/component/model.h"
#include "ballistica/python/python.h" #include "ballistica/python/python.h"
@ -102,7 +103,7 @@ void PythonClassModel::tp_dealloc(PythonClassModel* self) {
// be; otherwise do it immediately // be; otherwise do it immediately
if (!InLogicThread()) { if (!InLogicThread()) {
Object::Ref<Model>* m = self->model_; Object::Ref<Model>* m = self->model_;
g_game->PushCall([m] { Delete(m); }); g_game->thread()->PushCall([m] { Delete(m); });
} else { } else {
Delete(self->model_); Delete(self->model_);
} }

View File

@ -4,6 +4,7 @@
#include <list> #include <list>
#include "ballistica/core/thread.h"
#include "ballistica/game/game_stream.h" #include "ballistica/game/game_stream.h"
#include "ballistica/python/python.h" #include "ballistica/python/python.h"
#include "ballistica/scene/scene.h" #include "ballistica/scene/scene.h"
@ -113,7 +114,7 @@ void PythonClassNode::tp_dealloc(PythonClassNode* self) {
// be; otherwise do it immediately. // be; otherwise do it immediately.
if (!InLogicThread()) { if (!InLogicThread()) {
Object::WeakRef<Node>* n = self->node_; Object::WeakRef<Node>* n = self->node_;
g_game->PushCall([n] { delete n; }); g_game->thread()->PushCall([n] { delete n; });
} else { } else {
delete self->node_; delete self->node_;
} }

View File

@ -2,6 +2,7 @@
#include "ballistica/python/class/python_class_session_data.h" #include "ballistica/python/class/python_class_session_data.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/game/session/session.h" #include "ballistica/game/session/session.h"
#include "ballistica/generic/utils.h" #include "ballistica/generic/utils.h"
@ -82,7 +83,7 @@ void PythonClassSessionData::tp_dealloc(PythonClassSessionData* self) {
// until the delete goes through; could that ever be a problem? // until the delete goes through; could that ever be a problem?
if (!InLogicThread()) { if (!InLogicThread()) {
Object::WeakRef<Session>* s = self->session_; Object::WeakRef<Session>* s = self->session_;
g_game->PushCall([s] { delete s; }); g_game->thread()->PushCall([s] { delete s; });
} else { } else {
delete self->session_; delete self->session_;
} }

View File

@ -2,6 +2,7 @@
#include "ballistica/python/class/python_class_session_player.h" #include "ballistica/python/class/python_class_session_player.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/host_activity.h" #include "ballistica/game/host_activity.h"
#include "ballistica/game/player.h" #include "ballistica/game/player.h"
#include "ballistica/game/session/host_session.h" #include "ballistica/game/session/host_session.h"
@ -187,7 +188,7 @@ void PythonClassSessionPlayer::tp_dealloc(PythonClassSessionPlayer* self) {
// be; otherwise do it immediately. // be; otherwise do it immediately.
if (!InLogicThread()) { if (!InLogicThread()) {
Object::WeakRef<Player>* p = self->player_; Object::WeakRef<Player>* p = self->player_;
g_game->PushCall([p] { delete p; }); g_game->thread()->PushCall([p] { delete p; });
} else { } else {
delete self->player_; delete self->player_;
} }

View File

@ -2,6 +2,7 @@
#include "ballistica/python/class/python_class_sound.h" #include "ballistica/python/class/python_class_sound.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/media/component/sound.h" #include "ballistica/media/component/sound.h"
#include "ballistica/python/python.h" #include "ballistica/python/python.h"
@ -101,7 +102,7 @@ void PythonClassSound::tp_dealloc(PythonClassSound* self) {
// be; otherwise do it immediately // be; otherwise do it immediately
if (!InLogicThread()) { if (!InLogicThread()) {
Object::Ref<Sound>* s = self->sound_; Object::Ref<Sound>* s = self->sound_;
g_game->PushCall([s] { Delete(s); }); g_game->thread()->PushCall([s] { Delete(s); });
} else { } else {
Delete(self->sound_); Delete(self->sound_);
} }

View File

@ -2,6 +2,7 @@
#include "ballistica/python/class/python_class_texture.h" #include "ballistica/python/class/python_class_texture.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/media/component/texture.h" #include "ballistica/media/component/texture.h"
#include "ballistica/python/python.h" #include "ballistica/python/python.h"
@ -94,7 +95,7 @@ void PythonClassTexture::tp_dealloc(PythonClassTexture* self) {
// be; otherwise do it immediately. // be; otherwise do it immediately.
if (!InLogicThread()) { if (!InLogicThread()) {
Object::Ref<Texture>* t = self->texture_; Object::Ref<Texture>* t = self->texture_;
g_game->PushCall([t] { Delete(t); }); g_game->thread()->PushCall([t] { Delete(t); });
} else { } else {
Delete(self->texture_); Delete(self->texture_);
} }

View File

@ -2,6 +2,7 @@
#include "ballistica/python/class/python_class_timer.h" #include "ballistica/python/class/python_class_timer.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/python/python_context_call_runnable.h" #include "ballistica/python/python_context_call_runnable.h"
@ -166,7 +167,7 @@ void PythonClassTimer::tp_dealloc(PythonClassTimer* self) {
auto a1 = self->time_type_; auto a1 = self->time_type_;
auto a2 = self->timer_id_; auto a2 = self->timer_id_;
auto a3 = self->context_; auto a3 = self->context_;
g_game->PushCall( g_game->thread()->PushCall(
[a0, a1, a2, a3] { PythonClassTimer::DoDelete(a0, a1, a2, a3); }); [a0, a1, a2, a3] { PythonClassTimer::DoDelete(a0, a1, a2, a3); });
} else { } else {
DoDelete(self->have_timer_, self->time_type_, self->timer_id_, DoDelete(self->have_timer_, self->time_type_, self->timer_id_,

View File

@ -2,6 +2,7 @@
#include "ballistica/python/class/python_class_widget.h" #include "ballistica/python/class/python_class_widget.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/generic/utils.h" #include "ballistica/generic/utils.h"
#include "ballistica/graphics/graphics.h" #include "ballistica/graphics/graphics.h"
@ -92,7 +93,7 @@ void PythonClassWidget::tp_dealloc(PythonClassWidget* self) {
// need be // need be
if (!InLogicThread()) { if (!InLogicThread()) {
Object::WeakRef<Widget>* w = self->widget_; Object::WeakRef<Widget>* w = self->widget_;
g_game->PushCall([w] { delete w; }); g_game->thread()->PushCall([w] { delete w; });
} else { } else {
delete self->widget_; delete self->widget_;
} }

View File

@ -535,7 +535,7 @@ auto PyGetLog(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* {
BA_PYTHON_TRY; BA_PYTHON_TRY;
std::string log_fin; std::string log_fin;
{ {
std::lock_guard<std::mutex> lock(g_app_globals->log_mutex); std::scoped_lock lock(g_app_globals->log_mutex);
log_fin = g_app_globals->log; log_fin = g_app_globals->log;
} }
// we want to use something with error handling here since the last // we want to use something with error handling here since the last

View File

@ -4,6 +4,7 @@
#include "ballistica/app/app_globals.h" #include "ballistica/app/app_globals.h"
#include "ballistica/audio/audio.h" #include "ballistica/audio/audio.h"
#include "ballistica/core/thread.h"
#include "ballistica/dynamics/material/material.h" #include "ballistica/dynamics/material/material.h"
#include "ballistica/game/account.h" #include "ballistica/game/account.h"
#include "ballistica/game/friend_score_set.h" #include "ballistica/game/friend_score_set.h"
@ -853,6 +854,38 @@ auto Python::GetPyVector3f(PyObject* o) -> Vector3f {
Python::Python() = default; Python::Python() = default;
static struct PyModuleDef ba_module_def = {PyModuleDef_HEAD_INIT};
static auto ba_exec(PyObject* module) -> int {
Python::InitModuleClasses(module);
return 0;
}
static PyModuleDef_Slot ba_slots[] = {
{Py_mod_exec, reinterpret_cast<void*>(ba_exec)}, {0, NULL}};
// Called when our _ba module is getting spun up.
static auto PyInit__ba() -> PyObject* {
assert(Python::HaveGIL());
// We should be able to assign these in the initializer above,
// but older g++ chokes on it at the moment...
// (and this is still more readable than setting ALL values positionally)
assert(ba_module_def.m_size == 0); // should all be zeroed though...
// Gather our methods into a static null-terminated list.
auto* all_methods = new std::vector<PyMethodDef>{Python::GetModuleMethods()};
all_methods->push_back(PyMethodDef{nullptr, nullptr, 0, nullptr});
ba_module_def.m_methods = all_methods->data();
ba_module_def.m_slots = ba_slots;
PyObject* module = PyModuleDef_Init(&ba_module_def);
BA_PRECONDITION(module);
return module;
}
void Python::Reset(bool do_init) { void Python::Reset(bool do_init) {
assert(InLogicThread()); assert(InLogicThread());
assert(g_python); assert(g_python);
@ -939,6 +972,9 @@ void Python::Reset(bool do_init) {
config.module_search_paths_set = 1; config.module_search_paths_set = 1;
} }
// Let Python know how to spin up our _ba module.
PyImport_AppendInittab("_ba", &PyInit__ba);
// Inits our _ba module and runs Py_Initialize(). // Inits our _ba module and runs Py_Initialize().
g_app_internal->PyInitialize(&config); g_app_internal->PyInitialize(&config);
@ -1063,14 +1099,14 @@ auto Python::InitModuleClasses(PyObject* module) -> void {
} }
void Python::PushObjCall(ObjID obj_id) { void Python::PushObjCall(ObjID obj_id) {
g_game->PushCall([obj_id] { g_game->thread()->PushCall([obj_id] {
ScopedSetContext cp(g_game->GetUIContext()); ScopedSetContext cp(g_game->GetUIContext());
g_python->obj(obj_id).Call(); g_python->obj(obj_id).Call();
}); });
} }
void Python::PushObjCall(ObjID obj_id, const std::string& arg) { void Python::PushObjCall(ObjID obj_id, const std::string& arg) {
g_game->PushCall([this, obj_id, arg] { g_game->thread()->PushCall([this, obj_id, arg] {
ScopedSetContext cp(g_game->GetUIContext()); ScopedSetContext cp(g_game->GetUIContext());
PythonRef args(Py_BuildValue("(s)", arg.c_str()), PythonRef args(Py_BuildValue("(s)", arg.c_str()),
ballistica::PythonRef::kSteal); ballistica::PythonRef::kSteal);

View File

@ -4,6 +4,7 @@
#include "ballistica/app/app_globals.h" #include "ballistica/app/app_globals.h"
#include "ballistica/audio/audio.h" #include "ballistica/audio/audio.h"
#include "ballistica/generic/lambda_runnable.h"
#include "ballistica/graphics/component/empty_component.h" #include "ballistica/graphics/component/empty_component.h"
#include "ballistica/input/device/input_device.h" #include "ballistica/input/device/input_device.h"
#include "ballistica/input/input.h" #include "ballistica/input/input.h"

View File

@ -3,6 +3,7 @@
#include "ballistica/ui/widget/container_widget.h" #include "ballistica/ui/widget/container_widget.h"
#include "ballistica/audio/audio.h" #include "ballistica/audio/audio.h"
#include "ballistica/core/thread.h"
#include "ballistica/game/game.h" #include "ballistica/game/game.h"
#include "ballistica/graphics/component/empty_component.h" #include "ballistica/graphics/component/empty_component.h"
#include "ballistica/graphics/component/simple_component.h" #include "ballistica/graphics/component/simple_component.h"
@ -808,7 +809,7 @@ void ContainerWidget::Draw(RenderPass* pass, bool draw_transparent) {
// Probably not safe to delete ourself here since we're in // Probably not safe to delete ourself here since we're in
// the draw loop, but we can push a call to do it. // the draw loop, but we can push a call to do it.
Object::WeakRef<Widget> weakref(this); Object::WeakRef<Widget> weakref(this);
g_game->PushCall([weakref] { g_game->thread()->PushCall([weakref] {
Widget* w = weakref.get(); Widget* w = weakref.get();
if (w) g_ui->DeleteWidget(w); if (w) g_ui->DeleteWidget(w);
}); });
@ -863,7 +864,7 @@ void ContainerWidget::Draw(RenderPass* pass, bool draw_transparent) {
// Probably not safe to delete ourself here since we're in the // Probably not safe to delete ourself here since we're in the
// draw loop, but we can set up an event to do it. // draw loop, but we can set up an event to do it.
Object::WeakRef<Widget> weakref(this); Object::WeakRef<Widget> weakref(this);
g_game->PushCall([weakref] { g_game->thread()->PushCall([weakref] {
Widget* w = weakref.get(); Widget* w = weakref.get();
if (w) g_ui->DeleteWidget(w); if (w) g_ui->DeleteWidget(w);
}); });