From b77feb4d0eae087474171c7aa69e08286960cf42 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 9 Sep 2022 14:49:58 -0700 Subject: [PATCH] ripped out vestigial thread-module system and other c++ cleanup --- .efrocachemap | 88 ++--- CHANGELOG.md | 2 +- .../ba_data/python/._bainternal_sources_hash | 2 +- assets/src/ba_data/python/ba/_bootstrap.py | 14 +- ballisticacore-cmake/CMakeLists.txt | 2 - .../Generic/BallisticaCoreGeneric.vcxproj | 2 - .../BallisticaCoreGeneric.vcxproj.filters | 6 - .../Headless/BallisticaCoreHeadless.vcxproj | 2 - .../BallisticaCoreHeadless.vcxproj.filters | 6 - src/ballistica/app/app.cc | 44 +-- src/ballistica/app/app.h | 8 +- src/ballistica/app/app_globals.h | 1 + src/ballistica/app/headless_app.cc | 8 +- src/ballistica/app/vr_app.cc | 3 +- src/ballistica/audio/audio.cc | 8 +- src/ballistica/audio/audio.h | 1 - src/ballistica/audio/audio_server.cc | 60 ++-- src/ballistica/audio/audio_server.h | 16 +- src/ballistica/ballistica.cc | 30 +- src/ballistica/core/fatal_error.cc | 3 +- src/ballistica/core/logging.cc | 2 +- src/ballistica/core/module.cc | 58 ---- src/ballistica/core/module.h | 78 ----- src/ballistica/core/object.cc | 54 +-- src/ballistica/core/object.h | 90 +++-- src/ballistica/core/thread.cc | 320 +++++++++--------- src/ballistica/core/thread.h | 245 +++++--------- src/ballistica/dynamics/bg/bg_dynamics.cc | 10 +- .../dynamics/bg/bg_dynamics_server.cc | 51 ++- .../dynamics/bg/bg_dynamics_server.h | 9 +- src/ballistica/game/account.cc | 24 +- .../game/connection/connection_set.cc | 14 +- src/ballistica/game/game.cc | 127 +++---- src/ballistica/game/game.h | 12 +- src/ballistica/game/host_activity.cc | 1 + src/ballistica/game/session/host_session.cc | 1 + src/ballistica/generic/runnable.h | 4 +- src/ballistica/graphics/graphics.cc | 4 +- src/ballistica/graphics/graphics_server.cc | 30 +- src/ballistica/graphics/graphics_server.h | 7 +- src/ballistica/graphics/text/text_graphics.cc | 2 +- src/ballistica/input/device/joystick.cc | 3 +- src/ballistica/input/input.cc | 29 +- src/ballistica/input/remote_app.cc | 16 +- src/ballistica/input/std_input_module.cc | 9 +- src/ballistica/input/std_input_module.h | 7 +- src/ballistica/internal/app_internal.h | 2 +- src/ballistica/media/media.cc | 16 +- src/ballistica/media/media.h | 3 +- src/ballistica/media/media_server.cc | 13 +- src/ballistica/media/media_server.h | 8 +- src/ballistica/networking/network_reader.cc | 10 +- .../networking/network_write_module.cc | 8 +- .../networking/network_write_module.h | 8 +- src/ballistica/networking/networking.cc | 6 +- src/ballistica/networking/telnet_server.cc | 5 +- src/ballistica/platform/platform.cc | 18 +- src/ballistica/platform/sdl/sdl_app.cc | 9 +- .../class/python_class_activity_data.cc | 3 +- .../class/python_class_collide_model.cc | 3 +- .../python/class/python_class_context.cc | 3 +- .../python/class/python_class_context_call.cc | 3 +- .../python/class/python_class_data.cc | 3 +- .../python/class/python_class_input_device.cc | 3 +- .../python/class/python_class_material.cc | 3 +- .../python/class/python_class_model.cc | 3 +- .../python/class/python_class_node.cc | 3 +- .../python/class/python_class_session_data.cc | 3 +- .../class/python_class_session_player.cc | 3 +- .../python/class/python_class_sound.cc | 3 +- .../python/class/python_class_texture.cc | 3 +- .../python/class/python_class_timer.cc | 3 +- .../python/class/python_class_widget.cc | 3 +- .../python/methods/python_methods_system.cc | 2 +- src/ballistica/python/python.cc | 40 ++- src/ballistica/ui/ui.cc | 1 + src/ballistica/ui/widget/container_widget.cc | 5 +- 77 files changed, 782 insertions(+), 930 deletions(-) delete mode 100644 src/ballistica/core/module.cc delete mode 100644 src/ballistica/core/module.h diff --git a/.efrocachemap b/.efrocachemap index 5ba23826..d082ff3c 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -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/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", - "build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/cb/30/c0e7d89b368751cf26b1cef50664", - "build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/67/14/230fd06e06cb9040a03f6e74c1d7", - "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/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f0/13/399ce592b72284029a6291b89a0e", - "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/release/ballisticacore": "https://files.ballistica.net/cache/ba1/46/c0/9f10a2154b3ccb59a9710492c1c8", - "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/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/2e/f4/ab1229ec3bda80f8f47c3cf8bc62", - "build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/9d/3b/3bd29560197990be6a4da17604b8", - "build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d1/4f/9fb5889baf79de40ced767a75f47", - "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/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/5d/4d/70ee88bd453725a3af8469b832a9", - "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/release/ballisticacore": "https://files.ballistica.net/cache/ba1/4b/d5/01958f073d57e11b8a7af71c88b2", - "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/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ff/7f/96e6a7b76cf6c5a2b2cd381e4700", - "build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/10/58/8dea7e5b6987346f9116d3758a6b", - "build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/7f/cd/93c54dc256e1b3c3575d44efb730", - "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/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/d9/30/7b9bf572809afa6102afb5c396c4", - "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/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7f/74/101f9c7cdbea3924a3eb753372b8", - "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/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2f/69/3195bc2ac8ab95a072deb6328383", - "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/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/66/7d/4cf7e08113052eae664b714bdc64", - "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/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8a/a0/eb48807010d1e7fa6a4956d0967e", - "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/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b2/c1/92b3546f4ea454e3813062e7c2a0", - "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/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8c/ea/38c4415b9582016f40ecb9c371f6", - "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/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e0/9c/78bc9f3658e495238a9ae7095ad0", - "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/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7b/cb/93116682d10e6f868ec9215c0ab2", - "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/98/9b/43700428b53a552af3f32aa2577c", - "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/37/e2/b6da8c9dd13e91ebb705da51c653", - "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/f5/93/774197e1f445c4dee88ed2910006", - "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/2e/7c/139654fe813f9254ed5de42c7ef7", - "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/ef/ea/8ccb18f593d003984a1f56cf0c78", - "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/99/f6/7e56f9dd76d97493a53ebe755fba", - "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/cf/d5/2d58578076ced4f6e2b218b18d11", - "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/a2/07/d85112deb0bf08aa01d18f098b94", + "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/34/fd/2d0510b7da8899c78378296e38d3", + "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/bb/ee/7d33058363bb336fa9c52eb87207", + "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/bb/09/a68b6c70115f316fc5c044286e53", + "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/09/4f/fea5a0cd8b43fd4bfa91238c584e", + "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/c8/33/2067e22417e6546ebc62623a9186", + "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/2d/91/3b78ced38a1060fc6fe85438792c", + "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/d3/3b/86294ecdabdce129672784085280", + "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/29/e2/f84c711e2c7cc601dd5847cc60c1", + "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/71/bc/e74bae5930032177abc44078e056", + "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/bc/07/b08c86874771ee922da097fb0b06", + "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/e3/25/692a46a2437c11055c58d95f0a10", + "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/ec/c2/51a05d1066c208b3a17e8828238e", + "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/6b/06/86d995f8ea1b6e27ac3b9818e267", + "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/e9/9e/39dd389564d0a14f01bdb54b82ce", + "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/9d/1f/20ea75eb2a38a5aa740aebf68893", + "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/ac/38/4fc5345b0bbc0cc6965d510b2074", + "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/ec/96/b22889b59ba3279627d8de232a23", + "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/65/8c/6fa15f798f6ca73d753571f8cd4b", + "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/5a/18/c0ff4809d4550c794a516a320ed1", + "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/2f/1a/29105b0e71173826d2ea404982b6", + "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/4d/32/9ffcdc35d17ac6c9e3bb2bfd0e3c", + "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/e3/c1/f79676f11b4f4778e26933ba42d7", "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" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ada6f6c1..42844b2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. - Improved logging of missing playlist game types. - Some ba.Lstr functionality can now be used in background threads. diff --git a/assets/src/ba_data/python/._bainternal_sources_hash b/assets/src/ba_data/python/._bainternal_sources_hash index 21679383..26863289 100644 --- a/assets/src/ba_data/python/._bainternal_sources_hash +++ b/assets/src/ba_data/python/._bainternal_sources_hash @@ -1 +1 @@ -161826047581351482927423840323346436406 \ No newline at end of file +265405600297036989512577170988205019181 \ No newline at end of file diff --git a/assets/src/ba_data/python/ba/_bootstrap.py b/assets/src/ba_data/python/ba/_bootstrap.py index e1a87446..da44550e 100644 --- a/assets/src/ba_data/python/ba/_bootstrap.py +++ b/assets/src/ba_data/python/ba/_bootstrap.py @@ -3,6 +3,9 @@ """Bootstrapping.""" from __future__ import annotations +import os +import sys +import signal import threading from typing import TYPE_CHECKING @@ -11,6 +14,8 @@ import _ba if TYPE_CHECKING: from typing import Any, TextIO, Callable +_g_did_bootstrap = False # pylint: disable=invalid-name + def bootstrap() -> None: """Run bootstrapping logic. @@ -18,9 +23,10 @@ def bootstrap() -> None: This is the very first userland code that runs. It sets up low level environment bits and creates the app instance. """ - import os - import sys - import signal + global _g_did_bootstrap # pylint: disable=global-statement, invalid-name + if _g_did_bootstrap: + raise RuntimeError('Bootstrap has already been called.') + _g_did_bootstrap = True # The first thing we set up is capturing/redirecting Python # 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 # version than we expect. - expected_build = 20778 + expected_build = 20794 running_build: int = env['build_number'] if running_build != expected_build: print( diff --git a/ballisticacore-cmake/CMakeLists.txt b/ballisticacore-cmake/CMakeLists.txt index e2732f5e..010064e4 100644 --- a/ballisticacore-cmake/CMakeLists.txt +++ b/ballisticacore-cmake/CMakeLists.txt @@ -241,8 +241,6 @@ add_executable(ballisticacore ${BA_SRC_ROOT}/ballistica/core/logging.h ${BA_SRC_ROOT}/ballistica/core/macros.cc ${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.h ${BA_SRC_ROOT}/ballistica/core/thread.cc diff --git a/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj b/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj index d5eb5a98..e262bb74 100644 --- a/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj +++ b/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj @@ -232,8 +232,6 @@ - - diff --git a/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj.filters b/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj.filters index 7f4a0667..72b42275 100644 --- a/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj.filters +++ b/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj.filters @@ -130,12 +130,6 @@ ballistica\core - - ballistica\core - - - ballistica\core - ballistica\core diff --git a/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj b/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj index d17d70c4..e1c85303 100644 --- a/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj +++ b/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj @@ -227,8 +227,6 @@ - - diff --git a/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj.filters b/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj.filters index 7f4a0667..72b42275 100644 --- a/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj.filters +++ b/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj.filters @@ -130,12 +130,6 @@ ballistica\core - - ballistica\core - - - ballistica\core - ballistica\core diff --git a/src/ballistica/app/app.cc b/src/ballistica/app/app.cc index c0996f34..7c2843e1 100644 --- a/src/ballistica/app/app.cc +++ b/src/ballistica/app/app.cc @@ -17,7 +17,7 @@ namespace ballistica { App::App(Thread* thread) - : Module("app", thread), stress_test_(std::make_unique()) { + : thread_(thread), stress_test_(std::make_unique()) { assert(g_app == nullptr); g_app = this; @@ -37,8 +37,6 @@ void App::PostInit() { g_platform->SetHardwareCursorVisible(g_buildconfig.hardware_cursor()); } -App::~App() = default; - auto App::ManagesEventLoop() const -> bool { // We have 2 redundant values for essentially the same thing; // should get rid of IsEventPushMode() once we've created @@ -104,7 +102,7 @@ void App::SetScreenResolution(float width, float height) { } void App::PushShutdownCompleteCall() { - PushCall([this] { ShutdownComplete(); }); + thread()->PushCall([this] { ShutdownComplete(); }); } void App::ShutdownComplete() { @@ -154,8 +152,9 @@ void App::OnPause() { g_graphics->SetGyroEnabled(false); // IMPORTANT: Any on-pause related stuff that threads need to do must - // be done from their HandleThreadPause(). If we push runnables to them, - // they may or may not be called before the thread is actually paused. + // be done from registered pause-callbacks. If we instead push runnables + // to them from here they may or may not be called before the thread + // is actually paused. Thread::SetThreadsPaused(true); @@ -210,7 +209,7 @@ void App::OnResume() { } auto App::GetProductPrice(const std::string& product) -> std::string { - std::lock_guard lock(product_prices_mutex_); + std::scoped_lock lock(product_prices_mutex_); auto i = product_prices_.find(product); if (i == product_prices_.end()) { return ""; @@ -221,7 +220,7 @@ auto App::GetProductPrice(const std::string& product) -> std::string { void App::SetProductPrice(const std::string& product, const std::string& price) { - std::lock_guard lock(product_prices_mutex_); + std::scoped_lock lock(product_prices_mutex_); product_prices_[product] = price; } @@ -261,7 +260,7 @@ void App::PrimeEventPump() { void App::PushShowOnlineScoreUICall(const std::string& show, const std::string& game, const std::string& game_version) { - PushCall([show, game, game_version] { + thread()->PushCall([show, game, game_version] { assert(InMainThread()); 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, const std::string& telnet_password) { - PushCall([port, telnet_port, enable_telnet, telnet_password] { + thread()->PushCall([port, telnet_port, enable_telnet, telnet_password] { assert(InMainThread()); // Kick these off if they don't exist. // (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, const std::string& order_id) { - PushCall( + thread()->PushCall( [purchase, order_id] { g_platform->PurchaseAck(purchase, order_id); }); } void App::PushGetScoresToBeatCall(const std::string& level, const std::string& config, void* py_callback) { - PushCall([level, config, py_callback] { + thread()->PushCall([level, config, py_callback] { assert(InMainThread()); g_platform->GetScoresToBeat(level, config, py_callback); }); } void App::PushPurchaseCall(const std::string& item) { - PushCall([item] { + thread()->PushCall([item] { assert(InMainThread()); g_platform->Purchase(item); }); } void App::PushRestorePurchasesCall() { - PushCall([] { + thread()->PushCall([] { assert(InMainThread()); g_platform->RestorePurchases(); }); } 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, 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); }); } void App::PushSubmitScoreCall(const std::string& game, 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); }); } 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, int max_chars) { - PushCall([name, value, max_chars] { + thread()->PushCall([name, value, max_chars] { static millisecs_t last_edit_time = 0; 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) { - PushCall([this, enable, player_count] { + thread()->PushCall([this, enable, player_count] { stress_test_->SetStressTesting(enable, player_count); }); } void App::PushResetAchievementsCall() { - PushCall([] { g_platform->ResetAchievements(); }); + thread()->PushCall([] { g_platform->ResetAchievements(); }); } void App::OnBootstrapComplete() { @@ -386,7 +386,7 @@ void App::OnBootstrapComplete() { } void App::PushCursorUpdate(bool vis) { - PushCall([vis] { + thread()->PushCall([vis] { assert(InMainThread()); g_platform->SetHardwareCursorVisible(vis); }); diff --git a/src/ballistica/app/app.h b/src/ballistica/app/app.h index 3bfa0878..2f6052af 100644 --- a/src/ballistica/app/app.h +++ b/src/ballistica/app/app.h @@ -8,17 +8,17 @@ #include #include -#include "ballistica/core/module.h" +#include "ballistica/app/stress_test.h" +#include "ballistica/ballistica.h" namespace ballistica { /// Our high level app interface module. /// It runs in the main thread and is what platform wrappers /// should primarily interact with. -class App : public Module { +class App { public: explicit App(Thread* thread); - ~App() override; /// This gets run after the constructor completes. /// 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, const std::string& telnet_password) -> void; auto PushShutdownCompleteCall() -> void; + auto thread() const -> Thread* { return thread_; } private: auto UpdatePauseResume() -> void; auto OnPause() -> void; auto OnResume() -> void; auto ShutdownComplete() -> void; + Thread* thread_{}; bool done_{}; bool server_wrapper_managed_{}; bool sys_paused_app_{}; diff --git a/src/ballistica/app/app_globals.h b/src/ballistica/app/app_globals.h index b0813107..e65d510a 100644 --- a/src/ballistica/app/app_globals.h +++ b/src/ballistica/app/app_globals.h @@ -31,6 +31,7 @@ class AppGlobals { /// Program argument values (on applicable platforms). char** argv{}; + bool threads_paused{}; std::unordered_map node_types; std::unordered_map node_types_by_id; std::unordered_map node_message_types; diff --git a/src/ballistica/app/headless_app.cc b/src/ballistica/app/headless_app.cc index 817fef47..4da24f9e 100644 --- a/src/ballistica/app/headless_app.cc +++ b/src/ballistica/app/headless_app.cc @@ -13,10 +13,10 @@ HeadlessApp::HeadlessApp(Thread* thread) : App(thread) { // Handle a few misc things like stress-test updates. // (SDL builds set up a similar timer so we need to also). // This can probably go away at some point. - NewThreadTimer(10, true, NewLambdaRunnable([this] { - assert(g_app); - g_app->RunEvents(); - })); + this->thread()->NewTimer(10, true, NewLambdaRunnable([this] { + assert(g_app); + g_app->RunEvents(); + })); } } // namespace ballistica diff --git a/src/ballistica/app/vr_app.cc b/src/ballistica/app/vr_app.cc index 25de8698..2fa3569f 100644 --- a/src/ballistica/app/vr_app.cc +++ b/src/ballistica/app/vr_app.cc @@ -3,6 +3,7 @@ #include "ballistica/app/vr_app.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.h" #include "ballistica/graphics/graphics_server.h" #include "ballistica/graphics/renderer.h" @@ -13,7 +14,7 @@ VRApp::VRApp(Thread* thread) : App(thread) {} auto VRApp::PushVRSimpleRemoteStateCall(const VRSimpleRemoteState& state) -> void { - PushCall([this, state] { + thread()->PushCall([this, state] { // Convert this to a full hands state, adding in some simple elbow // positioning of our own and left/right. VRHandsState s; diff --git a/src/ballistica/audio/audio.cc b/src/ballistica/audio/audio.cc index ee2f2fa3..95238edc 100644 --- a/src/ballistica/audio/audio.cc +++ b/src/ballistica/audio/audio.cc @@ -4,6 +4,7 @@ #include "ballistica/audio/audio_server.h" #include "ballistica/audio/audio_source.h" +#include "ballistica/core/thread.h" #include "ballistica/media/data/sound_data.h" namespace ballistica { @@ -40,11 +41,12 @@ void Audio::SetListenerOrientation(const Vector3f& forward, // This stops a particular sound play ID only. 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) { - g_audio_server->PushCall( + g_audio_server->thread()->PushCall( [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. // Otherwise, theoretically, the audio thread could make our source // available again before we can use it. - std::lock_guard lock(available_sources_mutex_); + std::lock_guard lock(available_sources_mutex_); // If there's an available source, reserve and return it. auto i = available_sources_.begin(); diff --git a/src/ballistica/audio/audio.h b/src/ballistica/audio/audio.h index 4185d5b6..3a0b40d0 100644 --- a/src/ballistica/audio/audio.h +++ b/src/ballistica/audio/audio.h @@ -7,7 +7,6 @@ #include #include -#include "ballistica/core/module.h" #include "ballistica/core/object.h" namespace ballistica { diff --git a/src/ballistica/audio/audio_server.cc b/src/ballistica/audio/audio_server.cc index fec5d497..598bbc2e 100644 --- a/src/ballistica/audio/audio_server.cc +++ b/src/ballistica/audio/audio_server.cc @@ -8,6 +8,7 @@ #include "ballistica/audio/audio_source.h" #include "ballistica/audio/audio_streamer.h" #include "ballistica/audio/ogg_stream.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.h" #include "ballistica/generic/timer.h" #include "ballistica/math/vector3f.h" @@ -195,7 +196,7 @@ void AudioServer::SetPaused(bool pause) { } 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); if (s) { 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) { - PushCall([this, play_id, val] { + thread()->PushCall([this, play_id, val] { ThreadSource* s = GetPlayingSound(play_id); if (s) { s->SetPositional(val); @@ -214,7 +215,7 @@ void AudioServer::PushSourceSetPositionalCall(uint32_t play_id, bool val) { void AudioServer::PushSourceSetPositionCall(uint32_t play_id, const Vector3f& p) { - PushCall([this, play_id, p] { + thread()->PushCall([this, play_id, p] { ThreadSource* s = GetPlayingSound(play_id); if (s) { 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) { - PushCall([this, play_id, val] { + thread()->PushCall([this, play_id, val] { ThreadSource* s = GetPlayingSound(play_id); if (s) { 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) { - PushCall([this, play_id, val] { + thread()->PushCall([this, play_id, val] { ThreadSource* s = GetPlayingSound(play_id); if (s) { 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) { - PushCall([this, play_id, val] { + thread()->PushCall([this, play_id, val] { ThreadSource* s = GetPlayingSound(play_id); if (s) { s->SetLooping(val); @@ -251,7 +252,7 @@ void AudioServer::PushSourceSetLoopingCall(uint32_t play_id, bool val) { void AudioServer::PushSourcePlayCall(uint32_t play_id, Object::Ref* sound) { - PushCall([this, play_id, sound] { + thread()->PushCall([this, play_id, sound] { ThreadSource* s = GetPlayingSound(play_id); // 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) { - PushCall([this, play_id] { + thread()->PushCall([this, play_id] { ThreadSource* s = GetPlayingSound(play_id); if (s) { s->Stop(); @@ -280,7 +281,7 @@ void AudioServer::PushSourceStopCall(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); assert(s); s->client_source()->Lock(5); @@ -292,11 +293,11 @@ void AudioServer::PushSourceEndCall(uint32_t play_id) { } void AudioServer::PushResetCall() { - PushCall([this] { Reset(); }); + thread()->PushCall([this] { Reset(); }); } void AudioServer::PushSetListenerPositionCall(const Vector3f& p) { - PushCall([this, p] { + thread()->PushCall([this, p] { #if BA_ENABLE_AUDIO if (!paused_) { 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, const Vector3f& up) { - PushCall([this, forward, up] { + thread()->PushCall([this, forward, up] { #if BA_ENABLE_AUDIO if (!paused_) { 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) - : Module("audio", thread), - impl_{new AudioServer::Impl()} -// impl_{std::make_unique()} -{ + : thread_(thread), impl_{new AudioServer::Impl()} { // we're a singleton... assert(g_audio_server == nullptr); g_audio_server = this; + thread->AddPauseCallback(NewLambdaRunnableRaw([this] { OnThreadPause(); })); + thread->AddResumeCallback(NewLambdaRunnableRaw([this] { OnThreadResume(); })); + // Get our thread to give us periodic processing time. - process_timer_ = NewThreadTimer(kAudioProcessIntervalNormal, true, - NewLambdaRunnable([this] { Process(); })); + process_timer_ = thread->NewTimer(kAudioProcessIntervalNormal, true, + NewLambdaRunnable([this] { Process(); })); #if BA_ENABLE_AUDIO @@ -782,8 +783,7 @@ void AudioServer::ThreadSource::UpdateAvailability() { if (!busy) { if (g_audio->available_sources_mutex().try_lock()) { - std::lock_guard lock(g_audio->available_sources_mutex(), - std::adopt_lock); + std::lock_guard lock(g_audio->available_sources_mutex(), std::adopt_lock); Stop(); Reset(); #if BA_DEBUG_BUILD @@ -1074,18 +1074,18 @@ void AudioServer::ThreadSource::UpdatePitch() { } 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); SetMusicVolume(music_volume); }); } void AudioServer::PushSetSoundPitchCall(float val) { - PushCall([this, val] { SetSoundPitch(val); }); + thread()->PushCall([this, val] { SetSoundPitch(val); }); } void AudioServer::PushSetPausedCall(bool pause) { - PushCall([this, pause] { + thread()->PushCall([this, pause] { if (g_buildconfig.ostype_android()) { Log("Error: Shouldn't be getting SetPausedCall on android."); } @@ -1095,7 +1095,7 @@ void AudioServer::PushSetPausedCall(bool pause) { void AudioServer::PushComponentUnloadCall( const std::vector*>& components) { - PushCall([this, components] { + thread()->PushCall([this, components] { // Unload all components we were passed... for (auto&& i : components) { (**i).Unload(); @@ -1107,7 +1107,7 @@ void AudioServer::PushComponentUnloadCall( } void AudioServer::PushHavePendingLoadsCall() { - PushCall([this] { + thread()->PushCall([this] { have_pending_loads_ = true; UpdateTimerInterval(); }); @@ -1115,16 +1115,16 @@ void AudioServer::PushHavePendingLoadsCall() { void AudioServer::AddSoundRefDelete(const Object::Ref* c) { { - std::lock_guard lock(sound_ref_delete_list_mutex_); + std::scoped_lock lock(sound_ref_delete_list_mutex_); sound_ref_delete_list_.push_back(c); } // 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() { assert(InLogicThread()); - std::lock_guard lock(sound_ref_delete_list_mutex_); + std::scoped_lock lock(sound_ref_delete_list_mutex_); for (const Object::Ref* i : sound_ref_delete_list_) { 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() { assert(!InAudioThread()); diff --git a/src/ballistica/audio/audio_server.h b/src/ballistica/audio/audio_server.h index 53a5f00e..d747b98e 100644 --- a/src/ballistica/audio/audio_server.h +++ b/src/ballistica/audio/audio_server.h @@ -4,14 +4,15 @@ #define BALLISTICA_AUDIO_AUDIO_SERVER_H_ #include +#include #include -#include "ballistica/core/module.h" +#include "ballistica/core/object.h" namespace ballistica { /// A module that handles audio processing. -class AudioServer : public Module { +class AudioServer { public: static auto source_id_from_play_id(uint32_t play_id) -> uint32_t { return play_id & 0xFFFFu; @@ -27,9 +28,6 @@ class AudioServer : public Module { void PushSetSoundPitchCall(float val); void PushSetPausedCall(bool pause); - void HandleThreadPause() override; - void HandleThreadResume() override; - static void BeginInterruption(); static void EndInterruption(); @@ -64,11 +62,16 @@ class AudioServer : public Module { // Stop a sound from playing if it exists. void StopSound(uint32_t play_id); + auto thread() const -> Thread* { return thread_; } + private: class ThreadSource; struct Impl; - ~AudioServer() override; + ~AudioServer(); + + auto OnThreadPause() -> void; + auto OnThreadResume() -> void; void SetPaused(bool paused); @@ -104,6 +107,7 @@ class AudioServer : public Module { // std::unique_ptr impl_{}; Impl* impl_{}; + Thread* thread_{}; Timer* process_timer_{}; bool have_pending_loads_{}; bool paused_{}; diff --git a/src/ballistica/ballistica.cc b/src/ballistica/ballistica.cc index bd3e626b..48a59a19 100644 --- a/src/ballistica/ballistica.cc +++ b/src/ballistica/ballistica.cc @@ -22,7 +22,7 @@ namespace ballistica { // 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"; // Our standalone globals. @@ -87,7 +87,7 @@ auto BallisticaMain(int argc, char** argv) -> int { // ------------------------------------------------------------------------- g_app_globals = new AppGlobals(argc, argv); - g_app_internal = CreateAppInternal(); + g_app_internal = GetAppInternal(); g_platform = Platform::Create(); g_account = new Account(); g_utils = new Utils(); @@ -109,15 +109,21 @@ auto BallisticaMain(int argc, char** argv) -> int { auto* network_write_thread = new Thread(ThreadIdentifier::kNetworkWrite); g_app_globals->pausable_threads.push_back(network_write_thread); - // And add our other standard modules to them. - logic_thread->AddModule(); - network_write_thread->AddModule(); - media_thread->AddModule(); - g_main_thread->AddModule(); - audio_thread->AddModule(); + // Spin up our subsystems in those threads. + logic_thread->PushCallSynchronous( + [logic_thread] { new Game(logic_thread); }); + network_write_thread->PushCallSynchronous([network_write_thread] { + new NetworkWriteModule(network_write_thread); + }); + 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. - // (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(); // 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 // until the app quits. // 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 - // manually pump events until drawing begins (otherwise it will never + // if the main thread event loop is driven by frame draws, it may need + // to manually pump events until drawing begins (otherwise it will never // process the 'create-screen' event and wind up deadlocked). 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 (t != g_app_globals->last_real_time_ticks) { - std::lock_guard 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; // GetTicks() is supposed to be monotonic, but I've seen 'passed' diff --git a/src/ballistica/core/fatal_error.cc b/src/ballistica/core/fatal_error.cc index e04b78c4..7a85df82 100644 --- a/src/ballistica/core/fatal_error.cc +++ b/src/ballistica/core/fatal_error.cc @@ -4,6 +4,7 @@ #include "ballistica/app/app.h" #include "ballistica/core/logging.h" +#include "ballistica/core/thread.h" #include "ballistica/internal/app_internal.h" #include "ballistica/platform/platform.h" @@ -109,7 +110,7 @@ auto FatalError::DoBlockingFatalErrorDialog(const std::string& message) bool finished{}; bool* startedptr{&started}; bool* finishedptr{&finished}; - g_app->PushCall([message, startedptr, finishedptr] { + g_app->thread()->PushCall([message, startedptr, finishedptr] { *startedptr = true; g_platform->BlockingFatalErrorDialog(message); *finishedptr = true; diff --git a/src/ballistica/core/logging.cc b/src/ballistica/core/logging.cc index 523691d7..26594cb2 100644 --- a/src/ballistica/core/logging.cc +++ b/src/ballistica/core/logging.cc @@ -69,7 +69,7 @@ void Logging::Log(const std::string& msg, bool to_stdout, bool to_server) { // Add to our complete log. if (g_app_globals != nullptr) { - std::lock_guard lock(g_app_globals->log_mutex); + std::scoped_lock lock(g_app_globals->log_mutex); if (!g_app_globals->log_full) { (g_app_globals->log) += (msg + "\n"); if ((g_app_globals->log).size() > 10000) { diff --git a/src/ballistica/core/module.cc b/src/ballistica/core/module.cc deleted file mode 100644 index 23a4859b..00000000 --- a/src/ballistica/core/module.cc +++ /dev/null @@ -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) -> 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 runnables; - runnables_.swap(runnables); - for (Runnable* i : runnables) { - i->Run(); - delete i; - } -} - -} // namespace ballistica diff --git a/src/ballistica/core/module.h b/src/ballistica/core/module.h deleted file mode 100644 index fb79f9e9..00000000 --- a/src/ballistica/core/module.h +++ /dev/null @@ -1,78 +0,0 @@ -// Released under the MIT License. See LICENSE for details. - -#ifndef BALLISTICA_CORE_MODULE_H_ -#define BALLISTICA_CORE_MODULE_H_ - -#include -#include -#include -#include - -#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 - 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) -> Timer*; - - private: - std::string name_; - int id_{}; - std::list runnables_; - Thread* thread_{}; -}; - -} // namespace ballistica - -#endif // BALLISTICA_CORE_MODULE_H_ diff --git a/src/ballistica/core/object.cc b/src/ballistica/core/object.cc index ebf294e4..67f5e9e9 100644 --- a/src/ballistica/core/object.cc +++ b/src/ballistica/core/object.cc @@ -15,7 +15,7 @@ void Object::PrintObjects() { #if BA_DEBUG_BUILD std::string s; { - std::lock_guard 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 " + std::to_string(GetRealTime()) + ";"; @@ -66,7 +66,7 @@ Object::Object() { object_birth_time_ = GetRealTime(); // Add ourself to the global object list. - std::lock_guard lock(g_app_globals->object_list_mutex); + std::scoped_lock lock(g_app_globals->object_list_mutex); object_prev_ = nullptr; object_next_ = g_app_globals->object_list_first; g_app_globals->object_list_first = this; @@ -80,7 +80,7 @@ Object::Object() { Object::~Object() { #if BA_DEBUG_BUILD // Pull ourself from the global obj list. - std::lock_guard lock(g_app_globals->object_list_mutex); + std::scoped_lock lock(g_app_globals->object_list_mutex); if (object_next_) { object_next_->object_prev_ = object_prev_; } @@ -96,20 +96,19 @@ Object::~Object() { // Avoiding Log for these low level errors; can lead to deadlock. printf( "Warning: Object is dying with non-zero ref-count; this is bad. " - "(this " - "might mean the object raised an exception in its constructor after " - "being strong-referenced first).\n"); + "(this might mean the object raised an exception in its constructor" + " after being strong-referenced first).\n"); } #endif // BA_DEBUG_BUILD // Invalidate all our weak refs. // 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. - // Also we can take a few shortcuts here since we know we're deleting the - // entire list, not just one object. + // thread-check since virtual functions won't work as expected in a + // destructor. Also we can take a few shortcuts here since we know + // we're deleting the entire list, not just one object. while (object_weak_refs_) { - auto tmp = object_weak_refs_; + auto tmp{object_weak_refs_}; object_weak_refs_ = tmp->next_; tmp->prev_ = 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 { #if BA_DEBUG_BUILD return thread_ownership_; #else - // Not used in release build so doesn't matter. - return ThreadOwnership::kAny; + FatalError("Should not be called in release builds."); + return ThreadOwnership::kClassDefault; #endif } -auto Object::GetDefaultOwnerThread() const -> ThreadIdentifier { - return ThreadIdentifier::kLogic; -} - #if BA_DEBUG_BUILD 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_) { return; } 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; if (thread_ownership == ThreadOwnership::kClassDefault) { diff --git a/src/ballistica/core/object.h b/src/ballistica/core/object.h index a23b2868..974e5e45 100644 --- a/src/ballistica/core/object.h +++ b/src/ballistica/core/object.h @@ -34,23 +34,23 @@ class Object { // type-name plus address. 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 { - kClassDefault, // Uses class' GetDefaultOwnerThread() call. - kNextReferencing, // Uses whichever thread next acquires/accesses a ref. - kCustom, // Always use a specific thread. - kAny // Any thread is fine. + kClassDefault, // Uses class' GetDefaultOwnerThread() call. + kNextReferencing // Uses whichever thread next acquires/accesses a ref. }; +#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 /// for an Object. The default uses the object's individual value /// (which defaults to ThreadOwnership::kClassDefault and can be set via @@ -62,21 +62,12 @@ class Object { /// Default returns ThreadIdentifier::kLogic virtual auto GetDefaultOwnerThread() const -> ThreadIdentifier; - /// Set thread ownership values for an individual object. - /// Note that these values may be ignored if ObjectThreadCheck() is - /// 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) { + /// Set thread ownership for an individual object. + void SetThreadOwnership(ThreadOwnership ownership) { #if BA_DEBUG_BUILD thread_ownership_ = ownership; if (thread_ownership_ == ThreadOwnership::kNextReferencing) { owner_thread_ = ThreadIdentifier::kInvalid; - } else { - owner_thread_ = thread_identifier; } #endif } @@ -128,9 +119,9 @@ class Object { } private: - Object* obj_ = nullptr; - WeakRefBase* prev_ = nullptr; - WeakRefBase* next_ = nullptr; + Object* obj_{}; + WeakRefBase* prev_{}; + WeakRefBase* next_{}; friend class Object; }; // WeakRefBase @@ -278,8 +269,8 @@ class Object { if (obj == nullptr) { throw Exception("Acquiring invalid ptr of " + static_type_name()); } -#if BA_DEBUG_BUILD +#if BA_DEBUG_BUILD // 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 // doing this (session points contexts at itself as it dies, etc.) @@ -287,13 +278,14 @@ class Object { obj->ObjectThreadCheck(); assert(obj_ == nullptr && next_ == nullptr && prev_ == nullptr); #endif + if (obj->object_weak_refs_) { obj->object_weak_refs_->prev_ = this; next_ = obj->object_weak_refs_; } 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 // be certain that's the case. In some cases involving multiple // inheritance this might not be true, but we avoid those cases in our @@ -431,6 +423,7 @@ class Object { } #if BA_DEBUG_BUILD + obj->ObjectUpdateForAcquire(); obj->ObjectThreadCheck(); // Obvs shouldn't be referencing dead stuff. @@ -444,10 +437,10 @@ class Object { // Log only to system log for these low-level errors; // console or server can cause deadlock due to recursive // ref-list locks. - printf( - "Incorrectly creating initial strong-ref to %s; use " - "New() or MakeRefCounted()\n", - obj->GetObjectDescription().c_str()); + FatalError( + "Incorrectly creating initial strong-ref; use " + "New() or MakeRefCounted(): " + + obj->GetObjectDescription()); } obj->object_has_strong_ref_ = true; #endif // BA_DEBUG_BUILD @@ -474,7 +467,7 @@ class Object { } } } - T* obj_ = nullptr; + T* obj_{}; }; /// Object::New(): The preferred way to create ref-counted Objects. @@ -490,17 +483,16 @@ class Object { #if BA_DEBUG_BUILD if (ptr->object_creating_strong_reffed_) { // Avoiding Log for these low level errors; can lead to deadlock. - printf("Object already set up as reffed in New: %s\n", - ptr->GetObjectDescription().c_str()); + FatalError("ballistica::Object already set up as reffed in New: " + + ptr->GetObjectDescription()); } if (ptr->object_strong_ref_count_ > 0) { - // TODO(ericf): make this an error once its cleared out - printf("Obj strong-ref in constructor: %s\n", - ptr->GetObjectDescription().c_str()); + FatalError("ballistica::Object hs strong-ref in constructor: " + + ptr->GetObjectDescription()); } ptr->object_in_constructor_ = false; ptr->object_creating_strong_reffed_ = true; -#endif // BA_DEBUG_BUILD +#endif return Object::Ref(ptr); } @@ -517,8 +509,8 @@ class Object { T* ptr = new T(std::forward(args)...); #if BA_DEBUG_BUILD if (ptr->object_strong_ref_count_ > 0) { - printf("Obj strong-ref in constructor: %s\n", - ptr->GetObjectDescription().c_str()); + FatalError("ballistica::Object has strong-ref in constructor: " + + ptr->GetObjectDescription()); } ptr->object_in_constructor_ = false; #endif @@ -531,9 +523,9 @@ class Object { // Make sure we're operating on a fresh object. assert(ptr->object_strong_ref_count_ == 0); if (ptr->object_creating_strong_reffed_) { - // Avoiding Log for these low level errors; can lead to deadlock. - printf("Object already set up as reffed in MakeRefCounted: %s\n", - ptr->GetObjectDescription().c_str()); + FatalError( + "ballistica::Object already set up as reffed in MakeRefCounted: " + + ptr->GetObjectDescription()); } ptr->object_creating_strong_reffed_ = true; #endif @@ -554,15 +546,13 @@ class Object { } private: +#if BA_DEBUG_BUILD // Making operator new private here to help ensure all of our dynamic // allocation/deallocation goes through our special functions (New(), // NewDeferred(), etc.). However, sticking with original new for release // 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]; } -#endif - -#if BA_DEBUG_BUILD + auto ObjectUpdateForAcquire() -> void; bool object_has_strong_ref_{}; bool object_creating_strong_reffed_{}; bool object_is_dead_{}; diff --git a/src/ballistica/core/thread.cc b/src/ballistica/core/thread.cc index 33c36f82..c197f637 100644 --- a/src/ballistica/core/thread.cc +++ b/src/ballistica/core/thread.cc @@ -9,28 +9,14 @@ namespace ballistica { -bool Thread::threads_paused_ = false; - -void Thread::AddCurrentThreadName(const std::string& name) { - std::lock_guard lock(g_app_globals->thread_name_map_mutex); +void Thread::SetInternalThreadName(const std::string& name) { + std::scoped_lock lock(g_app_globals->thread_name_map_mutex); std::thread::id thread_id = std::this_thread::get_id(); - auto i = g_app_globals->thread_name_map.find(thread_id); - 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; + g_app_globals->thread_name_map[std::this_thread::get_id()] = name; } void Thread::ClearCurrentThreadName() { - std::lock_guard 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()); if (i != g_app_globals->thread_name_map.end()) { g_app_globals->thread_name_map.erase(i); @@ -50,27 +36,10 @@ void Thread::UpdateMainThreadID() { } } -void Thread::KillModule(const Module& module) { - for (auto i = modules_.begin(); i != modules_.end(); i++) { - if (*i == &module) { - delete *i; - modules_.erase(i); - return; - } - } - throw Exception("Module not found on this thread"); -} +// These are all exactly the same; its just a way to try and clarify +// in stack traces which thread is running in case it is not otherwise +// evident. -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 { return static_cast(data)->ThreadMain(); } @@ -108,13 +77,11 @@ void Thread::WaitForNextEvent(bool single_cycle) { 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 // may have enqueued more). - for (auto&& i : modules_) { - if (i->has_pending_runnables()) { - return; - } + if (has_pending_runnables()) { + return; } // 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 // 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 wait_time = timers_.GetTimeToNextExpire(real_time); if (wait_time > 0) { @@ -185,76 +152,44 @@ auto Thread::RunEventLoop(bool single_cycle) -> int { GetThreadMessages(&thread_messages); for (auto& thread_message : thread_messages) { switch (thread_message.type) { - case ThreadMessage::Type::kNewModule: { - // Launch a new module and unlock. - ModuleLauncher* tl; - tl = static_cast(thread_message.pval); - tl->Launch(this); - auto cmd = - static_cast(ThreadMessage::Type::kNewModuleConfirm); - WriteToOwner(&cmd, sizeof(cmd)); - break; - } case ThreadMessage::Type::kRunnable: { - auto module_id = thread_message.ival; - Module* t = GetModule(module_id); - assert(t); - auto e = static_cast(thread_message.pval); - - // Add the event to our list. - t->PushLocalRunnable(e); - RunnablesWhilePausedSanityCheck(e); - + PushLocalRunnable(thread_message.runnable, + thread_message.completion_flag); break; } case ThreadMessage::Type::kShutdown: { - // Shutdown; die! done_ = true; 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: { assert(!paused_); - - // Let all modules do pause-related stuff. - for (auto&& i : modules_) { - i->HandleThreadPause(); - } + RunPauseCallbacks(); paused_ = true; last_pause_time_ = GetRealTime(); messages_since_paused_ = 0; break; } + case ThreadMessage::Type::kResume: { + assert(paused_); + RunResumeCallbacks(); + paused_ = false; + break; + } default: { throw Exception(); } } - // If the thread is going down. if (done_) { break; } } - // Run timers && queued module runnables unless we're paused. if (!paused_) { - // Run timers. timers_.Run(GetRealTime()); - - // Run module-messages. - for (auto& module_entry : modules_) { - module_entry->RunPendingRunnables(); - } + RunPendingRunnables(); } + if (done_ || single_cycle) { break; } @@ -262,17 +197,6 @@ auto Thread::RunEventLoop(bool single_cycle) -> int { 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* messages) { assert(messages); assert(std::this_thread::get_id() == thread_id()); @@ -323,13 +247,12 @@ Thread::Thread(ThreadIdentifier identifier_in, ThreadType type_in) // Let 'er rip. thread_ = new std::thread(func, this); - // The thread lets us know when its up and running. - std::unique_lock lock(data_to_client_mutex_); + // Block until the thread is bootstrapped. + // (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(cmd) - == ThreadMessage::Type::kNewThreadConfirm); break; } 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); thread_id_ = std::this_thread::get_id(); - // Hmmm we might want to set our thread name here, - // as we do for other threads? + // Set our own thread-id-to-name mapping. + 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 // so maybe shouldn't. break; @@ -351,46 +277,60 @@ auto Thread::ThreadMain() -> int { try { assert(type_ == ThreadType::kStandard); thread_id_ = std::this_thread::get_id(); - + const char* name; const char* id_string; + switch (identifier_) { case ThreadIdentifier::kLogic: + name = "logic"; id_string = "ballistica logic"; break; case ThreadIdentifier::kStdin: + name = "stdin"; id_string = "ballistica stdin"; break; case ThreadIdentifier::kMedia: + name = "media"; id_string = "ballistica media"; break; case ThreadIdentifier::kFileOut: + name = "fileout"; id_string = "ballistica file-out"; break; case ThreadIdentifier::kMain: + name = "main"; id_string = "ballistica main"; break; case ThreadIdentifier::kAudio: + name = "audio"; id_string = "ballistica audio"; break; case ThreadIdentifier::kBGDynamics: + name = "bgdynamics"; id_string = "ballistica bg-dynamics"; break; case ThreadIdentifier::kNetworkWrite: + name = "networkwrite"; id_string = "ballistica network writing"; break; default: throw Exception(); } + assert(name && id_string); + SetInternalThreadName(name); g_platform->SetCurrentThreadName(id_string); - // Send our owner a confirmation that we're alive. - auto cmd = static_cast(ThreadMessage::Type::kNewThreadConfirm); - WriteToOwner(&cmd, sizeof(cmd)); + // Mark ourself as bootstrapped and signal listeners so + // anyone waiting for us to spin up can move along. + { + std::scoped_lock lock(client_listener_mutex_); + bootstrapped_ = true; + } + client_listener_cv_.notify_all(); // Now just run our loop until we die. int result = RunEventLoop(); - KillModules(); ClearCurrentThreadName(); return result; } catch (const std::exception& e) { @@ -453,15 +393,6 @@ void Thread::LogThreadMessageTally() { case ThreadMessage::Type::kRunnable: s += "kRunnable"; 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: s += "kPause"; break; @@ -473,8 +404,8 @@ void Thread::LogThreadMessageTally() { break; } if (m.type == ThreadMessage::Type::kRunnable) { - std::string m_name = g_platform->DemangleCXXSymbol( - typeid(*(static_cast(m.pval))).name()); + std::string m_name = + g_platform->DemangleCXXSymbol(typeid(*(m.runnable)).name()); s += std::string(": ") + m_name; } auto j = tally.find(s); @@ -550,55 +481,15 @@ void Thread::PushThreadMessage(const ThreadMessage& t) { 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 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* 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) { - threads_paused_ = paused; + g_app_globals->threads_paused = paused; for (auto&& i : g_app_globals->pausable_threads) { i->SetPaused(paused); } } -auto Thread::AreThreadsPaused() -> bool { return 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(modules_.size()); - // module_entries_.emplace_back(module, name, index); - modules_.push_back(module); - return index; +auto Thread::AreThreadsPaused() -> bool { + return g_app_globals->threads_paused; } auto Thread::NewTimer(millisecs_t length, bool repeat, @@ -613,12 +504,14 @@ auto Thread::GetCurrentThreadName() -> std::string { return "unknown(not-yet-inited)"; } { - std::lock_guard 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()); if (i != g_app_globals->thread_name_map.end()) { return i->second; } } + + // FIXME - move this to platform. #if BA_OSTYPE_MACOS || BA_OSTYPE_IOS_TVOS || BA_OSTYPE_LINUX std::string name = "unknown (sys-name="; char buffer[256]; @@ -634,4 +527,99 @@ auto Thread::GetCurrentThreadName() -> std::string { #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> 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 diff --git a/src/ballistica/core/thread.h b/src/ballistica/core/thread.h index 110e9ebb..7435531f 100644 --- a/src/ballistica/core/thread.h +++ b/src/ballistica/core/thread.h @@ -12,6 +12,7 @@ #include "ballistica/app/app_globals.h" #include "ballistica/ballistica.h" +#include "ballistica/generic/lambda_runnable.h" #include "ballistica/generic/timer_list.h" #include "ballistica/platform/min_sdl.h" @@ -26,18 +27,14 @@ class Thread { ThreadType type_in = ThreadType::kStandard); virtual ~Thread(); - /// Register a name for the current thread (should generally describe its - /// purpose). If called multiple times, names will be combined with a '+'. ie: - /// "graphics+animation+audio". - void AddCurrentThreadName(const std::string& name); - void ClearCurrentThreadName(); + auto ClearCurrentThreadName() -> void; static auto GetCurrentThreadName() -> std::string; /// 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; auto IsCurrent() const -> bool { @@ -47,105 +44,8 @@ class Thread { // Used to quit the main thread. void Quit(); - struct ModuleLauncher { - virtual void Launch(Thread* g) = 0; - virtual ~ModuleLauncher() = default; - }; - - template - struct ModuleLauncherTemplate : public ModuleLauncher { - void Launch(Thread* g) override { new MODULETYPE(g); } - }; - - template - 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(); - // 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 - void AddModule() { - switch (type_) { - case ThreadType::kStandard: { - // Launching a module in the current thread: do it immediately. - if (IsCurrent()) { - ModuleLauncherTemplate launcher; - launcher.Launch(this); - } else { - // Launching a module in another thread; - // send a module-launcher and wait for the confirmation. - ModuleLauncherTemplate launcher; - ModuleLauncher* tl = &launcher; - PushThreadMessage( - ThreadMessage(ThreadMessage::Type::kNewModule, 0, tl)); - std::unique_lock lock(data_to_client_mutex_); - uint32_t cmd; - ReadFromThread(&lock, &cmd, sizeof(cmd)); - assert(static_cast(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 - void AddModule(ARGTYPE arg) { - switch (type_) { - case ThreadType::kStandard: { - // Launching a module in the current thread: do it immediately. - if (IsCurrent()) { - ModuleLauncherArgTemplate launcher(arg); - launcher.Launch(this); - } else { - // Launching a module in another thread; - // send a module-launcher and wait for the confirmation. - ModuleLauncherArgTemplate launcher(arg); - ModuleLauncher* tl = &launcher; - PushThreadMessage( - ThreadMessage(ThreadMessage::Type::kNewModule, 0, tl)); - - std::unique_lock lock(data_to_client_mutex_); - - uint32_t cmd; - - ReadFromThread(&lock, &cmd, sizeof(cmd)); - - assert(static_cast(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); auto thread_id() const -> std::thread::id { return thread_id_; } @@ -157,14 +57,7 @@ class Thread { auto RunEventLoop(bool single_cycle = false) -> int; auto identifier() const -> ThreadIdentifier { return identifier_; } - // For use by modules. - 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 { + auto CheckPushRunnableSafety() -> bool { // We first complain when we get to 1000 queued messages so // let's consider things unsafe when we're halfway there. return (thread_message_count_ < kThreadMessageSafetyThreshold); @@ -174,52 +67,83 @@ class Thread { auto NewTimer(millisecs_t length, bool repeat, const Object::Ref& 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 + 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 + 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: struct ThreadMessage { - enum class Type { - kShutdown = 999, - kRunnable, - kNewModule, - kNewModuleConfirm, - kNewThreadConfirm, - kPause, - kResume - }; + enum class Type { kShutdown = 999, kRunnable, kPause, kResume }; Type type; - void* pval; - int ival; - explicit ThreadMessage(Type type_in, int ival_in = 0, - void* pval_in = nullptr) - : type(type_in), ival(ival_in), pval(pval_in) {} + union { + Runnable* runnable; + }; + bool* completion_flag{}; + explicit ThreadMessage(Type type_in) : type(type_in) {} + explicit ThreadMessage(Type type, Runnable* runnable, bool* completion_flag) + : type(type), runnable(runnable), completion_flag{completion_flag} {} }; - static void RunnablesWhilePausedSanityCheck(Runnable* r); - void WaitForNextEvent(bool single_cycle); - void LoopUpkeep(bool once); - void LogThreadMessageTally(); - void ReadFromThread(std::unique_lock* lock, void* buffer, - uint32_t size); - void WriteToOwner(const void* data, uint32_t size); - bool writing_tally_ = false; - bool paused_ = false; - millisecs_t last_pause_time_ = 0; - int messages_since_paused_ = 0; - millisecs_t last_paused_message_report_time_ = 0; - bool done_ = false; + auto SetInternalThreadName(const std::string& name) -> void; + auto WaitForNextEvent(bool single_cycle) -> void; + auto LoopUpkeep(bool once) -> void; + auto LogThreadMessageTally() -> void; + auto PushLocalRunnable(Runnable* runnable, bool* completion_flag) -> void; + auto PushCrossThreadRunnable(Runnable* runnable, bool* completion_flag) + -> 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_; - int listen_sd_ = 0; + int listen_sd_{}; std::thread::id thread_id_{}; - ThreadIdentifier identifier_ = ThreadIdentifier::kInvalid; - millisecs_t last_complaint_time_ = 0; - bool owns_python_ = false; + ThreadIdentifier identifier_{ThreadIdentifier::kInvalid}; + millisecs_t last_complaint_time_{}; + bool owns_python_{}; // FIXME: Should generalize this to some sort of PlatformThreadData class. #if BA_XCODE_BUILD void* auto_release_pool_ = nullptr; #endif - void KillModules(); - // 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. @@ -231,25 +155,26 @@ class Thread { static auto RunMediaThread(void* data) -> int; auto ThreadMain() -> int; - std::thread* thread_; - void GetThreadMessages(std::list* messages); - void PushThreadMessage(const ThreadMessage& t); + auto GetThreadMessages(std::list* messages) -> void; + auto PushThreadMessage(const ThreadMessage& t) -> void; + + auto RunPendingRunnables() -> void; + auto RunPauseCallbacks() -> void; + auto RunResumeCallbacks() -> void; + + int thread_message_count_{}; + bool bootstrapped_{}; + std::list> runnables_; + std::list pause_callbacks_; + std::list resume_callbacks_; + std::thread* thread_{}; std::condition_variable thread_message_cv_; std::mutex thread_message_mutex_; std::list thread_messages_; - int thread_message_count_ = 0; - std::condition_variable data_to_client_cv_; - std::mutex data_to_client_mutex_; - std::list > data_to_client_; - std::vector modules_; - auto GetModule(int id) -> Module* { - assert(id >= 0 && id < static_cast(modules_.size())); - return modules_[id]; - } - - // Complete list of all timers created by this group's modules. + std::condition_variable client_listener_cv_; + std::mutex client_listener_mutex_; + std::list> data_to_client_; TimerList timers_; - static bool threads_paused_; }; } // namespace ballistica diff --git a/src/ballistica/dynamics/bg/bg_dynamics.cc b/src/ballistica/dynamics/bg/bg_dynamics.cc index 75f55f09..dacfbce6 100644 --- a/src/ballistica/dynamics/bg/bg_dynamics.cc +++ b/src/ballistica/dynamics/bg/bg_dynamics.cc @@ -70,8 +70,7 @@ void BGDynamics::Step(const Vector3f& cam_pos) { { // Shadows. BA_DEBUG_TIME_CHECK_BEGIN(bg_dynamic_shadow_list_lock); { - std::lock_guard lock( - g_bg_dynamics_server->shadow_list_mutex_); + std::scoped_lock lock(g_bg_dynamics_server->shadow_list_mutex_); auto size = g_bg_dynamics_server->shadows_.size(); d->shadow_step_data_.resize(size); if (size > 0) { @@ -90,8 +89,7 @@ void BGDynamics::Step(const Vector3f& cam_pos) { { // Volume lights. BA_DEBUG_TIME_CHECK_BEGIN(bg_dynamic_volumelights_list_lock); { - std::lock_guard lock( - g_bg_dynamics_server->volume_light_list_mutex_); + std::scoped_lock lock(g_bg_dynamics_server->volume_light_list_mutex_); auto size = g_bg_dynamics_server->volume_lights_.size(); d->volume_light_step_data_.resize(size); if (size > 0) { @@ -116,7 +114,7 @@ void BGDynamics::Step(const Vector3f& cam_pos) { { // Fuses. BA_DEBUG_TIME_CHECK_BEGIN(bg_dynamic_fuse_list_lock); { - std::lock_guard 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(); d->fuse_step_data_.resize(size); if (size > 0) { @@ -137,7 +135,7 @@ void BGDynamics::Step(const Vector3f& cam_pos) { // Increase our step count and ship it. { - std::lock_guard 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_++; } diff --git a/src/ballistica/dynamics/bg/bg_dynamics_server.cc b/src/ballistica/dynamics/bg/bg_dynamics_server.cc index af02385d..427d0edd 100644 --- a/src/ballistica/dynamics/bg/bg_dynamics_server.cc +++ b/src/ballistica/dynamics/bg/bg_dynamics_server.cc @@ -2,6 +2,7 @@ #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_fuse_data.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. if (collide_model_) { Object::Ref* ref = collide_model_; - g_game->PushCall([ref] { + g_game->thread()->PushCall([ref] { (**ref).set_last_used_time(GetRealTime()); delete ref; }); @@ -660,7 +661,7 @@ void BGDynamicsServer::ParticleSet::UpdateAndCreateSnapshot( } BGDynamicsServer::BGDynamicsServer(Thread* thread) - : Module("bgDynamics", thread), + : thread_(thread), height_cache_(new BGDynamicsHeightCache()), collision_cache_(new CollisionCache) { BA_PRECONDITION(g_bg_dynamics_server == nullptr); @@ -682,8 +683,6 @@ BGDynamicsServer::BGDynamicsServer(Thread* thread) assert(ode_contact_group_); } -BGDynamicsServer::~BGDynamicsServer() = default; - BGDynamicsServer::Tendril::~Tendril() { // If we have a controller, tell them not to call us anymore. if (controller_) { @@ -992,7 +991,7 @@ void BGDynamicsServer::Clear() { } void BGDynamicsServer::PushEmitCall(const BGDynamicsEmission& def) { - PushCall([this, def] { Emit(def); }); + thread()->PushCall([this, def] { Emit(def); }); } void BGDynamicsServer::Emit(const BGDynamicsEmission& def) { @@ -1364,7 +1363,7 @@ void BGDynamicsServer::Emit(const BGDynamicsEmission& def) { } void BGDynamicsServer::PushRemoveTerrainCall(CollideModelData* collide_model) { - PushCall([this, collide_model] { + thread()->PushCall([this, collide_model] { assert(collide_model != nullptr); bool found = false; 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) { - PushCall([this, shadow_data] { + thread()->PushCall([this, shadow_data] { assert(InBGDynamicsThread()); - std::lock_guard lock(shadow_list_mutex_); + std::scoped_lock lock(shadow_list_mutex_); shadows_.push_back(shadow_data); }); } void BGDynamicsServer::PushRemoveShadowCall(BGDynamicsShadowData* shadow_data) { - PushCall([this, shadow_data] { + thread()->PushCall([this, shadow_data] { assert(InBGDynamicsThread()); bool found = false; { - std::lock_guard lock(shadow_list_mutex_); + std::scoped_lock lock(shadow_list_mutex_); for (auto i = shadows_.begin(); i != shadows_.end(); ++i) { if ((*i) == shadow_data) { found = true; @@ -1422,20 +1421,20 @@ void BGDynamicsServer::PushRemoveShadowCall(BGDynamicsShadowData* shadow_data) { void BGDynamicsServer::PushAddVolumeLightCall( BGDynamicsVolumeLightData* volume_light_data) { - PushCall([this, volume_light_data] { + thread()->PushCall([this, volume_light_data] { // Add to our internal list. - std::lock_guard lock(volume_light_list_mutex_); + std::scoped_lock lock(volume_light_list_mutex_); volume_lights_.push_back(volume_light_data); }); } void BGDynamicsServer::PushRemoveVolumeLightCall( BGDynamicsVolumeLightData* volume_light_data) { - PushCall([this, volume_light_data] { + thread()->PushCall([this, volume_light_data] { // Remove from our list and kill. bool found = false; { - std::lock_guard lock(volume_light_list_mutex_); + std::scoped_lock lock(volume_light_list_mutex_); for (auto i = volume_lights_.begin(); i != volume_lights_.end(); ++i) { if ((*i) == volume_light_data) { found = true; @@ -1450,17 +1449,17 @@ void BGDynamicsServer::PushRemoveVolumeLightCall( } void BGDynamicsServer::PushAddFuseCall(BGDynamicsFuseData* fuse_data) { - PushCall([this, fuse_data] { - std::lock_guard lock(fuse_list_mutex_); + thread()->PushCall([this, fuse_data] { + std::scoped_lock lock(fuse_list_mutex_); fuses_.push_back(fuse_data); }); } void BGDynamicsServer::PushRemoveFuseCall(BGDynamicsFuseData* fuse_data) { - PushCall([this, fuse_data] { + thread()->PushCall([this, fuse_data] { bool found = false; { - std::lock_guard lock(fuse_list_mutex_); + std::scoped_lock lock(fuse_list_mutex_); for (auto i = fuses_.begin(); i != fuses_.end(); i++) { if ((*i) == fuse_data) { found = true; @@ -1475,11 +1474,11 @@ void BGDynamicsServer::PushRemoveFuseCall(BGDynamicsFuseData* fuse_data) { } void BGDynamicsServer::PushSetDebrisFrictionCall(float friction) { - PushCall([this, friction] { debris_friction_ = friction; }); + thread()->PushCall([this, friction] { debris_friction_ = friction; }); } void BGDynamicsServer::PushSetDebrisKillHeightCall(float height) { - PushCall([this, height] { debris_kill_height_ = height; }); + thread()->PushCall([this, height] { debris_kill_height_ = height; }); } auto BGDynamicsServer::CreateDrawSnapshot() -> BGDynamicsDrawSnapshot* { @@ -2215,7 +2214,7 @@ auto BGDynamicsServer::CreateDrawSnapshot() -> BGDynamicsDrawSnapshot* { } // NOLINT (yes this should be shorter) void BGDynamicsServer::PushTooSlowCall() { - PushCall([this] { + thread()->PushCall([this] { if (chunk_count_ > 0 || tendril_count_thick_ > 0 || tendril_count_thin_ > 0) { // 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, // so they can draw us. BGDynamicsDrawSnapshot* snapshot = CreateDrawSnapshot(); - g_game->PushCall([snapshot] { + g_game->thread()->PushCall([snapshot] { snapshot->SetLogicThreadOwnership(); g_bg_dynamics->SetDrawSnapshot(snapshot); }); @@ -2326,19 +2325,19 @@ void BGDynamicsServer::Step(StepData* step_data) { // Job's done! { - std::lock_guard lock(step_count_mutex_); + std::scoped_lock lock(step_count_mutex_); step_count_--; } assert(step_count_ >= 0); } void BGDynamicsServer::PushStepCall(StepData* data) { - PushCall([this, data] { Step(data); }); + thread()->PushCall([this, data] { Step(data); }); } void BGDynamicsServer::PushAddTerrainCall( Object::Ref* collide_model) { - PushCall([this, collide_model] { + thread()->PushCall([this, collide_model] { assert(InBGDynamicsThread()); assert(collide_model != nullptr); @@ -2639,7 +2638,7 @@ void BGDynamicsServer::UpdateShadows() { { BA_DEBUG_TIME_CHECK_BEGIN(bg_dynamic_shadow_list_lock); { - std::lock_guard lock(shadow_list_mutex_); + std::scoped_lock lock(shadow_list_mutex_); for (auto&& s : shadows_) { s->UpdateClientData(); } diff --git a/src/ballistica/dynamics/bg/bg_dynamics_server.h b/src/ballistica/dynamics/bg/bg_dynamics_server.h index d8d23ee6..90a229f5 100644 --- a/src/ballistica/dynamics/bg/bg_dynamics_server.h +++ b/src/ballistica/dynamics/bg/bg_dynamics_server.h @@ -5,10 +5,10 @@ #include #include +#include #include #include -#include "ballistica/core/module.h" #include "ballistica/dynamics/bg/bg_dynamics.h" #include "ballistica/math/matrix44f.h" #include "ballistica/math/vector3f.h" @@ -16,7 +16,7 @@ namespace ballistica { -class BGDynamicsServer : public Module { +class BGDynamicsServer { public: struct Particle { float x; @@ -81,7 +81,7 @@ class BGDynamicsServer : public Module { }; explicit BGDynamicsServer(Thread* thread); - ~BGDynamicsServer() override; + auto time() const -> uint32_t { return time_; } auto graphics_quality() const -> GraphicsQuality { return graphics_quality_; } @@ -98,6 +98,7 @@ class BGDynamicsServer : public Module { return spark_particles_.get(); } auto step_count() const -> int { return step_count_; } + auto thread() const -> Thread* { return thread_; } private: class Terrain; @@ -121,6 +122,8 @@ class BGDynamicsServer : public Module { void UpdateFuses(); void UpdateShadows(); auto CreateDrawSnapshot() -> BGDynamicsDrawSnapshot*; + + Thread* thread_{}; BGDynamicsChunkType cb_type_ = BGDynamicsChunkType::kRock; dBodyID cb_body_{}; float cb_cfm_{0.0f}; diff --git a/src/ballistica/game/account.cc b/src/ballistica/game/account.cc index a154844c..40ce85c0 100644 --- a/src/ballistica/game/account.cc +++ b/src/ballistica/game/account.cc @@ -92,32 +92,32 @@ auto Account::AccountTypeToIconString(V1AccountType type) -> std::string { Account::Account() = default; auto Account::GetLoginName() -> std::string { - std::lock_guard lock(mutex_); + std::scoped_lock lock(mutex_); return login_name_; } auto Account::GetLoginID() -> std::string { - std::lock_guard lock(mutex_); + std::scoped_lock lock(mutex_); return login_id_; } auto Account::GetToken() -> std::string { - std::lock_guard lock(mutex_); + std::scoped_lock lock(mutex_); return token_; } auto Account::GetExtra() -> std::string { - std::lock_guard lock(mutex_); + std::scoped_lock lock(mutex_); return extra_; } auto Account::GetExtra2() -> std::string { - std::lock_guard lock(mutex_); + std::scoped_lock lock(mutex_); return extra_2_; } auto Account::GetLoginState(int* state_num) -> V1LoginState { - std::lock_guard lock(mutex_); + std::scoped_lock lock(mutex_); if (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) { - std::lock_guard lock(mutex_); + std::scoped_lock lock(mutex_); extra_ = extra; } void Account::SetExtra2(const std::string& extra) { - std::lock_guard lock(mutex_); + std::scoped_lock lock(mutex_); extra_2_ = extra; } void Account::SetToken(const std::string& account_id, const std::string& token) { - std::lock_guard lock(mutex_); + std::scoped_lock lock(mutex_); // Hmm, does this compare logic belong in here? if (login_id_ == account_id) { token_ = token; @@ -148,7 +148,7 @@ void Account::SetLogin(V1AccountType account_type, V1LoginState login_state, const std::string& login_id) { bool call_login_did_change = false; { - std::lock_guard lock(mutex_); + std::scoped_lock lock(mutex_); // We call out to Python so need to be in game thread. assert(InLogicThread()); @@ -183,7 +183,7 @@ void Account::SetLogin(V1AccountType account_type, V1LoginState login_state, } void Account::SetProductsPurchased(const std::vector& products) { - std::lock_guard lock(mutex_); + std::scoped_lock lock(mutex_); std::unordered_map purchases_old = product_purchases_; product_purchases_.clear(); for (auto&& i : products) { @@ -195,7 +195,7 @@ void Account::SetProductsPurchased(const std::vector& products) { } auto Account::GetProductPurchased(const std::string& product) -> bool { - std::lock_guard lock(mutex_); + std::scoped_lock lock(mutex_); auto i = product_purchases_.find(product); if (i == product_purchases_.end()) { return false; diff --git a/src/ballistica/game/connection/connection_set.cc b/src/ballistica/game/connection/connection_set.cc index 1f9e96bc..e9e164b7 100644 --- a/src/ballistica/game/connection/connection_set.cc +++ b/src/ballistica/game/connection/connection_set.cc @@ -2,6 +2,7 @@ #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_host_udp.h" #include "ballistica/game/game.h" @@ -215,14 +216,15 @@ void ConnectionSet::PushUDPConnectionPacketCall( const std::vector& data, const SockAddr& addr) { // Avoid buffer-full errors if something is causing us to write too often; // these are unreliable messages so its ok to just drop them. - if (!g_game->CheckPushSafety()) { + if (!g_game->thread()->CheckPushSafety()) { BA_LOG_ONCE( "Ignoring excessive udp-connection input packets; (could this be a " "flood attack?)."); return; } - g_game->PushCall([this, data, addr] { UDPConnectionPacket(data, addr); }); + g_game->thread()->PushCall( + [this, data, addr] { UDPConnectionPacket(data, addr); }); } auto ConnectionSet::Shutdown() -> void { @@ -352,11 +354,11 @@ auto ConnectionSet::DisconnectClient(int client_id, int ban_seconds) -> bool { } void ConnectionSet::PushClientDisconnectedCall(int id) { - g_game->PushCall([this, id] { HandleClientDisconnected(id); }); + g_game->thread()->PushCall([this, id] { HandleClientDisconnected(id); }); } void ConnectionSet::PushDisconnectedFromHostCall() { - g_game->PushCall([this] { + g_game->thread()->PushCall([this] { if (connection_to_host_.exists()) { bool was_connected = connection_to_host_->can_communicate(); connection_to_host_.Clear(); @@ -377,7 +379,7 @@ void ConnectionSet::PushDisconnectedFromHostCall() { void ConnectionSet::PushHostConnectedUDPCall(const SockAddr& addr, 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 // advertising, etc. g_game->CleanUpBeforeConnectingToHost(); @@ -389,7 +391,7 @@ void ConnectionSet::PushHostConnectedUDPCall(const SockAddr& addr, } void ConnectionSet::PushDisconnectFromHostCall() { - g_game->PushCall([this] { + g_game->thread()->PushCall([this] { if (connection_to_host_.exists()) { connection_to_host_->RequestDisconnect(); } diff --git a/src/ballistica/game/game.cc b/src/ballistica/game/game.cc index a728c72b..bfb9bf72 100644 --- a/src/ballistica/game/game.cc +++ b/src/ballistica/game/game.cc @@ -66,7 +66,7 @@ const int kMaxChatMessages = 40; const int kKickBanSeconds = 5 * 60; Game::Game(Thread* thread) - : Module("game", thread), + : thread_(thread), game_roster_(cJSON_CreateArray()), realtimers_(new TimerList()), connections_(std::make_unique()) { @@ -89,6 +89,9 @@ Game::Game(Thread* thread) 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? // Update: yes it does in order to be a context target. // (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 // since the main thread won't be blocking waiting for us to init. std::string what = e.what(); - PushCall([what] { + this->thread()->PushCall([what] { // Just throw a standard exception since our what already // contains a stack trace; if we throw an Exception we wind // 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() { - std::lock_guard lock(special_char_mutex_); + std::scoped_lock lock(special_char_mutex_); special_char_strings_[SpecialChar::kDownArrow] = "\xee\x80\x84"; special_char_strings_[SpecialChar::kUpArrow] = "\xee\x80\x83"; @@ -244,13 +255,13 @@ void Game::ResetActivityTracking() { #if BA_VR_BUILD 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 void Game::PushMediaPruneCall(int level) { - PushCall([level] { + thread()->PushCall([level] { assert(InLogicThread()); g_media->Prune(level); }); @@ -260,13 +271,14 @@ void Game::PushSetV1LoginCall(V1AccountType account_type, V1LoginState account_state, const std::string& account_name, 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); }); } void Game::PushInitialScreenCreatedCall() { - PushCall([this] { InitialScreenCreated(); }); + thread()->PushCall([this] { InitialScreenCreated(); }); } void Game::InitialScreenCreated() { @@ -288,22 +300,22 @@ void Game::InitialScreenCreated() { // Set up our timers. process_timer_ = - NewThreadTimer(0, true, NewLambdaRunnable([this] { Process(); })); - media_prune_timer_ = - NewThreadTimer(2345, true, NewLambdaRunnable([this] { Prune(); })); + thread()->NewTimer(0, true, NewLambdaRunnable([this] { Process(); })); + media_prune_timer_ = thread()->NewTimer( + 2345, true, NewLambdaRunnable([this] { PruneMedia(); })); // 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 // timer to take its place. if (HeadlessMode()) { headless_update_timer_ = - NewThreadTimer(8, true, NewLambdaRunnable([this] { Update(); })); + thread()->NewTimer(8, true, NewLambdaRunnable([this] { Update(); })); } RunAppLaunchCommands(); } -void Game::Prune() { g_media->Prune(); } +void Game::PruneMedia() { g_media->Prune(); } // Launch into main menu or whatever else. void Game::RunAppLaunchCommands() { @@ -334,8 +346,6 @@ void Game::RunAppLaunchCommands() { UpdateProcessTimer(); } -Game::~Game() = default; - // Set up our sleeping based on what we're doing. void Game::UpdateProcessTimer() { assert(InLogicThread()); @@ -497,7 +507,7 @@ void Game::HandleQuitOnIdle() { if (!idle_exiting_ && idle_seconds > (idle_exit_minutes_.value() * 60.0f)) { idle_exiting_ = true; - PushCall([this, idle_seconds] { + thread()->PushCall([this, idle_seconds] { assert(InLogicThread()); // Just go through _ba.quit() @@ -708,7 +718,7 @@ auto Game::IsInUIContext() const -> bool { } void Game::PushShowURLCall(const std::string& url) { - PushCall([url] { + thread()->PushCall([url] { assert(InLogicThread()); assert(g_python); g_python->ShowURL(url); @@ -725,7 +735,7 @@ auto Game::GetForegroundContext() -> Context { } void Game::PushBackButtonCall(InputDevice* input_device) { - PushCall([this, input_device] { + thread()->PushCall([this, input_device] { assert(InLogicThread()); // Ignore if UI isn't up yet. @@ -747,7 +757,7 @@ void Game::PushBackButtonCall(InputDevice* input_device) { } void Game::PushStringEditSetCall(const std::string& value) { - PushCall([value] { + thread()->PushCall([value] { if (!g_ui) { Log("Error: No ui on StringEditSetEvent."); return; @@ -764,7 +774,7 @@ void Game::PushStringEditSetCall(const std::string& value) { } void Game::PushStringEditCancelCall() { - PushCall([] { + thread()->PushCall([] { if (!g_ui) { Log("Error: No ui in PushStringEditCancelCall."); return; @@ -903,7 +913,7 @@ void Game::RunMainMenu() { // in the current visible context. void Game::PushInGameConsoleScriptCommand(const std::string& command) { - PushCall([this, command] { + thread()->PushCall([this, command] { // These are always run in whichever context is 'visible'. ScopedSetContext cp(GetForegroundContext()); PythonCommand cmd(command, ""); @@ -932,7 +942,7 @@ void Game::PushInGameConsoleScriptCommand(const std::string& command) { // Commands run via stdin. void Game::PushStdinScriptCommand(const std::string& command) { - PushCall([this, command] { + thread()->PushCall([this, command] { // These are always run in whichever context is 'visible'. ScopedSetContext cp(GetForegroundContext()); PythonCommand cmd(command, ""); @@ -966,7 +976,7 @@ void Game::PushStdinScriptCommand(const std::string& command) { } void Game::PushInterruptSignalCall() { - PushCall([this] { + thread()->PushCall([this] { assert(InLogicThread()); // Special case; when running under the server-wrapper, we completely @@ -981,26 +991,18 @@ void Game::PushInterruptSignalCall() { } void Game::PushAskUserForTelnetAccessCall() { - PushCall([this] { + thread()->PushCall([this] { assert(InLogicThread()); ScopedSetContext cp(GetUIContext()); 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& call) { // Since we're mucking with refs, need to limit to game thread. BA_PRECONDITION(InLogicThread()); BA_PRECONDITION(call->object_strong_ref_count() > 0); - PushCall([call] { + thread()->PushCall([call] { assert(call.exists()); call->Run(); }); @@ -1011,7 +1013,7 @@ void Game::PushPythonCallArgs(const Object::Ref& call, // Since we're mucking with refs, need to limit to game thread. BA_PRECONDITION(InLogicThread()); BA_PRECONDITION(call->object_strong_ref_count() > 0); - PushCall([call, args] { + thread()->PushCall([call, args] { assert(call.exists()); call->Run(args.get()); }); @@ -1025,7 +1027,7 @@ void Game::PushPythonWeakCall(const Object::WeakRef& call) { // object to be passed in. assert(call.exists() && call->object_strong_ref_count() > 0); - PushCall([call] { + thread()->PushCall([call] { if (call.exists()) { Python::ScopedCallLabel label("PythonWeakCallMessage"); call->Run(); @@ -1042,13 +1044,13 @@ void Game::PushPythonWeakCallArgs( // object to be passed in. assert(call.exists() && call->object_strong_ref_count() > 0); - PushCall([call, args] { + thread()->PushCall([call, args] { if (call.exists()) call->Run(args.get()); }); } void Game::PushPythonRawCallable(PyObject* callable) { - PushCall([this, callable] { + thread()->PushCall([this, callable] { assert(InLogicThread()); // Lets run this in the UI context. @@ -1065,7 +1067,8 @@ void Game::PushPythonRawCallable(PyObject* callable) { void Game::PushScreenMessage(const std::string& message, 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) { @@ -1113,19 +1116,19 @@ auto Game::GetUIContext() const -> Context { } void Game::PushToggleManualCameraCall() { - PushCall([] { g_graphics->ToggleManualCamera(); }); + thread()->PushCall([] { g_graphics->ToggleManualCamera(); }); } void Game::PushToggleDebugInfoDisplayCall() { - PushCall([] { g_graphics->ToggleNetworkDebugDisplay(); }); + thread()->PushCall([] { g_graphics->ToggleNetworkDebugDisplay(); }); } void Game::PushToggleCollisionGeometryDisplayCall() { - PushCall([] { g_graphics->ToggleDebugDraw(); }); + thread()->PushCall([] { g_graphics->ToggleDebugDraw(); }); } void Game::PushMainMenuPressCall(InputDevice* device) { - PushCall([this, device] { MainMenuPress(device); }); + thread()->PushCall([this, device] { MainMenuPress(device); }); } void Game::MainMenuPress(InputDevice* device) { @@ -1135,7 +1138,7 @@ void Game::MainMenuPress(InputDevice* device) { void Game::PushScreenResizeCall(float virtual_width, float virtual_height, float pixel_width, float pixel_height) { - PushCall([=] { + thread()->PushCall([=] { 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( const std::set& achievements) { - PushCall([this, achievements] { GameServiceAchievementList(achievements); }); + thread()->PushCall( + [this, achievements] { GameServiceAchievementList(achievements); }); } void Game::GameServiceAchievementList( @@ -1171,7 +1175,7 @@ void Game::GameServiceAchievementList( void Game::PushScoresToBeatResponseCall(bool success, const std::list& scores, void* py_callback) { - PushCall([this, success, scores, py_callback] { + thread()->PushCall([this, success, scores, py_callback] { ScoresToBeatResponse(success, scores, py_callback); }); } @@ -1185,15 +1189,16 @@ void Game::ScoresToBeatResponse(bool success, } 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) { - PushCall([score_set] { g_python->HandleFriendScoresCB(score_set); }); + thread()->PushCall( + [score_set] { g_python->HandleFriendScoresCB(score_set); }); } void Game::PushConfirmQuitCall() { - PushCall([this] { + thread()->PushCall([this] { assert(InLogicThread()); if (HeadlessMode()) { Log("PushConfirmQuitCall() unhandled on headless."); @@ -1256,11 +1261,11 @@ void Game::Draw() { } void Game::PushFrameDefRequest() { - PushCall([this] { Draw(); }); + thread()->PushCall([this] { Draw(); }); } void Game::PushOnAppResumeCall() { - PushCall([] { + thread()->PushCall([] { // Wipe out whatever input device was in control of the UI. assert(g_ui); g_ui->SetUIInputDevice(nullptr); @@ -1348,7 +1353,7 @@ void Game::ApplyConfig() { // FIXME: this should exist either on the client or the server; not both. // (and should be communicated via frameldefs/etc.) 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); }); g_graphics->set_tv_border(tv_border); @@ -1427,11 +1432,11 @@ void Game::ApplyConfig() { } void Game::PushApplyConfigCall() { - PushCall([this] { ApplyConfig(); }); + thread()->PushCall([this] { ApplyConfig(); }); } void Game::PushRemoveGraphicsServerRenderHoldCall() { - PushCall([] { + thread()->PushCall([] { // 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 // to start rendering again. Thus any already-queued-up @@ -1442,7 +1447,7 @@ void Game::PushRemoveGraphicsServerRenderHoldCall() { void Game::PushFreeMediaComponentRefsCall( const std::vector*>& components) { - PushCall([components] { + thread()->PushCall([components] { for (auto&& i : components) { delete i; } @@ -1450,7 +1455,7 @@ void Game::PushFreeMediaComponentRefsCall( } void Game::PushHavePendingLoadsDoneCall() { - PushCall([] { g_media->ClearPendingLoadsDoneList(); }); + thread()->PushCall([] { g_media->ClearPendingLoadsDoneList(); }); } void Game::ToggleConsole() { @@ -1461,7 +1466,7 @@ void Game::ToggleConsole() { } void Game::PushConsolePrintCall(const std::string& msg) { - PushCall([msg] { + thread()->PushCall([msg] { // 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). if (auto console = g_app_globals->console) { @@ -1473,14 +1478,14 @@ void Game::PushConsolePrintCall(const std::string& msg) { } void Game::PushHavePendingLoadsCall() { - PushCall([this] { + thread()->PushCall([this] { have_pending_loads_ = true; UpdateProcessTimer(); }); } void Game::PushShutdownCall(bool soft) { - PushCall([this, soft] { Shutdown(soft); }); + thread()->PushCall([this, soft] { Shutdown(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 // account-client to disk, etc). - HandleThreadPause(); + OnThreadPause(); // Attempt to report/store outstanding log stuff. g_app_internal->PutLog(false); @@ -1553,7 +1558,7 @@ void Game::SetLanguageKeys( const std::unordered_map& language) { assert(InLogicThread()); { - std::lock_guard lock(language_mutex_); + std::scoped_lock lock(language_mutex_); 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 { std::string val; { - std::lock_guard lock(language_mutex_); + std::scoped_lock lock(language_mutex_); auto i = language_.find(key); if (i != language_.end()) { val = i->second; @@ -1807,7 +1812,7 @@ auto Game::GetResourceString(const std::string& key) -> std::string { } auto Game::CharStr(SpecialChar id) -> std::string { - std::lock_guard lock(special_char_mutex_); + std::scoped_lock lock(special_char_mutex_); std::string val; auto i = special_char_strings_.find(id); if (i != special_char_strings_.end()) { diff --git a/src/ballistica/game/game.h b/src/ballistica/game/game.h index bdd24927..acd50155 100644 --- a/src/ballistica/game/game.h +++ b/src/ballistica/game/game.h @@ -13,7 +13,7 @@ #include #include -#include "ballistica/core/module.h" +#include "ballistica/core/object.h" namespace ballistica { @@ -22,10 +22,10 @@ const int kMaxPartyNameCombinedSize = 25; /// The Game Module generally runs on a dedicated thread; it manages /// all game logic, builds frame_defs to send to the graphics-server for /// rendering, etc. -class Game : public Module { +class Game { public: explicit Game(Thread* thread); - ~Game() override; + auto LaunchHostSession(PyObject* session_type_obj, BenchmarkType benchmark_type = BenchmarkType::kNone) -> void; @@ -102,7 +102,7 @@ class Game : public Module { auto ChangeGameSpeed(int offs) -> void; auto ResetInput() -> void; auto RunMainMenu() -> void; - auto HandleThreadPause() -> void override; + auto OnThreadPause() -> void; #if BA_VR_BUILD auto PushVRHandsState(const VRHandsState& state) -> void; @@ -244,6 +244,7 @@ class Game : public Module { return connections_.get(); } auto mark_game_roster_dirty() -> void { game_roster_dirty_ = true; } + auto thread() const -> Thread* { return thread_; } private: auto HandleQuitOnIdle() -> void; @@ -258,7 +259,7 @@ class Game : public Module { auto ScoresToBeatResponse(bool success, const std::list& scores, void* py_callback) -> void; - auto Prune() -> void; // Periodic pruning of dead stuff. + auto PruneMedia() -> void; auto Update() -> void; auto Process() -> void; auto UpdateKickVote() -> void; @@ -277,6 +278,7 @@ class Game : public Module { int rift_step_index_{}; #endif + Thread* thread_{}; std::unique_ptr connections_; std::list > banned_players_; std::list chat_messages_; diff --git a/src/ballistica/game/host_activity.cc b/src/ballistica/game/host_activity.cc index 8e915832..3d097d7e 100644 --- a/src/ballistica/game/host_activity.cc +++ b/src/ballistica/game/host_activity.cc @@ -6,6 +6,7 @@ #include "ballistica/game/game_stream.h" #include "ballistica/game/player.h" #include "ballistica/game/session/host_session.h" +#include "ballistica/generic/lambda_runnable.h" #include "ballistica/generic/timer.h" #include "ballistica/input/device/input_device.h" #include "ballistica/media/component/collide_model.h" diff --git a/src/ballistica/game/session/host_session.cc b/src/ballistica/game/session/host_session.cc index 7521ac7e..b3dfa82b 100644 --- a/src/ballistica/game/session/host_session.cc +++ b/src/ballistica/game/session/host_session.cc @@ -5,6 +5,7 @@ #include "ballistica/game/game_stream.h" #include "ballistica/game/host_activity.h" #include "ballistica/game/player.h" +#include "ballistica/generic/lambda_runnable.h" #include "ballistica/generic/timer.h" #include "ballistica/graphics/graphics.h" #include "ballistica/input/device/input_device.h" diff --git a/src/ballistica/generic/runnable.h b/src/ballistica/generic/runnable.h index 0d600fba..a0fe102c 100644 --- a/src/ballistica/generic/runnable.h +++ b/src/ballistica/generic/runnable.h @@ -13,8 +13,8 @@ class Runnable : public Object { public: virtual void Run() = 0; - // these are used on lots of threads; lets - // lock to wherever we're first referenced + // these are used on lots of threads; we lock to whichever + // thread first creates a reference to us. auto GetThreadOwnership() const -> ThreadOwnership override; }; diff --git a/src/ballistica/graphics/graphics.cc b/src/ballistica/graphics/graphics.cc index 69e86b8f..50e69f10 100644 --- a/src/ballistica/graphics/graphics.cc +++ b/src/ballistica/graphics/graphics.cc @@ -858,7 +858,7 @@ auto Graphics::GetEmptyFrameDef() -> FrameDef* { void Graphics::ClearFrameDefDeleteList() { assert(InLogicThread()); - std::lock_guard lock(frame_def_delete_list_mutex_); + std::scoped_lock lock(frame_def_delete_list_mutex_); for (auto& i : frame_def_delete_list_) { // 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) { - std::lock_guard 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); } diff --git a/src/ballistica/graphics/graphics_server.cc b/src/ballistica/graphics/graphics_server.cc index dea3f0fa..84720d23 100644 --- a/src/ballistica/graphics/graphics_server.cc +++ b/src/ballistica/graphics/graphics_server.cc @@ -30,15 +30,15 @@ void GraphicsServer::FullscreenCheck() { } #endif -GraphicsServer::GraphicsServer(Thread* thread) : Module("graphics", thread) { +GraphicsServer::GraphicsServer(Thread* thread) : thread_(thread) { // We're a singleton. assert(g_graphics_server == nullptr); g_graphics_server = this; // For janky old non-event-push mode, just fall back on a timer for rendering. if (!g_platform->IsEventPushMode()) { - render_timer_ = NewThreadTimer(1000 / 60, true, - NewLambdaRunnable([this] { TryRender(); })); + render_timer_ = this->thread()->NewTimer( + 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 // progress 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_graphics->EnableProgressBar(false); PushRemoveRenderHoldCall(); @@ -249,7 +249,7 @@ void GraphicsServer::RebuildLostContext() { // 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. - g_game->PushCall([this] { + g_game->thread()->PushCall([this] { g_media->MarkAllMediaForLoad(); g_graphics->EnableProgressBar(false); 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 (create_fullscreen_check_timer) { - NewThreadTimer(1000, false, - NewLambdaRunnable([this] { FullscreenCheck(); })); + thread()->NewTimer(1000, false, + NewLambdaRunnable([this] { FullscreenCheck(); })); } #endif // BA_OSTYPE_MACOS @@ -463,7 +463,7 @@ void GraphicsServer::HandleFullContextScreenRebuild( // 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. - g_game->PushCall([this] { + g_game->thread()->PushCall([this] { g_media->MarkAllMediaForLoad(); g_graphics->set_internal_components_inited(false); g_graphics->EnableProgressBar(false); @@ -708,18 +708,18 @@ void GraphicsServer::PushSetScreenCall(bool fullscreen, int width, int height, TextureQuality texture_quality, GraphicsQuality graphics_quality, const std::string& android_res) { - PushCall([=] { + thread()->PushCall([=] { SetScreen(fullscreen, width, height, texture_quality, graphics_quality, android_res); }); } void GraphicsServer::PushReloadMediaCall() { - PushCall([this] { ReloadMedia(); }); + thread()->PushCall([this] { ReloadMedia(); }); } void GraphicsServer::PushSetScreenGammaCall(float gamma) { - PushCall([this, gamma] { + thread()->PushCall([this, gamma] { assert(InGraphicsThread()); if (!renderer_) { return; @@ -729,7 +729,7 @@ void GraphicsServer::PushSetScreenGammaCall(float gamma) { } void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) { - PushCall([this, pixel_scale] { + thread()->PushCall([this, pixel_scale] { assert(InGraphicsThread()); if (!renderer_) { return; @@ -739,7 +739,7 @@ void GraphicsServer::PushSetScreenPixelScaleCall(float pixel_scale) { } void GraphicsServer::PushSetVSyncCall(bool sync, bool auto_sync) { - PushCall([this, sync, auto_sync] { + thread()->PushCall([this, sync, auto_sync] { assert(InGraphicsThread()); #if BA_SDL_BUILD @@ -769,7 +769,7 @@ void GraphicsServer::PushSetVSyncCall(bool sync, bool auto_sync) { void GraphicsServer::PushComponentUnloadCall( const std::vector*>& components) { - PushCall([this, components] { + thread()->PushCall([this, components] { // Unload all components we were passed. for (auto&& i : components) { (**i).Unload(); @@ -781,7 +781,7 @@ void GraphicsServer::PushComponentUnloadCall( } void GraphicsServer::PushRemoveRenderHoldCall() { - PushCall([this] { + thread()->PushCall([this] { assert(render_hold_); render_hold_--; if (render_hold_ < 0) { diff --git a/src/ballistica/graphics/graphics_server.h b/src/ballistica/graphics/graphics_server.h index 8b1e9f78..79cb989d 100644 --- a/src/ballistica/graphics/graphics_server.h +++ b/src/ballistica/graphics/graphics_server.h @@ -9,7 +9,6 @@ #include #include "ballistica/ballistica.h" -#include "ballistica/core/module.h" #include "ballistica/core/object.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 // Graphics -class GraphicsServer : public Module { +class GraphicsServer { public: explicit GraphicsServer(Thread* thread); auto PushSetScreenGammaCall(float gamma) -> void; @@ -170,7 +169,7 @@ class GraphicsServer : public Module { } auto RebuildLostContext() -> void; - ~GraphicsServer() override; + ~GraphicsServer(); auto renderer() { return renderer_; } auto quality() const -> GraphicsQuality { @@ -240,6 +239,7 @@ class GraphicsServer : public Module { auto texture_quality_requested() const { return texture_quality_requested_; } auto renderer() const { return renderer_; } auto initial_screen_created() const { return initial_screen_created_; } + auto thread() const -> Thread* { return thread_; } private: auto HandleFullscreenToggling(bool do_set_existing_fs, bool do_toggle_fs, @@ -273,6 +273,7 @@ class GraphicsServer : public Module { #if BA_ENABLE_OPENGL std::unique_ptr gl_context_; #endif + Thread* thread_{}; float res_x_{}; float res_y_{}; float res_x_virtual_{0.0f}; diff --git a/src/ballistica/graphics/text/text_graphics.cc b/src/ballistica/graphics/text/text_graphics.cc index 8e17d62d..fb8de9fb 100644 --- a/src/ballistica/graphics/text/text_graphics.cc +++ b/src/ballistica/graphics/text/text_graphics.cc @@ -826,7 +826,7 @@ auto TextGraphics::GetBigCharIndex(int c) -> int { } void TextGraphics::LoadGlyphPage(uint32_t index) { - std::lock_guard lock(glyph_load_mutex_); + std::scoped_lock lock(glyph_load_mutex_); // Its possible someone else coulda loaded it since we last checked. if (g_glyph_pages[index] == nullptr) { diff --git a/src/ballistica/input/device/joystick.cc b/src/ballistica/input/device/joystick.cc index e70e6398..492b3b6e 100644 --- a/src/ballistica/input/device/joystick.cc +++ b/src/ballistica/input/device/joystick.cc @@ -5,6 +5,7 @@ #include "ballistica/app/app.h" #include "ballistica/app/app_globals.h" #include "ballistica/audio/audio.h" +#include "ballistica/core/thread.h" #include "ballistica/game/connection/connection_set.h" #include "ballistica/game/player.h" #include "ballistica/graphics/renderer.h" @@ -305,7 +306,7 @@ Joystick::~Joystick() { #if BA_ENABLE_SDL_JOYSTICKS assert(g_app); auto joystick = sdl_joystick_; - g_app->PushCall([joystick] { SDL_JoystickClose(joystick); }); + g_app->thread()->PushCall([joystick] { SDL_JoystickClose(joystick); }); sdl_joystick_ = nullptr; #else Log("sdl_joystick_ set in non-sdl-joystick build destructor."); diff --git a/src/ballistica/input/input.cc b/src/ballistica/input/input.cc index 85ae4c91..b7a2cefb 100644 --- a/src/ballistica/input/input.cc +++ b/src/ballistica/input/input.cc @@ -5,6 +5,7 @@ #include "ballistica/app/app_config.h" #include "ballistica/app/app_globals.h" #include "ballistica/audio/audio.h" +#include "ballistica/core/thread.h" #include "ballistica/game/player.h" #include "ballistica/graphics/camera.h" #include "ballistica/input/device/joystick.h" @@ -330,7 +331,7 @@ Input::Input() { } void Input::PushCreateKeyboardInputDevices() { - g_game->PushCall([this] { CreateKeyboardInputDevices(); }); + g_game->thread()->PushCall([this] { CreateKeyboardInputDevices(); }); } void Input::CreateKeyboardInputDevices() { @@ -346,7 +347,7 @@ void Input::CreateKeyboardInputDevices() { } void Input::PushDestroyKeyboardInputDevices() { - g_game->PushCall([this] { DestroyKeyboardInputDevices(); }); + g_game->thread()->PushCall([this] { DestroyKeyboardInputDevices(); }); } void Input::DestroyKeyboardInputDevices() { @@ -554,7 +555,7 @@ void Input::ShowStandardInputDeviceDisconnectedMessage(InputDevice* j) { void Input::PushAddInputDeviceCall(InputDevice* input_device, 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); }); } @@ -616,7 +617,7 @@ void Input::AddInputDevice(InputDevice* input, bool standard_message) { void Input::PushRemoveInputDeviceCall(InputDevice* input_device, 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); }); } @@ -1087,7 +1088,7 @@ void Input::HandleBackPress(bool from_toolbar) { } void Input::PushTextInputEvent(const std::string& text) { - g_game->PushCall([this, text] { + g_game->thread()->PushCall([this, text] { mark_input_active(); // 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) -> void { - g_game->PushCall([this, event, input_device] { + g_game->thread()->PushCall([this, 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) { - g_game->PushCall([this, keysym] { HandleKeyPress(&keysym); }); + g_game->thread()->PushCall([this, keysym] { HandleKeyPress(&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) { @@ -1392,7 +1393,7 @@ auto Input::UpdateModKeyStates(const SDL_Keysym* keysym, bool press) -> 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 { @@ -1425,7 +1426,7 @@ auto Input::HandleMouseScroll(const Vector2f& amount) -> void { auto Input::PushSmoothMouseScrollEvent(const Vector2f& velocity, bool momentum) -> void { - g_game->PushCall([this, velocity, momentum] { + g_game->thread()->PushCall([this, velocity, momentum] { HandleSmoothMouseScroll(velocity, momentum); }); } @@ -1460,7 +1461,7 @@ auto Input::HandleSmoothMouseScroll(const Vector2f& velocity, bool momentum) } 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 { @@ -1511,7 +1512,7 @@ auto Input::HandleMouseMotion(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); }); } @@ -1588,7 +1589,7 @@ auto Input::HandleMouseDown(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); }); } @@ -1637,7 +1638,7 @@ auto Input::HandleMouseUp(int button, const Vector2f& position) -> void { } 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) { diff --git a/src/ballistica/input/remote_app.cc b/src/ballistica/input/remote_app.cc index 74c6b066..5afe1fd6 100644 --- a/src/ballistica/input/remote_app.cc +++ b/src/ballistica/input/remote_app.cc @@ -62,7 +62,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt, size_t msg_len = 1 + strlen(msg + 1); // This needs to be locked during any sd changes/writes. - std::lock_guard lock(g_network_reader->sd_mutex()); + std::scoped_lock lock(g_network_reader->sd_mutex()); sendto(socket, msg, static_cast_check_fit(msg_len), 0, addr, static_cast(addr_len)); @@ -91,7 +91,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt, static_cast_check_fit(RemoteError::kVersionMismatch)}; // This needs to be locked during any sd changes/writes. - std::lock_guard lock(g_network_reader->sd_mutex()); + std::scoped_lock lock(g_network_reader->sd_mutex()); sendto(socket, reinterpret_cast(data), sizeof(data), 0, addr, static_cast(addr_len)); break; @@ -135,7 +135,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt, static_cast(protocol_response)}; // This needs to be locked during any sd changes/writes. - std::lock_guard lock(g_network_reader->sd_mutex()); + std::scoped_lock lock(g_network_reader->sd_mutex()); sendto(socket, reinterpret_cast(data), sizeof(data), 0, addr, static_cast(addr_len)); } else { @@ -145,7 +145,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt, RemoteError::kNotAcceptingConnections)}; // This needs to be locked during any sd changes/writes. - std::lock_guard lock(g_network_reader->sd_mutex()); + std::scoped_lock lock(g_network_reader->sd_mutex()); sendto(socket, reinterpret_cast(data), sizeof(data), 0, addr, static_cast(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}; // This needs to be locked during any sd changes/writes. - std::lock_guard lock(g_network_reader->sd_mutex()); + std::scoped_lock lock(g_network_reader->sd_mutex()); sendto(socket, reinterpret_cast(data), 1, 0, addr, static_cast(addr_len)); } @@ -204,7 +204,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt, static_cast_check_fit(RemoteError::kNotConnected)}; // This needs to be locked during any sd changes/writes. - std::lock_guard lock(g_network_reader->sd_mutex()); + std::scoped_lock lock(g_network_reader->sd_mutex()); sendto(socket, reinterpret_cast(data), sizeof(data), 0, addr, static_cast(addr_len)); 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}; // This needs to be locked during any sd changes/writes. - std::lock_guard lock(g_network_reader->sd_mutex()); + std::scoped_lock lock(g_network_reader->sd_mutex()); sendto(socket, reinterpret_cast(data), 2, 0, addr, static_cast(addr_len)); @@ -331,7 +331,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt, static_cast_check_fit(RemoteError::kVersionMismatch)}; // This needs to be locked during any sd changes/writes. - std::lock_guard lock(g_network_reader->sd_mutex()); + std::scoped_lock lock(g_network_reader->sd_mutex()); sendto(socket, reinterpret_cast(data), sizeof(data), 0, addr, static_cast(addr_len)); break; diff --git a/src/ballistica/input/std_input_module.cc b/src/ballistica/input/std_input_module.cc index 88e42fd7..0537af8d 100644 --- a/src/ballistica/input/std_input_module.cc +++ b/src/ballistica/input/std_input_module.cc @@ -7,20 +7,19 @@ #endif #include "ballistica/app/app_globals.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.h" #include "ballistica/platform/platform.h" namespace ballistica { -StdInputModule::StdInputModule(Thread* thread) : Module("stdin", thread) { +StdInputModule::StdInputModule(Thread* thread) : thread_(thread) { assert(g_std_input_module == nullptr); g_std_input_module = this; } -StdInputModule::~StdInputModule() = default; - void StdInputModule::PushBeginReadCall() { - PushCall([this] { + thread()->PushCall([this] { bool stdin_is_terminal = g_platform->is_stdin_a_terminal(); while (true) { @@ -28,7 +27,7 @@ void StdInputModule::PushBeginReadCall() { // We send this to the game thread so it happens AFTER the // results of the last script-command message we may have just sent. if (stdin_is_terminal) { - g_game->PushCall([] { + g_game->thread()->PushCall([] { if (!g_app_globals->shutting_down) { printf(">>> "); fflush(stdout); diff --git a/src/ballistica/input/std_input_module.h b/src/ballistica/input/std_input_module.h index 2a8137c9..7b8e04ee 100644 --- a/src/ballistica/input/std_input_module.h +++ b/src/ballistica/input/std_input_module.h @@ -3,17 +3,18 @@ #ifndef BALLISTICA_INPUT_STD_INPUT_MODULE_H_ #define BALLISTICA_INPUT_STD_INPUT_MODULE_H_ -#include "ballistica/core/module.h" +#include "ballistica/ballistica.h" namespace ballistica { -class StdInputModule : public Module { +class StdInputModule { public: explicit StdInputModule(Thread* thread); - ~StdInputModule() override; void PushBeginReadCall(); + auto thread() const -> Thread* { return thread_; } private: + Thread* thread_{}; std::string pending_input_; }; diff --git a/src/ballistica/internal/app_internal.h b/src/ballistica/internal/app_internal.h index 3c26ee9b..e1cf5d08 100644 --- a/src/ballistica/internal/app_internal.h +++ b/src/ballistica/internal/app_internal.h @@ -10,7 +10,7 @@ namespace ballistica { -auto CreateAppInternal() -> AppInternal*; +auto GetAppInternal() -> AppInternal*; class AppInternal { public: diff --git a/src/ballistica/media/media.cc b/src/ballistica/media/media.cc index 3a9d7dea..b0e280d5 100644 --- a/src/ballistica/media/media.cc +++ b/src/ballistica/media/media.cc @@ -7,6 +7,7 @@ #endif #include "ballistica/audio/audio_server.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.h" #include "ballistica/generic/timer.h" #include "ballistica/graphics/graphics_server.h" @@ -614,7 +615,8 @@ void Media::MarkComponentForLoad(MediaComponentData* c) { // ClearPendingLoadsDoneList) auto media_ptr = new Object::Ref(c); - g_media_server->PushRunnable(Object::NewDeferred(media_ptr)); + g_media_server->thread()->PushRunnable( + Object::NewDeferred(media_ptr)); } #pragma clang diagnostic push @@ -756,7 +758,7 @@ auto Media::RunPendingLoadList(std::vector*>* c_list) -> bool { std::vector*> l_unfinished; std::vector*> l_finished; { - std::lock_guard lock(pending_load_list_mutex_); + std::scoped_lock lock(pending_load_list_mutex_); // If we're already out of time. if (!flush && GetRealTime() - starttime > PENDING_LOAD_PROCESS_TIME) { @@ -807,7 +809,7 @@ auto Media::RunPendingLoadList(std::vector*>* c_list) -> bool { // Now add unfinished ones back onto the original list and finished ones into // the done list. { - std::lock_guard lock(pending_load_list_mutex_); + std::scoped_lock lock(pending_load_list_mutex_); for (auto&& i : l) { c_list->push_back(i); } @@ -1173,14 +1175,14 @@ void Media::AddPendingLoad(Object::Ref* c) { case MediaType::kTexture: case MediaType::kModel: { // Tell the graphics thread there's pending loads... - std::lock_guard lock(pending_load_list_mutex_); + std::scoped_lock lock(pending_load_list_mutex_); pending_loads_graphics_.push_back(c); break; } case MediaType::kSound: { // Tell the audio thread there's pending loads. { - std::lock_guard lock(pending_load_list_mutex_); + std::scoped_lock lock(pending_load_list_mutex_); pending_loads_sounds_.push_back(c); } g_audio_server->PushHavePendingLoadsCall(); @@ -1189,7 +1191,7 @@ void Media::AddPendingLoad(Object::Ref* c) { default: { // Tell the game thread there's pending loads. { - std::lock_guard lock(pending_load_list_mutex_); + std::scoped_lock lock(pending_load_list_mutex_); pending_loads_other_.push_back(c); } g_game->PushHavePendingLoadsCall(); @@ -1201,7 +1203,7 @@ void Media::AddPendingLoad(Object::Ref* c) { void Media::ClearPendingLoadsDoneList() { assert(InLogicThread()); - std::lock_guard 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 // the game thread. diff --git a/src/ballistica/media/media.h b/src/ballistica/media/media.h index 64a3cf98..bdb3cd8a 100644 --- a/src/ballistica/media/media.h +++ b/src/ballistica/media/media.h @@ -3,13 +3,14 @@ #ifndef BALLISTICA_MEDIA_MEDIA_H_ #define BALLISTICA_MEDIA_MEDIA_H_ +#include #include #include #include #include "ballistica/core/context.h" -#include "ballistica/core/module.h" #include "ballistica/core/object.h" +#include "ballistica/generic/runnable.h" namespace ballistica { diff --git a/src/ballistica/media/media_server.cc b/src/ballistica/media/media_server.cc index 6785cdd0..5592b1b6 100644 --- a/src/ballistica/media/media_server.cc +++ b/src/ballistica/media/media_server.cc @@ -2,6 +2,7 @@ #include "ballistica/media/media_server.h" +#include "ballistica/core/thread.h" #include "ballistica/generic/huffman.h" #include "ballistica/generic/timer.h" #include "ballistica/generic/utils.h" @@ -12,7 +13,7 @@ namespace ballistica { MediaServer::MediaServer(Thread* thread) - : Module("media", thread), + : thread_(thread), writing_replay_(false), replay_message_bytes_(0), replays_broken_(false), @@ -21,14 +22,14 @@ MediaServer::MediaServer(Thread* thread) g_media_server = this; // get our thread to give us periodic processing time... - process_timer_ = - NewThreadTimer(1000, true, NewLambdaRunnable([this] { Process(); })); + process_timer_ = this->thread()->NewTimer( + 1000, true, NewLambdaRunnable([this] { Process(); })); } MediaServer::~MediaServer() = default; void MediaServer::PushBeginWriteReplayCall() { - PushCall([this] { + thread()->PushCall([this] { if (replays_broken_) { return; } @@ -79,7 +80,7 @@ void MediaServer::PushBeginWriteReplayCall() { } void MediaServer::PushAddMessageToReplayCall(const std::vector& data) { - PushCall([this, data] { + thread()->PushCall([this, data] { if (replays_broken_) { return; } @@ -110,7 +111,7 @@ void MediaServer::PushAddMessageToReplayCall(const std::vector& data) { } void MediaServer::PushEndWriteReplayCall() { - PushCall([this] { + thread()->PushCall([this] { if (replays_broken_) { return; } diff --git a/src/ballistica/media/media_server.h b/src/ballistica/media/media_server.h index b52b1cd0..cff7007e 100644 --- a/src/ballistica/media/media_server.h +++ b/src/ballistica/media/media_server.h @@ -6,21 +6,23 @@ #include #include -#include "ballistica/core/module.h" +#include "ballistica/core/object.h" namespace ballistica { -class MediaServer : public Module { +class MediaServer { public: explicit MediaServer(Thread* thread); - ~MediaServer() override; + ~MediaServer(); void PushBeginWriteReplayCall(); void PushEndWriteReplayCall(); void PushAddMessageToReplayCall(const std::vector& data); + auto thread() const -> Thread* { return thread_; } private: void Process(); void WriteReplayMessages(); + Thread* thread_{}; FILE* replay_out_file_{}; size_t replay_bytes_written_{}; bool writing_replay_{}; diff --git a/src/ballistica/networking/network_reader.cc b/src/ballistica/networking/network_reader.cc index ba1655f9..8ca323ff 100644 --- a/src/ballistica/networking/network_reader.cc +++ b/src/ballistica/networking/network_reader.cc @@ -239,7 +239,7 @@ auto NetworkReader::RunThread() -> int { "happen"); } else if (rresult == -1) { // This needs to be locked during any sd changes/writes. - std::lock_guard lock(sd_mutex_); + std::scoped_lock lock(sd_mutex_); // If either of our sockets goes down lets close *both* of // them. @@ -258,7 +258,7 @@ auto NetworkReader::RunThread() -> int { // sockets (we ping ourself for this purpose). if (paused_) { // This needs to be locked during any sd changes/writes. - std::lock_guard lock(sd_mutex_); + std::scoped_lock lock(sd_mutex_); if (sd4_ != -1) { g_platform->CloseSocket(sd4_); sd4_ = -1; @@ -274,7 +274,7 @@ auto NetworkReader::RunThread() -> int { break; case BA_PACKET_SIMPLE_PING: { // This needs to be locked during any sd changes/writes. - std::lock_guard lock(sd_mutex_); + std::scoped_lock lock(sd_mutex_); char msg[1] = {BA_PACKET_SIMPLE_PONG}; sendto(sd, msg, 1, 0, reinterpret_cast(&from), from_size); @@ -290,7 +290,7 @@ auto NetworkReader::RunThread() -> int { std::vector msg(1 + response.size()); msg[0] = BA_PACKET_JSON_PONG; memcpy(msg.data() + 1, response.c_str(), response.size()); - std::lock_guard lock(sd_mutex_); + std::scoped_lock lock(sd_mutex_); sendto( sd, msg.data(), static_cast_check_fit(msg.size()), @@ -378,7 +378,7 @@ auto NetworkReader::RunThread() -> int { auto NetworkReader::OpenSockets() -> void { // This needs to be locked during any socket-descriptor changes/writes. - std::lock_guard lock(sd_mutex_); + std::scoped_lock lock(sd_mutex_); int result; int print_port_unavailable = false; diff --git a/src/ballistica/networking/network_write_module.cc b/src/ballistica/networking/network_write_module.cc index 88b1b415..93dbde63 100644 --- a/src/ballistica/networking/network_write_module.cc +++ b/src/ballistica/networking/network_write_module.cc @@ -2,13 +2,13 @@ #include "ballistica/networking/network_write_module.h" +#include "ballistica/core/thread.h" #include "ballistica/networking/networking.h" #include "ballistica/networking/sockaddr.h" namespace ballistica { -NetworkWriteModule::NetworkWriteModule(Thread* thread) - : Module("networkWrite", thread) { +NetworkWriteModule::NetworkWriteModule(Thread* thread) : thread_(thread) { // we're a singleton assert(g_network_write_module == nullptr); g_network_write_module = this; @@ -18,11 +18,11 @@ void NetworkWriteModule::PushSendToCall(const std::vector& msg, const SockAddr& addr) { // Avoid buffer-full errors if something is causing us to write too often; // 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."); return; } - PushCall([this, msg, addr] { + thread()->PushCall([this, msg, addr] { assert(g_network_reader); Networking::SendTo(msg, addr); }); diff --git a/src/ballistica/networking/network_write_module.h b/src/ballistica/networking/network_write_module.h index 821d3e77..20a01131 100644 --- a/src/ballistica/networking/network_write_module.h +++ b/src/ballistica/networking/network_write_module.h @@ -5,15 +5,19 @@ #include -#include "ballistica/core/module.h" +#include "ballistica/ballistica.h" namespace ballistica { // this thread handles network output and whatnot -class NetworkWriteModule : public Module { +class NetworkWriteModule { public: void PushSendToCall(const std::vector& msg, const SockAddr& addr); explicit NetworkWriteModule(Thread* thread); + auto thread() const -> Thread* { return thread_; } + + private: + Thread* thread_{}; }; } // namespace ballistica diff --git a/src/ballistica/networking/networking.cc b/src/ballistica/networking/networking.cc index 8557eded..e35296a6 100644 --- a/src/ballistica/networking/networking.cc +++ b/src/ballistica/networking/networking.cc @@ -159,7 +159,7 @@ void Networking::HostScanCycle() { // Add or modify an entry for this. { - std::lock_guard lock(scan_results_mutex_); + std::scoped_lock lock(scan_results_mutex_); // Ignore if it looks like its us. if (id != GetAppInstanceUUID()) { @@ -197,7 +197,7 @@ auto Networking::GetScanResults() -> std::vector { std::vector results; results.resize(scan_results_.size()); { - std::lock_guard lock(scan_results_mutex_); + std::scoped_lock lock(scan_results_mutex_); int out_num = 0; for (auto&& i : scan_results_) { ScanResultsEntryPriv& in(i.second); @@ -252,7 +252,7 @@ void Networking::SendTo(const std::vector& buffer, assert(!buffer.empty()); // This needs to be locked during any sd changes/writes. - std::lock_guard 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 // otherwise. diff --git a/src/ballistica/networking/telnet_server.cc b/src/ballistica/networking/telnet_server.cc index d22b4a99..a1e6eb7d 100644 --- a/src/ballistica/networking/telnet_server.cc +++ b/src/ballistica/networking/telnet_server.cc @@ -4,6 +4,7 @@ #include "ballistica/app/app_globals.h" #include "ballistica/core/context.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.h" #include "ballistica/networking/networking.h" #include "ballistica/networking/networking_sys.h" @@ -188,7 +189,7 @@ void TelnetServer::PushTelnetScriptCommand(const std::string& command) { if (g_game == nullptr) { return; } - g_game->PushCall([this, command] { + g_game->thread()->PushCall([this, command] { // These are always run in whichever context is 'visible'. ScopedSetContext cp(g_game->GetForegroundContext()); 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) { 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) { diff --git a/src/ballistica/platform/platform.cc b/src/ballistica/platform/platform.cc index 4a7dd8f0..1ed29e2b 100644 --- a/src/ballistica/platform/platform.cc +++ b/src/ballistica/platform/platform.cc @@ -659,20 +659,20 @@ void Platform::CreateApp() { #endif #if BA_HEADLESS_BUILD - g_main_thread->AddModule(); + new HeadlessApp(g_main_thread); #elif BA_RIFT_BUILD // Rift build can spin up in either VR or regular mode. if (g_app_globals->vr_mode) { - g_main_thread->AddModule(); + new VRApp(g_main_thread); } else { - g_main_thread->AddModule(); + new SDLApp(g_main_thread); } #elif BA_CARDBOARD_BUILD - g_main_thread->AddModule(); + new VRApp(g_main_thread); #elif BA_SDL_BUILD - g_main_thread->AddModule(); + new SDLApp(g_main_thread); #else - g_main_thread->AddModule(); + new App(g_main_thread); #endif // 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); #endif #if !BA_HEADLESS_BUILD - bg_dynamics_thread->AddModule(); + bg_dynamics_thread->PushCallSynchronous( + [bg_dynamics_thread] { new BGDynamicsServer(bg_dynamics_thread); }); #endif 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 // pausable list. auto* std_input_thread = new Thread(ThreadIdentifier::kStdin); - std_input_thread->AddModule(); + std_input_thread->PushCallSynchronous( + [std_input_thread] { new StdInputModule(std_input_thread); }); g_std_input_module->PushBeginReadCall(); } } diff --git a/src/ballistica/platform/sdl/sdl_app.cc b/src/ballistica/platform/sdl/sdl_app.cc index 42bdb72a..c7909201 100644 --- a/src/ballistica/platform/sdl/sdl_app.cc +++ b/src/ballistica/platform/sdl/sdl_app.cc @@ -4,6 +4,7 @@ #include "ballistica/platform/sdl/sdl_app.h" +#include "ballistica/app/stress_test.h" #include "ballistica/core/thread.h" #include "ballistica/dynamics/bg/bg_dynamics.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 // thing, except that we're free to handle other matters concurrently // instead of being locked in a delay call. - NewThreadTimer(10, true, NewLambdaRunnable([this] { - assert(g_app); - g_app->RunEvents(); - })); + this->thread()->NewTimer(10, true, NewLambdaRunnable([this] { + assert(g_app); + g_app->RunEvents(); + })); } } diff --git a/src/ballistica/python/class/python_class_activity_data.cc b/src/ballistica/python/class/python_class_activity_data.cc index be2cd942..016a1a8b 100644 --- a/src/ballistica/python/class/python_class_activity_data.cc +++ b/src/ballistica/python/class/python_class_activity_data.cc @@ -2,6 +2,7 @@ #include "ballistica/python/class/python_class_activity_data.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.h" #include "ballistica/game/host_activity.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. if (!InLogicThread()) { Object::WeakRef* h = self->host_activity_; - g_game->PushCall([h] { delete h; }); + g_game->thread()->PushCall([h] { delete h; }); } else { delete self->host_activity_; } diff --git a/src/ballistica/python/class/python_class_collide_model.cc b/src/ballistica/python/class/python_class_collide_model.cc index 368f4379..15da88cc 100644 --- a/src/ballistica/python/class/python_class_collide_model.cc +++ b/src/ballistica/python/class/python_class_collide_model.cc @@ -2,6 +2,7 @@ #include "ballistica/python/class/python_class_collide_model.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.h" #include "ballistica/media/component/collide_model.h" #include "ballistica/python/python.h" @@ -104,7 +105,7 @@ void PythonClassCollideModel::tp_dealloc(PythonClassCollideModel* self) { // be; otherwise do it immediately if (!InLogicThread()) { Object::Ref* c = self->collide_model_; - g_game->PushCall([c] { Delete(c); }); + g_game->thread()->PushCall([c] { Delete(c); }); } else { Delete(self->collide_model_); } diff --git a/src/ballistica/python/class/python_class_context.cc b/src/ballistica/python/class/python_class_context.cc index 4d246cc8..09338be3 100644 --- a/src/ballistica/python/class/python_class_context.cc +++ b/src/ballistica/python/class/python_class_context.cc @@ -2,6 +2,7 @@ #include "ballistica/python/class/python_class_context.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.h" #include "ballistica/game/host_activity.h" #include "ballistica/game/session/host_session.h" @@ -187,7 +188,7 @@ void PythonClassContext::tp_dealloc(PythonClassContext* self) { if (!InLogicThread()) { Context* c = self->context_; Context* c2 = self->context_prev_; - g_game->PushCall([c, c2] { + g_game->thread()->PushCall([c, c2] { delete c; delete c2; }); diff --git a/src/ballistica/python/class/python_class_context_call.cc b/src/ballistica/python/class/python_class_context_call.cc index 9fe65f7a..9e5eac31 100644 --- a/src/ballistica/python/class/python_class_context_call.cc +++ b/src/ballistica/python/class/python_class_context_call.cc @@ -2,6 +2,7 @@ #include "ballistica/python/class/python_class_context_call.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.h" #include "ballistica/python/python.h" #include "ballistica/python/python_context_call.h" @@ -118,7 +119,7 @@ void PythonClassContextCall::tp_dealloc(PythonClassContextCall* self) { // be; otherwise do it immediately if (!InLogicThread()) { Object::Ref* c = self->context_call_; - g_game->PushCall([c] { delete c; }); + g_game->thread()->PushCall([c] { delete c; }); } else { delete self->context_call_; } diff --git a/src/ballistica/python/class/python_class_data.cc b/src/ballistica/python/class/python_class_data.cc index 2c680fe6..88791137 100644 --- a/src/ballistica/python/class/python_class_data.cc +++ b/src/ballistica/python/class/python_class_data.cc @@ -2,6 +2,7 @@ #include "ballistica/python/class/python_class_data.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.h" #include "ballistica/media/component/data.h" #include "ballistica/python/python.h" @@ -101,7 +102,7 @@ void PythonClassData::tp_dealloc(PythonClassData* self) { // be; otherwise do it immediately if (!InLogicThread()) { Object::Ref* s = self->data_; - g_game->PushCall([s] { Delete(s); }); + g_game->thread()->PushCall([s] { Delete(s); }); } else { Delete(self->data_); } diff --git a/src/ballistica/python/class/python_class_input_device.cc b/src/ballistica/python/class/python_class_input_device.cc index 3fb445ed..38e04607 100644 --- a/src/ballistica/python/class/python_class_input_device.cc +++ b/src/ballistica/python/class/python_class_input_device.cc @@ -2,6 +2,7 @@ #include "ballistica/python/class/python_class_input_device.h" +#include "ballistica/core/thread.h" #include "ballistica/game/player.h" #include "ballistica/input/device/input_device.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? if (!InLogicThread()) { Object::WeakRef* d = self->input_device_; - g_game->PushCall([d] { delete d; }); + g_game->thread()->PushCall([d] { delete d; }); } else { delete self->input_device_; } diff --git a/src/ballistica/python/class/python_class_material.cc b/src/ballistica/python/class/python_class_material.cc index 90d6f253..2999279e 100644 --- a/src/ballistica/python/class/python_class_material.cc +++ b/src/ballistica/python/class/python_class_material.cc @@ -2,6 +2,7 @@ #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/material.h" #include "ballistica/dynamics/material/material_component.h" @@ -147,7 +148,7 @@ void PythonClassMaterial::tp_dealloc(PythonClassMaterial* self) { // need be.. otherwise do it immediately. if (!InLogicThread()) { Object::Ref* ptr = self->material_; - g_game->PushCall([ptr] { Delete(ptr); }); + g_game->thread()->PushCall([ptr] { Delete(ptr); }); } else { Delete(self->material_); } diff --git a/src/ballistica/python/class/python_class_model.cc b/src/ballistica/python/class/python_class_model.cc index b38cbe88..55e2b090 100644 --- a/src/ballistica/python/class/python_class_model.cc +++ b/src/ballistica/python/class/python_class_model.cc @@ -2,6 +2,7 @@ #include "ballistica/python/class/python_class_model.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.h" #include "ballistica/media/component/model.h" #include "ballistica/python/python.h" @@ -102,7 +103,7 @@ void PythonClassModel::tp_dealloc(PythonClassModel* self) { // be; otherwise do it immediately if (!InLogicThread()) { Object::Ref* m = self->model_; - g_game->PushCall([m] { Delete(m); }); + g_game->thread()->PushCall([m] { Delete(m); }); } else { Delete(self->model_); } diff --git a/src/ballistica/python/class/python_class_node.cc b/src/ballistica/python/class/python_class_node.cc index 18755ead..a9e6b395 100644 --- a/src/ballistica/python/class/python_class_node.cc +++ b/src/ballistica/python/class/python_class_node.cc @@ -4,6 +4,7 @@ #include +#include "ballistica/core/thread.h" #include "ballistica/game/game_stream.h" #include "ballistica/python/python.h" #include "ballistica/scene/scene.h" @@ -113,7 +114,7 @@ void PythonClassNode::tp_dealloc(PythonClassNode* self) { // be; otherwise do it immediately. if (!InLogicThread()) { Object::WeakRef* n = self->node_; - g_game->PushCall([n] { delete n; }); + g_game->thread()->PushCall([n] { delete n; }); } else { delete self->node_; } diff --git a/src/ballistica/python/class/python_class_session_data.cc b/src/ballistica/python/class/python_class_session_data.cc index 5f9914b8..ec97ae43 100644 --- a/src/ballistica/python/class/python_class_session_data.cc +++ b/src/ballistica/python/class/python_class_session_data.cc @@ -2,6 +2,7 @@ #include "ballistica/python/class/python_class_session_data.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.h" #include "ballistica/game/session/session.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? if (!InLogicThread()) { Object::WeakRef* s = self->session_; - g_game->PushCall([s] { delete s; }); + g_game->thread()->PushCall([s] { delete s; }); } else { delete self->session_; } diff --git a/src/ballistica/python/class/python_class_session_player.cc b/src/ballistica/python/class/python_class_session_player.cc index 7220a09d..2c3a7ffc 100644 --- a/src/ballistica/python/class/python_class_session_player.cc +++ b/src/ballistica/python/class/python_class_session_player.cc @@ -2,6 +2,7 @@ #include "ballistica/python/class/python_class_session_player.h" +#include "ballistica/core/thread.h" #include "ballistica/game/host_activity.h" #include "ballistica/game/player.h" #include "ballistica/game/session/host_session.h" @@ -187,7 +188,7 @@ void PythonClassSessionPlayer::tp_dealloc(PythonClassSessionPlayer* self) { // be; otherwise do it immediately. if (!InLogicThread()) { Object::WeakRef* p = self->player_; - g_game->PushCall([p] { delete p; }); + g_game->thread()->PushCall([p] { delete p; }); } else { delete self->player_; } diff --git a/src/ballistica/python/class/python_class_sound.cc b/src/ballistica/python/class/python_class_sound.cc index 9e7f1d9b..4186668e 100644 --- a/src/ballistica/python/class/python_class_sound.cc +++ b/src/ballistica/python/class/python_class_sound.cc @@ -2,6 +2,7 @@ #include "ballistica/python/class/python_class_sound.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.h" #include "ballistica/media/component/sound.h" #include "ballistica/python/python.h" @@ -101,7 +102,7 @@ void PythonClassSound::tp_dealloc(PythonClassSound* self) { // be; otherwise do it immediately if (!InLogicThread()) { Object::Ref* s = self->sound_; - g_game->PushCall([s] { Delete(s); }); + g_game->thread()->PushCall([s] { Delete(s); }); } else { Delete(self->sound_); } diff --git a/src/ballistica/python/class/python_class_texture.cc b/src/ballistica/python/class/python_class_texture.cc index 72ffa741..396440dc 100644 --- a/src/ballistica/python/class/python_class_texture.cc +++ b/src/ballistica/python/class/python_class_texture.cc @@ -2,6 +2,7 @@ #include "ballistica/python/class/python_class_texture.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.h" #include "ballistica/media/component/texture.h" #include "ballistica/python/python.h" @@ -94,7 +95,7 @@ void PythonClassTexture::tp_dealloc(PythonClassTexture* self) { // be; otherwise do it immediately. if (!InLogicThread()) { Object::Ref* t = self->texture_; - g_game->PushCall([t] { Delete(t); }); + g_game->thread()->PushCall([t] { Delete(t); }); } else { Delete(self->texture_); } diff --git a/src/ballistica/python/class/python_class_timer.cc b/src/ballistica/python/class/python_class_timer.cc index 123d951a..8a53687d 100644 --- a/src/ballistica/python/class/python_class_timer.cc +++ b/src/ballistica/python/class/python_class_timer.cc @@ -2,6 +2,7 @@ #include "ballistica/python/class/python_class_timer.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.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 a2 = self->timer_id_; auto a3 = self->context_; - g_game->PushCall( + g_game->thread()->PushCall( [a0, a1, a2, a3] { PythonClassTimer::DoDelete(a0, a1, a2, a3); }); } else { DoDelete(self->have_timer_, self->time_type_, self->timer_id_, diff --git a/src/ballistica/python/class/python_class_widget.cc b/src/ballistica/python/class/python_class_widget.cc index 7f48bed7..cf8e0a07 100644 --- a/src/ballistica/python/class/python_class_widget.cc +++ b/src/ballistica/python/class/python_class_widget.cc @@ -2,6 +2,7 @@ #include "ballistica/python/class/python_class_widget.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.h" #include "ballistica/generic/utils.h" #include "ballistica/graphics/graphics.h" @@ -92,7 +93,7 @@ void PythonClassWidget::tp_dealloc(PythonClassWidget* self) { // need be if (!InLogicThread()) { Object::WeakRef* w = self->widget_; - g_game->PushCall([w] { delete w; }); + g_game->thread()->PushCall([w] { delete w; }); } else { delete self->widget_; } diff --git a/src/ballistica/python/methods/python_methods_system.cc b/src/ballistica/python/methods/python_methods_system.cc index 0a1108b1..42f9fca9 100644 --- a/src/ballistica/python/methods/python_methods_system.cc +++ b/src/ballistica/python/methods/python_methods_system.cc @@ -535,7 +535,7 @@ auto PyGetLog(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { BA_PYTHON_TRY; std::string log_fin; { - std::lock_guard lock(g_app_globals->log_mutex); + std::scoped_lock lock(g_app_globals->log_mutex); log_fin = g_app_globals->log; } // we want to use something with error handling here since the last diff --git a/src/ballistica/python/python.cc b/src/ballistica/python/python.cc index 135b36c1..1a9c8e33 100644 --- a/src/ballistica/python/python.cc +++ b/src/ballistica/python/python.cc @@ -4,6 +4,7 @@ #include "ballistica/app/app_globals.h" #include "ballistica/audio/audio.h" +#include "ballistica/core/thread.h" #include "ballistica/dynamics/material/material.h" #include "ballistica/game/account.h" #include "ballistica/game/friend_score_set.h" @@ -853,6 +854,38 @@ auto Python::GetPyVector3f(PyObject* o) -> Vector3f { 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(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{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) { assert(InLogicThread()); assert(g_python); @@ -939,6 +972,9 @@ void Python::Reset(bool do_init) { 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(). g_app_internal->PyInitialize(&config); @@ -1063,14 +1099,14 @@ auto Python::InitModuleClasses(PyObject* module) -> void { } void Python::PushObjCall(ObjID obj_id) { - g_game->PushCall([obj_id] { + g_game->thread()->PushCall([obj_id] { ScopedSetContext cp(g_game->GetUIContext()); g_python->obj(obj_id).Call(); }); } 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()); PythonRef args(Py_BuildValue("(s)", arg.c_str()), ballistica::PythonRef::kSteal); diff --git a/src/ballistica/ui/ui.cc b/src/ballistica/ui/ui.cc index cf2e3223..29c5a1c1 100644 --- a/src/ballistica/ui/ui.cc +++ b/src/ballistica/ui/ui.cc @@ -4,6 +4,7 @@ #include "ballistica/app/app_globals.h" #include "ballistica/audio/audio.h" +#include "ballistica/generic/lambda_runnable.h" #include "ballistica/graphics/component/empty_component.h" #include "ballistica/input/device/input_device.h" #include "ballistica/input/input.h" diff --git a/src/ballistica/ui/widget/container_widget.cc b/src/ballistica/ui/widget/container_widget.cc index 9f680d20..2cc0edb6 100644 --- a/src/ballistica/ui/widget/container_widget.cc +++ b/src/ballistica/ui/widget/container_widget.cc @@ -3,6 +3,7 @@ #include "ballistica/ui/widget/container_widget.h" #include "ballistica/audio/audio.h" +#include "ballistica/core/thread.h" #include "ballistica/game/game.h" #include "ballistica/graphics/component/empty_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 // the draw loop, but we can push a call to do it. Object::WeakRef weakref(this); - g_game->PushCall([weakref] { + g_game->thread()->PushCall([weakref] { Widget* w = weakref.get(); 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 // draw loop, but we can set up an event to do it. Object::WeakRef weakref(this); - g_game->PushCall([weakref] { + g_game->thread()->PushCall([weakref] { Widget* w = weakref.get(); if (w) g_ui->DeleteWidget(w); });