diff --git a/loader/include/Geode/utils/Promise.hpp b/loader/include/Geode/utils/Promise.hpp index fef10a8f..5d6b6ba8 100644 --- a/loader/include/Geode/utils/Promise.hpp +++ b/loader/include/Geode/utils/Promise.hpp @@ -11,7 +11,7 @@ namespace geode { std::optional percentage; DefaultProgress() = default; - DefaultProgress(auto msg) : message(msg) {} + DefaultProgress(std::string const& msg) : message(msg) {} DefaultProgress(auto msg, uint8_t percentage) : message(msg), percentage(percentage) {} }; } @@ -24,8 +24,8 @@ namespace geode { template class Promise final { public: - using Value = T; - using Error = E; + using Value = T; + using Error = E; using Progress = P; using OnResolved = utils::MiniFunction; @@ -46,12 +46,12 @@ namespace geode { Promise() : m_data(std::make_shared()) {} Promise(utils::MiniFunction source, bool threaded = true) - : Promise([source](auto resolve, auto reject, auto, auto) { + : Promise([source](auto resolve, auto reject, auto, auto const&) { source(resolve, reject); - }) {} + }, threaded) {} - Promise(utils::MiniFunction source, bool threaded = true) - : Promise([source](auto onStateChanged) { + Promise(utils::MiniFunction source, bool threaded = true) + : Promise([source](auto onStateChanged, auto const& cancelled) { source( [onStateChanged](auto&& value) { onStateChanged(State(std::in_place_index, value)); @@ -62,13 +62,12 @@ namespace geode { [onStateChanged](auto&& progress) { onStateChanged(State(std::in_place_index, progress)); }, - [onStateChanged]() { - onStateChanged(State(std::in_place_index, CancelledState())); - } + cancelled ); - }) {} + }, threaded, std::monostate()) {} - Promise(utils::MiniFunction source, bool threaded = true) : m_data(std::make_shared()) { + Promise(utils::MiniFunction source, bool threaded, std::monostate tag) : m_data(std::make_shared()) { + m_data->shouldStartThreaded = threaded; if (threaded) { std::thread([source = std::move(source), data = m_data]() mutable { Promise::invoke_source(std::move(source), data); @@ -88,7 +87,10 @@ namespace geode { template requires (!std::is_void_v) Promise then(utils::MiniFunction&& callback) { - if (m_data->cancelled) return make_cancelled(); + if (m_data->cancelled) { + return make_cancelled(); + } + std::unique_lock _(m_data->mutex); // Check if this Promise has already been resolved, and if so @@ -103,47 +105,16 @@ namespace geode { return make_cancelled(); } - return Promise([data = m_data, callback](auto fwdStateToNextPromise) { - data->callback = [fwdStateToNextPromise, callback](auto&& state) { - // Can't use std::visit if Value and Error are the same >:( - switch (state.index()) { - case STATE_VALUE_INDEX: { - auto mapped = callback(std::get(state)); - fwdStateToNextPromise(Promise::State( - std::in_place_index, - std::move(mapped) - )); - } break; - - case STATE_ERROR_INDEX: { - fwdStateToNextPromise(Promise::State( - std::in_place_index, - std::move(std::get(state)) - )); - } break; - - case STATE_PROGRESS_INDEX: { - fwdStateToNextPromise(Promise::State( - std::in_place_index, - std::move(std::get(state)) - )); - } break; - - case STATE_CANCELLED_INDEX: { - fwdStateToNextPromise(Promise::State( - std::in_place_index, - std::move(std::get(state)) - )); - } break; - } - }; - }); + return make_fwd(callback, m_data); } template requires (!std::is_void_v) Promise then(utils::MiniFunction(Result)>&& callback) { - if (m_data->cancelled) return make_cancelled(); + if (m_data->cancelled) { + return make_cancelled(); + } + std::unique_lock _(m_data->mutex); // Check if this Promise has already been resolved, and if so @@ -163,8 +134,8 @@ namespace geode { return make_cancelled(); } - return Promise([data = m_data, callback](auto fwdStateToNextPromise) { - data->callback = [fwdStateToNextPromise, callback](auto&& state) { + return Promise([data = m_data, callback](auto fwdStateToNextPromise, auto const& nextPromiseCancelled) { + data->callback = [&nextPromiseCancelled, fwdStateToNextPromise, callback](auto&& state) { // Can't use std::visit if Value and Error are the same >:( switch (state.index()) { case STATE_VALUE_INDEX: { @@ -214,7 +185,7 @@ namespace geode { } break; } }; - }); + }, m_data->shouldStartThreaded, std::monostate()); } Promise expect(utils::MiniFunction&& callback) { @@ -226,7 +197,10 @@ namespace geode { template requires (!std::is_void_v) Promise expect(utils::MiniFunction&& callback) { - if (m_data->cancelled) return make_cancelled(); + if (m_data->cancelled) { + return make_cancelled(); + } + std::unique_lock _(m_data->mutex); // Check if this Promise has already been resolved, and if so @@ -241,41 +215,7 @@ namespace geode { return make_cancelled(); } - return Promise([data = m_data, callback](auto fwdStateToNextPromise) { - data->callback = [fwdStateToNextPromise, callback](auto&& state) { - // Can't use std::visit if Value and Error are the same >:( - switch (state.index()) { - case STATE_VALUE_INDEX: { - fwdStateToNextPromise(Promise::State( - std::in_place_index, - std::move(std::get(state)) - )); - } break; - - case STATE_ERROR_INDEX: { - auto mapped = callback(std::get(state)); - fwdStateToNextPromise(Promise::State( - std::in_place_index, - std::move(mapped) - )); - } break; - - case STATE_PROGRESS_INDEX: { - fwdStateToNextPromise(Promise::State( - std::in_place_index, - std::move(std::get(state)) - )); - } break; - - case STATE_CANCELLED_INDEX: { - fwdStateToNextPromise(Promise::State( - std::in_place_index, - std::move(std::get(state)) - )); - } break; - } - }; - }); + return make_fwd(callback, m_data); } Promise progress(utils::MiniFunction&& callback) { @@ -287,7 +227,10 @@ namespace geode { template requires (!std::is_void_v) Promise progress(utils::MiniFunction&& callback) { - if (m_data->cancelled) return make_cancelled(); + if (m_data->cancelled) { + return make_cancelled(); + } + std::unique_lock _(m_data->mutex); // Check if this Promise has already been resolved @@ -295,21 +238,14 @@ namespace geode { return make_cancelled(); } - return Promise([data = m_data, callback](auto fwdStateToNextPromise) { - data->callback = [fwdStateToNextPromise, callback](auto&& state) { - if (state.index() == STATE_PROGRESS_INDEX) { - auto mapped = callback(std::get(state)); - fwdStateToNextPromise(Promise::State(std::in_place_index, mapped)); - } - else { - fwdStateToNextPromise(state); - } - }; - }); + return make_fwd(callback, m_data); } Promise finally(utils::MiniFunction&& callback) { - if (m_data->cancelled) return make_cancelled(); + if (m_data->cancelled) { + return make_cancelled(); + } + std::unique_lock _(m_data->mutex); // Check if this Promise has already been resolved, and if so @@ -380,6 +316,7 @@ namespace geode { OnStateChange callback; std::optional> result; std::atomic_bool cancelled; + bool shouldStartThreaded; }; std::shared_ptr m_data; @@ -390,12 +327,59 @@ namespace geode { return std::move(ret); } + template + static Promise make_fwd(auto mapper, std::shared_ptr data) { + return Promise([data, mapper](auto fwdStateToNextPromise, auto const&) { + data->callback = [fwdStateToNextPromise, mapper](auto&& state) { + if (state.index() == Ix) { + auto mapped = mapper(std::get(state)); + fwdStateToNextPromise(Promise::State( + std::in_place_index, + mapped + )); + } + // Can't use std::visit if Value and Error are the same >:( + else switch (state.index()) { + case STATE_VALUE_INDEX: if constexpr (Ix != STATE_VALUE_INDEX) { + fwdStateToNextPromise(Promise::State( + std::in_place_index, + std::move(std::get(state)) + )); + } break; + + case STATE_ERROR_INDEX: if constexpr (Ix != STATE_ERROR_INDEX) { + fwdStateToNextPromise(Promise::State( + std::in_place_index, + std::move(std::get(state)) + )); + } break; + + case STATE_PROGRESS_INDEX: if constexpr (Ix != STATE_PROGRESS_INDEX) { + fwdStateToNextPromise(Promise::State( + std::in_place_index, + std::move(std::get(state)) + )); + } break; + + case STATE_CANCELLED_INDEX: if constexpr (Ix != STATE_CANCELLED_INDEX) { + fwdStateToNextPromise(Promise::State( + std::in_place_index, + std::move(std::get(state)) + )); + } break; + } + }; + }, data->shouldStartThreaded, std::monostate()); + } + static void invoke_callback(State&& state, std::shared_ptr data) { if (data->cancelled) return; std::unique_lock _(data->mutex); if (data->callback) { - data->callback(State(state)); + Loader::get()->queueInMainThread([callback = data->callback, state = State(state)]() { + callback(state); + }); } // Store the state to let future installed callbacks be immediately resolved @@ -410,10 +394,13 @@ namespace geode { } } - static void invoke_source(utils::MiniFunction&& source, std::shared_ptr data) { - source([data](auto&& state) { - invoke_callback(std::move(state), data); - }); + static void invoke_source(utils::MiniFunction&& source, std::shared_ptr data) { + source( + [data](auto&& state) { + invoke_callback(std::move(state), data); + }, + data->cancelled + ); } }; @@ -472,11 +459,12 @@ namespace geode { // the same IDs again, so technically if some promise takes // literally forever then this could cause issues later on static size_t ID_COUNTER = 0; + ID_COUNTER += 1; // Reserve 0 for PromiseEventFilter not listening to anything if (ID_COUNTER == 0) { ID_COUNTER += 1; } - size_t id = ++ID_COUNTER; + size_t id = ID_COUNTER; this ->then([id](auto&& value) { PromiseEvent(id, std::variant { std::in_place_index<0>, std::forward(value) }).post(); diff --git a/loader/src/server/Server.hpp b/loader/src/server/Server.hpp index 0a0ad5a0..92fa9e95 100644 --- a/loader/src/server/Server.hpp +++ b/loader/src/server/Server.hpp @@ -150,7 +150,7 @@ namespace server { template As* find(Query const& query) { - auto it = std::find_if(m_values.begin(), m_values.end(), [](auto const& q) { + auto it = std::find_if(m_values.begin(), m_values.end(), [query](auto const& q) { return q.first == query; }); if (it == m_values.end()) { @@ -226,7 +226,11 @@ namespace server { Cache m_cache; ServerPromise fetch(Query const& query) { - return m_cache.pend(Query(query)); + return F(Query(query)) + .then([this, query = std::move(query)](auto res) { + m_cache.add(Query(query), Result(res)); + }); + // return m_cache.pend(Query(query)); } public: diff --git a/loader/src/ui/GeodeUI.cpp b/loader/src/ui/GeodeUI.cpp index 3b471d4e..5bd5b890 100644 --- a/loader/src/ui/GeodeUI.cpp +++ b/loader/src/ui/GeodeUI.cpp @@ -93,7 +93,8 @@ protected: this->setSprite(CCSprite::create("loadingCircle.png")); static_cast(m_sprite)->setBlendFunc({ GL_ONE, GL_ONE }); m_sprite->runAction(CCRepeatForever::create(CCRotateBy::create(1.f, 360.f))); - m_listener.setFilter(server::ServerResultCache<&server::getModLogo>::shared().get(id).listen()); + m_listener.setFilter(server::getModLogo(id).listen()); + // m_listener.setFilter(server::ServerResultCache<&server::getModLogo>::shared().get(id).listen()); } return true; diff --git a/loader/src/ui/mods/ModListSource.cpp b/loader/src/ui/mods/ModListSource.cpp index b7810ee4..f304ce6f 100644 --- a/loader/src/ui/mods/ModListSource.cpp +++ b/loader/src/ui/mods/ModListSource.cpp @@ -110,7 +110,7 @@ typename ModListSource::PagePromise ModListSource::loadPage(size_t page, bool up // Return a generic "Coming soon" message if there's no provider set if (!m_provider.get) { return PagePromise([this, page](auto, auto reject) { - reject("Coming soon! ;)"); + reject(LoadPageError("Coming soon! ;)")); }); } if (!update && m_cachedPages.contains(page)) { diff --git a/loader/src/ui/mods/ModListSource.hpp b/loader/src/ui/mods/ModListSource.hpp index 96deca71..9b0bdd3c 100644 --- a/loader/src/ui/mods/ModListSource.hpp +++ b/loader/src/ui/mods/ModListSource.hpp @@ -24,7 +24,7 @@ public: std::optional details; LoadPageError() = default; - LoadPageError(auto msg) : message(msg) {} + LoadPageError(std::string const& msg) : message(msg) {} LoadPageError(auto msg, auto details) : message(msg), details(details) {} }; diff --git a/loader/src/utils/web2.cpp b/loader/src/utils/web2.cpp index 1f2912d4..ee969b08 100644 --- a/loader/src/utils/web2.cpp +++ b/loader/src/utils/web2.cpp @@ -115,7 +115,7 @@ std::string urlParamEncode(std::string_view const input) { WebPromise WebRequest::send(std::string_view method, std::string_view url) { m_impl->m_method = method; m_impl->m_url = url; - return WebPromise([impl = m_impl](auto resolve, auto reject, auto progress, auto cancelled) { + return WebPromise([impl = m_impl](auto resolve, auto reject, auto progress, auto const& cancelled) { // Init Curl auto curl = curl_easy_init(); if (!curl) { @@ -130,11 +130,13 @@ WebPromise WebRequest::send(std::string_view method, std::string_view url) { struct ResponseData { WebResponse response; Impl* impl; - WebPromise::Progress progress; + WebPromise::OnProgress progress; + std::atomic_bool const& cancelled; } responseData = { .response = WebResponse(), .impl = impl.get(), .progress = progress, + .cancelled = cancelled, }; // Store downloaded response data into a byte vector