From bfd9f681f7749734a3c0f6b0879fa78f7662a11e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?kleines=20Filmr=C3=B6llchen?= Date: Thu, 29 Dec 2022 14:53:05 +0100 Subject: LibCore+Userland: Allow canceling promises To make EventLoop cancel its managed Promises, we need the ability to cancel them in the first place. --- Userland/Applications/Browser/CookieJar.cpp | 2 +- Userland/Applications/Mail/MailWidget.cpp | 16 +++++------ Userland/Libraries/LibCore/Promise.h | 33 +++++++++++++++------- .../Libraries/LibFileSystemAccessClient/Client.cpp | 2 +- Userland/Services/WebDriver/Session.cpp | 2 +- Userland/Utilities/test-imap.cpp | 28 +++++++++--------- 6 files changed, 48 insertions(+), 35 deletions(-) (limited to 'Userland') diff --git a/Userland/Applications/Browser/CookieJar.cpp b/Userland/Applications/Browser/CookieJar.cpp index 76d987f8f8..cb07e08566 100644 --- a/Userland/Applications/Browser/CookieJar.cpp +++ b/Userland/Applications/Browser/CookieJar.cpp @@ -577,7 +577,7 @@ void CookieJar::select_all_cookies_from_database(OnSelectAllCookiesResult on_res promise->resolve({}); }); - promise->await(); + MUST(promise->await()); } void CookieJar::purge_expired_cookies() diff --git a/Userland/Applications/Mail/MailWidget.cpp b/Userland/Applications/Mail/MailWidget.cpp index 5d9c8487b5..da4140dbda 100644 --- a/Userland/Applications/Mail/MailWidget.cpp +++ b/Userland/Applications/Mail/MailWidget.cpp @@ -132,9 +132,9 @@ bool MailWidget::connect_and_login() auto connection_promise = m_imap_client->connection_promise(); VERIFY(!connection_promise.is_null()); - connection_promise->await(); + MUST(connection_promise->await()); - auto response = m_imap_client->login(username, password)->await().release_value(); + auto response = MUST(m_imap_client->login(username, password)->await()).release_value(); if (response.status() != IMAP::ResponseStatus::OK) { dbgln("Failed to login. The server says: '{}'", response.response_text()); @@ -142,7 +142,7 @@ bool MailWidget::connect_and_login() return false; } - response = m_imap_client->list(""sv, "*"sv)->await().release_value(); + response = MUST(m_imap_client->list(""sv, "*"sv)->await()).release_value(); if (response.status() != IMAP::ResponseStatus::OK) { dbgln("Failed to retrieve mailboxes. The server says: '{}'", response.response_text()); @@ -163,7 +163,7 @@ bool MailWidget::connect_and_login() void MailWidget::on_window_close() { - auto response = move(m_imap_client->send_simple_command(IMAP::CommandType::Logout)->await().release_value().get()); + auto response = move(MUST(m_imap_client->send_simple_command(IMAP::CommandType::Logout)->await()).release_value().get()); VERIFY(response.status() == IMAP::ResponseStatus::OK); m_imap_client->close(); @@ -253,7 +253,7 @@ void MailWidget::selected_mailbox() if (mailbox.flags & (unsigned)IMAP::MailboxFlag::NoSelect) return; - auto response = m_imap_client->select(mailbox.name)->await().release_value(); + auto response = MUST(m_imap_client->select(mailbox.name)->await()).release_value(); if (response.status() != IMAP::ResponseStatus::OK) { dbgln("Failed to select mailbox. The server says: '{}'", response.response_text()); @@ -280,7 +280,7 @@ void MailWidget::selected_mailbox() }, }; - auto fetch_response = m_imap_client->fetch(fetch_command, false)->await().release_value(); + auto fetch_response = MUST(m_imap_client->fetch(fetch_command, false)->await()).release_value(); if (response.status() != IMAP::ResponseStatus::OK) { dbgln("Failed to retrieve subject/from for e-mails. The server says: '{}'", response.response_text()); @@ -396,7 +396,7 @@ void MailWidget::selected_email_to_load() }, }; - auto fetch_response = m_imap_client->fetch(fetch_command, false)->await().release_value(); + auto fetch_response = MUST(m_imap_client->fetch(fetch_command, false)->await()).release_value(); if (fetch_response.status() != IMAP::ResponseStatus::OK) { dbgln("Failed to retrieve the body structure of the selected e-mail. The server says: '{}'", fetch_response.response_text()); @@ -457,7 +457,7 @@ void MailWidget::selected_email_to_load() }, }; - fetch_response = m_imap_client->fetch(fetch_command, false)->await().release_value(); + fetch_response = MUST(m_imap_client->fetch(fetch_command, false)->await()).release_value(); if (fetch_response.status() != IMAP::ResponseStatus::OK) { dbgln("Failed to retrieve the body of the selected e-mail. The server says: '{}'", fetch_response.response_text()); diff --git a/Userland/Libraries/LibCore/Promise.h b/Userland/Libraries/LibCore/Promise.h index e38458fbd8..90dc01eeba 100644 --- a/Userland/Libraries/LibCore/Promise.h +++ b/Userland/Libraries/LibCore/Promise.h @@ -10,6 +10,7 @@ #include namespace Core { + template class Promise : public Object { C_OBJECT(Promise); @@ -19,22 +20,33 @@ public: void resolve(Result&& result) { - m_pending = move(result); + m_pending_or_error = move(result); + if (on_resolved) - on_resolved(m_pending.value()); + on_resolved(m_pending_or_error.value()); + } + + void cancel(Error error) + { + m_pending_or_error = move(error); } - bool is_resolved() + bool is_canceled() { - return m_pending.has_value(); - }; + return m_pending_or_error.has_value() && m_pending_or_error->is_error(); + } - Result await() + bool is_resolved() const { - while (!is_resolved()) { + return m_pending_or_error.has_value() && !m_pending_or_error->is_error(); + } + + ErrorOr await() + { + while (!m_pending_or_error.has_value()) Core::EventLoop::current().pump(); - } - return m_pending.release_value(); + + return m_pending_or_error.release_value(); } // Converts a Promise to a Promise using a function func: A -> B @@ -52,6 +64,7 @@ public: private: Promise() = default; - Optional m_pending; + Optional> m_pending_or_error; }; + } diff --git a/Userland/Libraries/LibFileSystemAccessClient/Client.cpp b/Userland/Libraries/LibFileSystemAccessClient/Client.cpp index 9c3dcdaa40..ded74eef9d 100644 --- a/Userland/Libraries/LibFileSystemAccessClient/Client.cpp +++ b/Userland/Libraries/LibFileSystemAccessClient/Client.cpp @@ -170,7 +170,7 @@ int Client::get_new_id() Result Client::handle_promise(int id) { - auto result = m_promises.get(id)->promise->await(); + auto result = TRY(m_promises.get(id)->promise->await()); m_promises.remove(id); return result; } diff --git a/Userland/Services/WebDriver/Session.cpp b/Userland/Services/WebDriver/Session.cpp index 0c6fd87434..d3427711bf 100644 --- a/Userland/Services/WebDriver/Session.cpp +++ b/Userland/Services/WebDriver/Session.cpp @@ -80,7 +80,7 @@ ErrorOr Session::start(LaunchBrowserCallbacks const& callbacks) // FIXME: Allow this to be more asynchronous. For now, this at least allows us to propagate // errors received while accepting the Browser and WebContent sockets. - TRY(promise->await()); + TRY(TRY(promise->await())); m_started = true; return {}; diff --git a/Userland/Utilities/test-imap.cpp b/Userland/Utilities/test-imap.cpp index 90378d604f..de55539271 100644 --- a/Userland/Utilities/test-imap.cpp +++ b/Userland/Utilities/test-imap.cpp @@ -44,20 +44,20 @@ ErrorOr serenity_main(Main::Arguments arguments) Core::EventLoop loop; auto client = TRY(tls ? IMAP::Client::connect_tls(host, port) : IMAP::Client::connect_plaintext(host, port)); - client->connection_promise()->await(); + TRY(client->connection_promise()->await()); - auto response = client->login(username, password.view())->await().release_value(); + auto response = TRY(client->login(username, password.view())->await()).release_value(); outln("[LOGIN] Login response: {}", response.response_text()); - response = move(client->send_simple_command(IMAP::CommandType::Capability)->await().value().get()); + response = move(TRY(client->send_simple_command(IMAP::CommandType::Capability)->await()).value().get()); outln("[CAPABILITY] First capability: {}", response.data().capabilities().first()); bool idle_supported = !response.data().capabilities().find_if([](auto capability) { return capability.equals_ignoring_ascii_case("IDLE"sv); }).is_end(); - response = client->list(""sv, "*"sv)->await().release_value(); + response = TRY(client->list(""sv, "*"sv)->await()).release_value(); outln("[LIST] First mailbox: {}", response.data().list_items().first().name); auto mailbox = "Inbox"sv; - response = client->select(mailbox)->await().release_value(); + response = TRY(client->select(mailbox)->await()).release_value(); outln("[SELECT] Select response: {}", response.response_text()); auto message = Message { @@ -71,7 +71,7 @@ ErrorOr serenity_main(Main::Arguments arguments) "So, \"Hello\"." }; auto promise = client->append("INBOX"sv, move(message)); - response = promise->await().release_value(); + response = TRY(promise->await()).release_value(); outln("[APPEND] Response: {}", response.response_text()); Vector keys; @@ -79,13 +79,13 @@ ErrorOr serenity_main(Main::Arguments arguments) IMAP::SearchKey::From { "jdoe@machine.example" } }); keys.append(IMAP::SearchKey { IMAP::SearchKey::Subject { "Saying Hello" } }); - response = client->search({}, move(keys), false)->await().release_value(); + response = TRY(client->search({}, move(keys), false)->await()).release_value(); Vector search_results = move(response.data().search_results()); auto added_message = search_results.first(); outln("[SEARCH] Number of results: {}", search_results.size()); - response = client->status("INBOX"sv, { IMAP::StatusItemType::Recent, IMAP::StatusItemType::Messages })->await().release_value(); + response = TRY(client->status("INBOX"sv, { IMAP::StatusItemType::Recent, IMAP::StatusItemType::Messages })->await()).release_value(); outln("[STATUS] Recent items: {}", response.data().status_item().get(IMAP::StatusItemType::Recent)); for (auto item : search_results) { @@ -118,7 +118,7 @@ ErrorOr serenity_main(Main::Arguments arguments) }; // clang-format on - auto fetch_response = client->fetch(fetch_command, false)->await().release_value(); + auto fetch_response = TRY(client->fetch(fetch_command, false)->await()).release_value(); outln("[FETCH] Subject of search result: {}", fetch_response.data() .fetch_data() @@ -136,22 +136,22 @@ ErrorOr serenity_main(Main::Arguments arguments) // FIXME: There is a discrepancy between IMAP::Sequence wanting signed ints // and IMAP search results returning unsigned ones. Find which one is // more correct and fix this. - response = client->store(IMAP::StoreMethod::Add, { static_cast(added_message), static_cast(added_message) }, false, { "\\Deleted" }, false)->await().release_value(); + response = TRY(client->store(IMAP::StoreMethod::Add, { static_cast(added_message), static_cast(added_message) }, false, { "\\Deleted" }, false)->await()).release_value(); outln("[STORE] Store response: {}", response.response_text()); - response = move(client->send_simple_command(IMAP::CommandType::Expunge)->await().release_value().get()); + response = move(TRY(client->send_simple_command(IMAP::CommandType::Expunge)->await()).release_value().get()); outln("[EXPUNGE] Number of expunged entries: {}", response.data().expunged().size()); if (idle_supported) { - VERIFY(client->idle()->await().has_value()); + VERIFY(TRY(client->idle()->await()).has_value()); sleep(3); - response = client->finish_idle()->await().release_value(); + response = TRY(client->finish_idle()->await()).release_value(); outln("[IDLE] Idle response: {}", response.response_text()); } else { outln("[IDLE] Skipped. No IDLE support."); } - response = move(client->send_simple_command(IMAP::CommandType::Logout)->await().release_value().get()); + response = move(TRY(client->send_simple_command(IMAP::CommandType::Logout)->await()).release_value().get()); outln("[LOGOUT] Bye: {}", response.data().bye_message().value()); client->close(); -- cgit v1.2.3