Browse Source

LibCore+Userland: Allow canceling promises

To make EventLoop cancel its managed Promises, we need the ability to
cancel them in the first place.
kleines Filmröllchen 2 years ago
parent
commit
bfd9f681f7

+ 1 - 1
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()

+ 8 - 8
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<IMAP::SolidResponse>());
+    auto response = move(MUST(m_imap_client->send_simple_command(IMAP::CommandType::Logout)->await()).release_value().get<IMAP::SolidResponse>());
     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());

+ 23 - 10
Userland/Libraries/LibCore/Promise.h

@@ -10,6 +10,7 @@
 #include <LibCore/Object.h>
 
 namespace Core {
+
 template<typename Result>
 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<Result> 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<A> to a Promise<B> using a function func: A -> B
@@ -52,6 +64,7 @@ public:
 private:
     Promise() = default;
 
-    Optional<Result> m_pending;
+    Optional<ErrorOr<Result>> m_pending_or_error;
 };
+
 }

+ 1 - 1
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;
 }

+ 1 - 1
Userland/Services/WebDriver/Session.cpp

@@ -80,7 +80,7 @@ ErrorOr<void> 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 {};

+ 14 - 14
Userland/Utilities/test-imap.cpp

@@ -44,20 +44,20 @@ ErrorOr<int> 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<IMAP::SolidResponse>());
+    response = move(TRY(client->send_simple_command(IMAP::CommandType::Capability)->await()).value().get<IMAP::SolidResponse>());
     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<int> 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<IMAP::SearchKey> keys;
@@ -79,13 +79,13 @@ ErrorOr<int> 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<unsigned> 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<int> 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<int> 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<int>(added_message), static_cast<int>(added_message) }, false, { "\\Deleted" }, false)->await().release_value();
+    response = TRY(client->store(IMAP::StoreMethod::Add, { static_cast<int>(added_message), static_cast<int>(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<IMAP::SolidResponse>());
+    response = move(TRY(client->send_simple_command(IMAP::CommandType::Expunge)->await()).release_value().get<IMAP::SolidResponse>());
     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<IMAP::SolidResponse>());
+    response = move(TRY(client->send_simple_command(IMAP::CommandType::Logout)->await()).release_value().get<IMAP::SolidResponse>());
     outln("[LOGOUT] Bye: {}", response.data().bye_message().value());
 
     client->close();