From 9f0d525ba5f3a0bd9255d9422b618b09e2ca7ebe Mon Sep 17 00:00:00 2001 From: felk Date: Thu, 26 Oct 2023 00:56:30 +0200 Subject: [PATCH] rework event system: avoid overhead for events with no listeners, simplify --- Source/Core/Core/API/Events.h | 46 +-- Source/Core/Scripting/CMakeLists.txt | 2 - .../Scripting/Python/Modules/eventmodule.cpp | 292 ++++++++++-------- .../Scripting/Python/Modules/eventmodule.h | 8 +- .../Scripting/Python/PyScriptingBackend.cpp | 3 +- Source/Core/Scripting/Python/Utils/invoke.h | 16 +- Source/Core/Scripting/Python/coroutine.cpp | 66 ---- Source/Core/Scripting/Python/coroutine.h | 23 -- Source/Core/Scripting/Scripting.vcxproj | 2 - .../Core/Scripting/Scripting.vcxproj.filters | 6 - 10 files changed, 185 insertions(+), 279 deletions(-) delete mode 100644 Source/Core/Scripting/Python/coroutine.cpp delete mode 100644 Source/Core/Scripting/Python/coroutine.h diff --git a/Source/Core/Core/API/Events.h b/Source/Core/Core/API/Events.h index 019cd29c43f9..67e54117169f 100644 --- a/Source/Core/Core/API/Events.h +++ b/Source/Core/Core/API/Events.h @@ -6,6 +6,8 @@ #include #include +#include +#include #include "Common/Assert.h" #include "Core/Core.h" @@ -59,6 +61,9 @@ template struct ListenerID { u64 value; + + bool operator==(const ListenerID& o) const { return value == o.value; } + bool operator<(const ListenerID& o) const { return value < o.value; } }; // an event container manages a single event type @@ -68,7 +73,7 @@ class EventContainer final public: bool HasListeners() { - return !m_listener_pairs.empty() || !m_one_time_listeners.empty(); + return !m_listeners.empty(); } void EmitEvent(T evt) @@ -93,40 +98,24 @@ class EventContainer final // Just to be sure, have some guards against concurrent modifications. std::lock_guard lock{m_listeners_iterate_mutex}; // avoid concurrent modification issues by iterating over a copy - std::vector, Listener>> listener_pairs = m_listener_pairs; - for (auto& listener_pair : listener_pairs) - listener_pair.second(evt); - // avoid concurrent modification issues by performing a swap - // with an fresh empty vector. - std::vector> one_time_listeners; - std::swap(one_time_listeners, m_one_time_listeners); - for (auto& listener : one_time_listeners) + auto view = m_listeners | std::views::values; + for (const auto copy = std::vector>(view.begin(), view.end()); + const Listener& listener : copy) + { listener(evt); + } } ListenerID ListenEvent(Listener listener) { auto id = ListenerID{m_next_listener_id++}; - m_listener_pairs.emplace_back(std::pair, Listener>(id, std::move(listener))); + m_listeners[id] = std::move(listener); return id; } bool UnlistenEvent(ListenerID listener_id) { - for (auto it = m_listener_pairs.begin(); it != m_listener_pairs.end(); ++it) - { - if (it->first.value == listener_id.value) - { - m_listener_pairs.erase(it); - return true; - } - } - return false; - } - - void ListenEventOnce(Listener listener) - { - m_one_time_listeners.emplace_back(std::move(listener)); + return m_listeners.erase(listener_id) > 0; } void TickListeners() @@ -135,8 +124,7 @@ class EventContainer final } private: std::mutex m_listeners_iterate_mutex{}; - std::vector, Listener>> m_listener_pairs{}; - std::vector> m_one_time_listeners{}; + std::map, Listener> m_listeners{}; u64 m_next_listener_id = 0; }; @@ -177,12 +165,6 @@ class GenericEventHub final return GetEventContainer().UnlistenEvent(listener_id); } - template - void ListenEventOnce(Listener listener) - { - GetEventContainer().ListenEventOnce(listener); - } - void TickAllListeners() { std::apply([](auto&&... arg) { (arg.TickListeners(), ...); }, m_event_containers); diff --git a/Source/Core/Scripting/CMakeLists.txt b/Source/Core/Scripting/CMakeLists.txt index fbe9dc740ecb..2b5258c35c56 100644 --- a/Source/Core/Scripting/CMakeLists.txt +++ b/Source/Core/Scripting/CMakeLists.txt @@ -1,8 +1,6 @@ add_library(scripting ScriptingEngine.cpp ScriptingEngine.h - Python/coroutine.cpp - Python/coroutine.h Python/PyScriptingBackend.cpp Python/PyScriptingBackend.h Python/Modules/controllermodule.cpp diff --git a/Source/Core/Scripting/Python/Modules/eventmodule.cpp b/Source/Core/Scripting/Python/Modules/eventmodule.cpp index 9f93b2694b6b..4d49eb14ea73 100644 --- a/Source/Core/Scripting/Python/Modules/eventmodule.cpp +++ b/Source/Core/Scripting/Python/Modules/eventmodule.cpp @@ -14,7 +14,6 @@ #include "Core/Movie.h" #include "Core/System.h" -#include "Scripting/Python/coroutine.h" #include "Scripting/Python/Utils/convert.h" #include "Scripting/Python/Utils/invoke.h" #include "Scripting/Python/Utils/module.h" @@ -27,38 +26,90 @@ namespace PyScripting // If you are looking for where the actual events are defined, // scroll to the bottom of this file. -// We just want one Py::Object and one std::deque per event, -// but unpacking the template parameters seem to require them to be used. -// So let's just wrap them in a custom templated struct without actually using T. +// For an already-started coroutine and its event tuple describing what +// is being awaited, decode that tuple and make sure the coroutine gets +// resumed once the event being awaited is emitted. +void HandleCoroutine(PyObject* module, PyObject* coro, const Py::Object asyncEventTuple) +{ + const char* magic_string; + const char* event_name; + PyObject* args_tuple; + if (!PyArg_ParseTuple(asyncEventTuple.Lend(), "ssO", &magic_string, &event_name, &args_tuple)) + { + ERROR_LOG_FMT(SCRIPTING, "A coroutine was yielded to the emulator that it cannot process. " + "Did you await something that isn't a dolphin event? " + "(error: await-tuple was not (str, str, args))"); + return; + } + if (std::string(magic_string) != "dolphin_async_event_magic_string") + { + ERROR_LOG_FMT(SCRIPTING, "A coroutine was yielded to the emulator that it cannot process. " + "Did you await something that isn't a dolphin event? " + "(error: wrong magic string to identify as dolphin-native event)"); + return; + } + // `args_tuple` is unused: + // right now there are no events that take in arguments. + // If there were, say `await frameadvance(5)` to wait 5 frames, + // those arguments would be passed as a tuple via `args_tuple`. + + auto scheduler_opt = GetCoroutineScheduler(event_name); + if (!scheduler_opt.has_value()) + { + ERROR_LOG_FMT(SCRIPTING, "An unknown event was tried to be awaited: {}", event_name); + return; + } + std::function scheduler = scheduler_opt.value(); + scheduler(module, coro); +} + +void HandleNewCoroutine(PyObject* module, PyObject* coro) +{ + PyObject* asyncEventTuple = Py::CallMethod(coro, "send", Py::Take(Py_None).Leak()); + if (asyncEventTuple != nullptr) + { + HandleCoroutine(module, coro, Py::Wrap(asyncEventTuple)); + } + else if (!PyErr_ExceptionMatches(PyExc_StopIteration)) + { + // coroutines signal completion by raising StopIteration + PyErr_Print(); + } +} + template struct EventState { - Py::Object callback; - std::deque awaiting_coroutines; + std::set> m_active_listener_ids; }; template struct GenericEventModuleState { API::EventHub* event_hub; - std::optional> cleanup_listeners; std::tuple...> event_state; - template - Py::Object& GetCallback() + void Reset() { - return std::get>(event_state).callback; + std::apply( + [&](auto&&... s) { + (([&]() { + for (auto listener_id : s.m_active_listener_ids) + event_hub->UnlistenEvent(listener_id); + s.m_active_listener_ids.clear(); + }()), + ...); + }, + event_state); } - - template - std::deque& GetAwaitingCoroutines() + template + void NoteActiveListenerID(API::ListenerID listener_id) { - return std::get>(event_state).awaiting_coroutines; + std::get>(event_state).m_active_listener_ids.insert(listener_id); } - - void Reset() + template + void ForgetActiveListenerID(API::ListenerID listener_id) { - std::apply([](auto&&... s) { ((s.callback = Py::Null(), s.awaiting_coroutines.clear()), ...); }, - event_state); + std::get>(event_state).m_active_listener_ids.erase(listener_id); } }; using EventModuleState = GenericEventModuleState< @@ -80,23 +131,98 @@ struct PyEvent; template TFunc> struct PyEvent, TFunc> { - static std::function GetListener(PyObject* module) + static PyObject* AddCallback(PyObject* module, PyObject* newCallback) { + if (newCallback == Py_None) + { + PyErr_SetString(PyExc_ValueError, "event callback must not be None"); + return nullptr; + } + if (!PyCallable_Check(newCallback)) + { + PyErr_SetString(PyExc_TypeError, "event callback must be callable"); + return nullptr; + } + EventModuleState* state = Py::GetState(module); PyInterpreterState* interpreter_state = PyThreadState_Get()->interp; - return [=](const TEvent& event) { + Py_INCREF(module); // TODO felk: where DECREF? + Py_INCREF(newCallback); // TODO felk: where DECREF? + + auto listener = [=](const TEvent& event) { // TODO felk: Creating a new thread state for each event is unnecessary overhead. - // Since all events happen inside the CPU thread anyway, it would be safe to create it once and then reuse it + // Since all events of the same type happen inside the same thread anyway, it would be safe to create it once and then reuse it // (using PyEval_RestoreThread and PyEval_SaveThread). We can't use the thread state from outside the lambda - // (PyThreadState_Get()), because the listeners (may) get registered from the UI thread at startup, + // (PyThreadState_Get()), because the listeners (may) get registered from a different thread, // and a python thread state is only valid in the OS thread it was created in. PyThreadState* thread_state = PyThreadState_New(interpreter_state); PyEval_RestoreThread(thread_state); - Listener(module, event); + + const std::tuple args = TFunc(event); + PyObject* result = + std::apply([&](auto&&... arg) { return Py::CallFunction(newCallback, arg...); }, args); + if (result == nullptr) + { + PyErr_Print(); + } + else + { + if (PyCoro_CheckExact(result)) + HandleNewCoroutine(module, result); + // TODO felk: else? + } + + DecrefPyObjectsInArgs(args); + PyThreadState_Clear(thread_state); PyThreadState_DeleteCurrent(); }; + auto listener_id = state->event_hub->ListenEvent(listener); + state->NoteActiveListenerID(listener_id); + // TODO felk: handle in python somehow, currently impossible to unsubscribe. + // TODO felk: documentation is currently wrong: it says only one can be registered (wrong) and you may register "None" to unregister (wrong) + // TODO felk: where state->ForgetActiveListenerID(listener_id)? + return Py_BuildValue("i", listener_id.value); } + static void ScheduleCoroutine(PyObject* module, PyObject* coro) + { + PyInterpreterState* interpreter_state = PyThreadState_Get()->interp; + EventModuleState* state = Py::GetState(module); + + Py_INCREF(module); + Py_INCREF(coro); + auto listener_id = std::make_shared>(); + auto listener = [=](const TEvent& event) mutable { + // TODO felk: Creating a new thread state for each event is unnecessary overhead. + // Since all events of the same type happen inside the same thread anyway, it would be safe to + // create it once and then reuse it (using PyEval_RestoreThread and PyEval_SaveThread). We + // can't use the thread state from outside the lambda (PyThreadState_Get()), because the + // listeners (may) get registered from a different thread, and a python thread state is only + // valid in the OS thread it was created in. + PyThreadState* thread_state = PyThreadState_New(interpreter_state); + PyEval_RestoreThread(thread_state); + + const std::tuple args = TFunc(event); + PyObject* args_tuple = Py::BuildValueTuple(args); + PyObject* newAsyncEventTuple = Py::CallMethod(coro, "send", args_tuple); + if (newAsyncEventTuple != nullptr) + HandleCoroutine(module, coro, Py::Wrap(newAsyncEventTuple)); + else if (!PyErr_ExceptionMatches(PyExc_StopIteration)) + // coroutines signal completion by raising StopIteration + PyErr_Print(); + DecrefPyObjectsInArgs(args); + Py_DECREF(args_tuple); + Py_DECREF(coro); + Py_DECREF(module); + + PyThreadState_Clear(thread_state); + PyThreadState_DeleteCurrent(); + state->ForgetActiveListenerID(*listener_id); + state->event_hub->UnlistenEvent(*listener_id); + }; + *listener_id = state->event_hub->ListenEvent(listener); + state->NoteActiveListenerID(*listener_id); + } static void DecrefPyObjectsInArgs(const std::tuple args) { std::apply( [&](auto&&... arg) { @@ -113,73 +239,6 @@ struct PyEvent, TFunc> }, args); } - - static void Listener(PyObject* module, const TEvent& event) - { - EventModuleState* state = Py::GetState(module); - // TODO felk: unregister the event instead of checking for possibly awaiting coroutines here to not have the event's overhead even though there isn't actually anyone listening - NotifyAwaitingCoroutines(module, event); - // TODO felk: unregister the event instead of checking for a callback here to not have the event's overhead even though there isn't actually anyone listening - if (state->GetCallback().IsNull()) - return; - const std::tuple args = TFunc(event); - PyObject* result = - std::apply([&](auto&&... arg) { return Py::CallFunction(state->GetCallback(), arg...); }, args); - if (result == nullptr) - { - PyErr_Print(); - return; - } - if (PyCoro_CheckExact(result)) - HandleNewCoroutine(module, Py::Wrap(result)); - DecrefPyObjectsInArgs(args); - } - static PyObject* SetCallback(PyObject* module, PyObject* newCallback) - { - EventModuleState* state = Py::GetState(module); - if (newCallback == Py_None) - { - state->GetCallback() = Py::Null(); - Py_RETURN_NONE; - } - if (!PyCallable_Check(newCallback)) - { - PyErr_SetString(PyExc_TypeError, "event callback must be callable"); - return nullptr; - } - state->GetCallback() = Py::Take(newCallback); - Py_RETURN_NONE; - } - static void ScheduleCoroutine(PyObject* module, const Py::Object coro) - { - EventModuleState* state = Py::GetState(module); - state->GetAwaitingCoroutines().emplace_back(coro); - } - static void NotifyAwaitingCoroutines(PyObject* module, const TEvent& event) - { - EventModuleState* state = Py::GetState(module); - std::deque awaiting_coroutines; - std::swap(state->GetAwaitingCoroutines(), awaiting_coroutines); - while (!awaiting_coroutines.empty()) - { - const Py::Object coro = awaiting_coroutines.front(); - awaiting_coroutines.pop_front(); - const std::tuple args = TFunc(event); - Py::Object args_tuple = Py::Wrap(Py::BuildValueTuple(args)); - PyObject* newAsyncEventTuple = Py::CallMethod(coro, "send", args_tuple.Lend()); - if (newAsyncEventTuple != nullptr) - HandleCoroutine(module, coro, Py::Wrap(newAsyncEventTuple)); - else if (!PyErr_ExceptionMatches(PyExc_StopIteration)) - // coroutines signal completion by raising StopIteration - PyErr_Print(); - DecrefPyObjectsInArgs(args); - } - } - static void Clear(EventModuleState* state) - { - state->GetCallback() = Py::Null(); - state->GetAwaitingCoroutines().clear(); - } }; template @@ -187,36 +246,6 @@ struct PyEventFromMappingFunc : PyEvent { }; -template -struct PythonEventContainer -{ -public: - static void RegisterListeners(PyObject* module) - { - Py_INCREF(module); - EventModuleState* state = Py::GetState(module); - const auto listener_ids = std::apply( - [&](auto&&... pyevent) { - return std::make_tuple(state->event_hub->ListenEvent(pyevent.GetListener(module))...); - }, - s_pyevents); - state->cleanup_listeners.emplace([=]() { - std::apply( - [&](const auto&... listener_id) { (state->event_hub->UnlistenEvent(listener_id), ...); }, - listener_ids); - Py_DECREF(module); - }); - } - static void UnregisterListeners(EventModuleState* state) - { - state->cleanup_listeners.value()(); - state->cleanup_listeners.reset(); - std::apply([&](const auto&... pyevent) { (pyevent.Clear(state), ...); }, s_pyevents); - } -private: - static const std::tuple s_pyevents; -}; - /********************************* * actual events defined below * *********************************/ @@ -250,15 +279,6 @@ using PyMemoryBreakpointEvent = PyEventFromMappingFunc; using PyCodeBreakpointEvent = PyEventFromMappingFunc; using PyFrameDrawnEvent = PyEventFromMappingFunc; -// HOOKING UP PY EVENTS TO DOLPHIN EVENTS -// For all python events listed here, listens to the respective API::Events event -// deduced from the PyEvent signature's input argument. -using EventContainer = - PythonEventContainer; -template <> -const std::tuple - EventContainer::s_pyevents = {}; - std::optional GetCoroutineScheduler(std::string aeventname) { static std::map lookup = { @@ -272,7 +292,7 @@ std::optional GetCoroutineScheduler(std::string aeventname) }; auto iter = lookup.find(aeventname); if (iter == lookup.end()) - return std::optional{}; + return std::nullopt; else return iter->second; } @@ -306,9 +326,7 @@ async def framedrawn(): } API::EventHub* event_hub = PyScripting::PyScriptingBackend::GetCurrent()->GetEventHub(); state->event_hub = event_hub; - const std::function cleanup = [state] { EventContainer::UnregisterListeners(state); }; - PyScripting::PyScriptingBackend::GetCurrent()->AddCleanupFunc(cleanup); - EventContainer::RegisterListeners(module); + PyScripting::PyScriptingBackend::GetCurrent()->AddCleanupFunc([state] { state->Reset(); }); } static PyObject* Reset(PyObject* module) @@ -333,10 +351,10 @@ PyMODINIT_FUNC PyInit_event() static PyMethodDef methods[] = { // EVENT CALLBACKS // Has "on_"-prefix, let's python code register a callback - Py::MakeMethodDef("on_frameadvance"), - Py::MakeMethodDef("on_memorybreakpoint"), - Py::MakeMethodDef("on_codebreakpoint"), - Py::MakeMethodDef("on_framedrawn"), + Py::MakeMethodDef("on_frameadvance"), + Py::MakeMethodDef("on_memorybreakpoint"), + Py::MakeMethodDef("on_codebreakpoint"), + Py::MakeMethodDef("on_framedrawn"), Py::MakeMethodDef("_dolphin_reset"), Py::MakeMethodDef("system_reset"), diff --git a/Source/Core/Scripting/Python/Modules/eventmodule.h b/Source/Core/Scripting/Python/Modules/eventmodule.h index 549efa7bb704..5a32732ed632 100644 --- a/Source/Core/Scripting/Python/Modules/eventmodule.h +++ b/Source/Core/Scripting/Python/Modules/eventmodule.h @@ -13,9 +13,15 @@ namespace PyScripting { +// Handle a not-yet-started coroutine that was returned by normal +// script execution (top-level await) or an async callback. +// Those need to get started by initially calling "send" with None +// and then hand them over to HandleCoroutine. +void HandleNewCoroutine(PyObject* module, PyObject* coro); + PyMODINIT_FUNC PyInit_event(); -using CoroutineScheduler = void(*)(PyObject*, const Py::Object); +using CoroutineScheduler = void(*)(PyObject*, PyObject*); std::optional GetCoroutineScheduler(std::string aeventname); } diff --git a/Source/Core/Scripting/Python/PyScriptingBackend.cpp b/Source/Core/Scripting/Python/PyScriptingBackend.cpp index 9369d42a781b..62fb175a1a27 100644 --- a/Source/Core/Scripting/Python/PyScriptingBackend.cpp +++ b/Source/Core/Scripting/Python/PyScriptingBackend.cpp @@ -11,7 +11,6 @@ #include "Common/Logging/Log.h" #include "Common/StringUtil.h" -#include "Scripting/Python/coroutine.h" #include "Scripting/Python/Modules/controllermodule.h" #include "Scripting/Python/Modules/doliomodule.h" #include "Scripting/Python/Modules/dolphinmodule.h" @@ -111,7 +110,7 @@ static void Init(std::filesystem::path script_filepath) PyErr_Print(); return; } - HandleNewCoroutine(event_module, Py::Wrap(execution_result)); + HandleNewCoroutine(event_module, execution_result); } } diff --git a/Source/Core/Scripting/Python/Utils/invoke.h b/Source/Core/Scripting/Python/Utils/invoke.h index da9fcc650ed0..fcc4ef5dafe5 100644 --- a/Source/Core/Scripting/Python/Utils/invoke.h +++ b/Source/Core/Scripting/Python/Utils/invoke.h @@ -16,25 +16,25 @@ namespace Py { template -inline PyObject* CallFunction(const Py::Object& callable_object, Ts... ts) +inline PyObject* CallFunction(PyObject* callable_object, Ts... ts) { if constexpr (sizeof...(Ts) == 0) { - return PyObject_CallFunction(callable_object.Lend(), nullptr); + return PyObject_CallFunction(callable_object, nullptr); } else if constexpr (sizeof...(Ts) == 1) { // Avoid the special behaviour for singular elements, // see Py_BuildValue's documentation for details. auto arg = Py::ToPyCompatibleValue(std::get<0>(std::make_tuple(ts...))); - return PyObject_CallFunction(callable_object.Lend(), + return PyObject_CallFunction(callable_object, ("(" + Py::fmts + ")").c_str(), arg); } else { return std::apply( [&](auto&&... arg) { - return PyObject_CallFunction(callable_object.Lend(), + return PyObject_CallFunction(callable_object, Py::fmts...>.c_str(), arg...); }, @@ -43,25 +43,25 @@ inline PyObject* CallFunction(const Py::Object& callable_object, Ts... ts) } template -inline PyObject* CallMethod(const Py::Object& callable_object, const char* name, Ts... ts) +inline PyObject* CallMethod(PyObject* callable_object, const char* name, Ts... ts) { if constexpr (sizeof...(Ts) == 0) { - return PyObject_CallMethod(callable_object.Lend(), name, nullptr); + return PyObject_CallMethod(callable_object, name, nullptr); } else if constexpr (sizeof...(Ts) == 1) { // Avoid the special behaviour for singular elements, // see Py_BuildValue's documentation for details. auto arg = Py::ToPyCompatibleValue(std::get<0>(std::make_tuple(ts...))); - return PyObject_CallMethod(callable_object.Lend(), name, + return PyObject_CallMethod(callable_object, name, ("(" + Py::fmts + ")").c_str(), arg); } else { return std::apply( [&](auto&&... arg) { - return PyObject_CallMethod(callable_object.Lend(), name, + return PyObject_CallMethod(callable_object, name, Py::fmts...>.c_str(), arg...); }, diff --git a/Source/Core/Scripting/Python/coroutine.cpp b/Source/Core/Scripting/Python/coroutine.cpp deleted file mode 100644 index 5da5f74e3096..000000000000 --- a/Source/Core/Scripting/Python/coroutine.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2018 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include "Scripting/Python/coroutine.h" - -#include - -#include "Common/Logging/Log.h" -#include "Core/API/Events.h" - -#include "Scripting/Python/Modules/eventmodule.h" -#include "Scripting/Python/PyScriptingBackend.h" -#include "Scripting/Python/Utils/invoke.h" - -namespace PyScripting -{ - -void HandleNewCoroutine(PyObject* module, const Py::Object coro) { - PyObject *asyncEventTuple = Py::CallMethod(coro, "send", Py::Take(Py_None).Leak()); - if (asyncEventTuple != nullptr) - { - HandleCoroutine(module, coro, Py::Wrap(asyncEventTuple)); - } - else if (!PyErr_ExceptionMatches(PyExc_StopIteration)) - { - // coroutines signal completion by raising StopIteration - PyErr_Print(); - } -} - -void HandleCoroutine(PyObject* module, const Py::Object coro, const Py::Object asyncEventTuple) -{ - const char* magic_string; - const char* event_name; - PyObject* args_tuple; - if (!PyArg_ParseTuple(asyncEventTuple.Lend(), "ssO", &magic_string, &event_name, &args_tuple)) - { - ERROR_LOG_FMT(SCRIPTING, "A coroutine was yielded to the emulator that it cannot process. " - "Did you await something that isn't a dolphin event? " - "(error: await-tuple was not (str, str, args))"); - return; - } - if (std::string(magic_string) != "dolphin_async_event_magic_string") - { - ERROR_LOG_FMT(SCRIPTING, "A coroutine was yielded to the emulator that it cannot process. " - "Did you await something that isn't a dolphin event? " - "(error: wrong magic string to identify as dolphin-native event)"); - return; - } - // `args_tuple` is unused: - // right now there are no events that take in arguments. - // If there were, say `await frameadvance(5)` to wait 5 frames, - // those arguments would be passed as a tuple via `args_tuple`. - - auto scheduler_opt = GetCoroutineScheduler(event_name); - if (!scheduler_opt.has_value()) - { - ERROR_LOG_FMT(SCRIPTING, "An unknown event was tried to be awaited: {}", event_name); - return; - } - std::function scheduler = scheduler_opt.value(); - scheduler(module, coro); -} - -} // namespace PyScripting diff --git a/Source/Core/Scripting/Python/coroutine.h b/Source/Core/Scripting/Python/coroutine.h deleted file mode 100644 index 4b37db799718..000000000000 --- a/Source/Core/Scripting/Python/coroutine.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2018 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include "Scripting/Python/Utils/object_wrapper.h" - -namespace PyScripting -{ - -// Handle a not-yet-started coroutine that was returned by normal -// script execution (top-level await) or an async callback. -// Those need to get started by initally calling "send" with None -// and then hand them over to HandleCoroutine. -void HandleNewCoroutine(PyObject* module, const Py::Object obj); - -// For an already-started coroutine and its event tuple describing what -// is being awaited, decode that tuple and make sure the coroutine gets -// resumed once the event being awaited is emitted. -void HandleCoroutine(PyObject* module, const Py::Object coro, Py::Object asyncEventTuple); - -} // namespace PyScripting diff --git a/Source/Core/Scripting/Scripting.vcxproj b/Source/Core/Scripting/Scripting.vcxproj index 9351dc971e73..daaa55913bc6 100644 --- a/Source/Core/Scripting/Scripting.vcxproj +++ b/Source/Core/Scripting/Scripting.vcxproj @@ -25,7 +25,6 @@ - @@ -39,7 +38,6 @@ - diff --git a/Source/Core/Scripting/Scripting.vcxproj.filters b/Source/Core/Scripting/Scripting.vcxproj.filters index 87bc5958d84e..9dcf48f2624b 100644 --- a/Source/Core/Scripting/Scripting.vcxproj.filters +++ b/Source/Core/Scripting/Scripting.vcxproj.filters @@ -17,9 +17,6 @@ Python\Modules - - Python - Python\Utils @@ -38,9 +35,6 @@ - - Python - Python\Modules