Extract `wait_manager` template class into a separate file 09/274709/2
authorAdam Michalski <a.michalski2@partner.samsung.com>
Mon, 2 May 2022 16:11:33 +0000 (18:11 +0200)
committerAdam Michalski <a.michalski2@partner.samsung.com>
Fri, 6 May 2022 13:19:58 +0000 (15:19 +0200)
Change-Id: I57c9e56335a9365bcbdbf6a522a3fd32581a7dee

sessiond/src/globals.hpp [new file with mode: 0644]
sessiond/src/main.cpp
sessiond/src/wait_manager.hpp [new file with mode: 0644]

diff --git a/sessiond/src/globals.hpp b/sessiond/src/globals.hpp
new file mode 100644 (file)
index 0000000..6f72d07
--- /dev/null
@@ -0,0 +1,45 @@
+#pragma once
+
+/* MIT License
+ *
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished
+ * to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE. */
+
+#include <chrono>
+#include <exception>
+#include <stdexcept>
+#include <string>
+
+#include <gio/gio.h>
+
+using namespace std::chrono_literals;
+
+constexpr inline std::string_view bus_name   = "org.tizen.sessiond";
+constexpr inline std::string_view bus_object = "/org/tizen/sessiond";
+constexpr inline std::string_view bus_iface  = "org.tizen.sessiond.subsession.Manager";
+
+std::chrono::seconds timeout = 10s;
+
+[[noreturn]] inline void g_error_throw(GError *err, std::string message) {
+       message += err->message;
+       g_error_free(err);
+       throw std::runtime_error(std::move(message));
+}
+
index 844d95052de79daa28f0e0580754958950b3ef75..d314c91304548b37f3089e43ea219f4413808629 100644 (file)
 
 #include <algorithm>
 #include <iostream>
-#include <memory>
 #include <unordered_map>
-#include <unordered_set>
-#include <vector>
 
 #include <gio/gio.h>
 
+#include "globals.hpp"
+#include "fs_helpers.h"
 #include "tuple_g_variant_helpers.hpp"
 #include "tuple_hash.hpp"
-#include "fs_helpers.h"
-
-constexpr static std::string_view bus_name   = "org.tizen.sessiond";
-constexpr static std::string_view bus_object = "/org/tizen/sessiond";
-constexpr static std::string_view bus_iface  = "org.tizen.sessiond.subsession.Manager";
-
-constexpr static int timeout = 10;
-
-[[noreturn]] void g_error_throw(GError *err, std::string message) {
-       message += err->message;
-       g_error_free(err);
-       throw std::runtime_error(std::move(message));
-}
+#include "wait_manager.hpp"
 
 struct introspection_data {
        introspection_data(std::string_view xml)
@@ -105,174 +92,6 @@ struct main_loop {
        GMainLoop *inner;
 };
 
-template<typename ...Vs>
-struct wait_manager {
-       wait_manager(int session_uid, GDBusConnection *connection, std::string_view completion_signal)
-                       : session_uid(session_uid), connection(connection), completion_signal(completion_signal) {}
-       ~wait_manager()
-       {
-               for (auto &registration : registered)
-                       g_bus_unwatch_name(registration.second);
-
-               // We ought to swallow exceptions in the destructor.
-               // (std::uncaught_exception exists, but there's no need for it IMHO.)
-               try {
-                       for (auto &waiting_for : waiting) {
-                               waiting_for.second.clear();
-                               finalize_if_empty(waiting_for.first);
-                       }
-               } catch (const std::exception &ex) {
-                       std::cerr << "Exception " << ex.what() << "\n" <<
-                               "while finalizing the wait manager\n";
-               }
-
-               for (auto &timeout : timeouts)
-                       g_source_remove(timeout->timeout_id);
-       }
-
-       // Because of the rule of three, we also have to delete these.
-       wait_manager(const wait_manager &) = delete;
-       wait_manager &operator=(const wait_manager &) = delete;
-
-       void on_client_register(const std::string &name)
-       {
-               if (registered.contains(name))
-                       throw std::invalid_argument("Client already registered for waiting");
-               auto match = g_bus_watch_name(G_BUS_TYPE_SYSTEM, name.data(), G_BUS_NAME_WATCHER_FLAGS_NONE,
-                               nullptr, glib_client_disappeared, this, nullptr);
-               registered.emplace(name, std::move(match));
-       }
-
-       void on_start(std::tuple<Vs...> v)
-       {
-               // See the comment in on_timeout.
-               timeouts.erase(std::remove_if(timeouts.begin(), timeouts.end(),
-                                             [](const auto &td) { return !td->active; }),
-                              timeouts.end());
-
-               // See the comment in finalize.
-               std::erase_if(waiting, [](const auto &waits) { return waits.second.empty(); });
-
-               if (waiting.contains(v))
-                       return; // TODO: any error here?
-                               // This shouldn't happen, but it's unclear how to punish this correctly.
-                               // In particular, this will not happen by construction in case of switch_user,
-                               // since v contains the switch_id.
-
-               std::unordered_set<std::string> waits = {};
-               for (const auto &going_to_wait : registered)
-                       waits.emplace(going_to_wait.first);
-               if (waits.empty())
-                       signal(v);
-               else {
-                       auto td = std::make_unique<timeout_data>();
-                       td->self = this;
-                       td->v = v;
-                       td->active = true;
-                       td->timeout_id = g_timeout_add_seconds(timeout, glib_timeout, td.get());
-
-                       waiting.emplace(v, std::move(waits));
-                       timeouts.emplace_back(std::move(td));
-               }
-       }
-
-       void on_client_done(std::tuple<Vs...> v, const std::string &name)
-       {
-               if (!waiting.contains(v))
-                       return; // This could be an error, but then we would have a race condition with the timeout.
-               drop_name(v, waiting[v], name);
-       }
-
-       void on_client_disappeared(const std::string &name)
-       {
-               registered.erase(name);
-               for (auto &waiting_for : waiting) {
-                       drop_name(waiting_for.first, waiting_for.second, name);
-               }
-       }
-
-       void drop_name(std::tuple<Vs...> v, std::unordered_set<std::string> &waits, const std::string &name)
-       {
-               waits.erase(name);
-               finalize_if_empty(v);
-       }
-
-       void on_timeout(std::tuple<Vs...> v)
-       {
-               if (!waiting.contains(v))
-                       return;
-               waiting[v].clear();
-
-               finalize_if_empty(v);
-
-               // One thing that we'd like to do here is removing the timeout from the array.
-               // However doing so is a bit sus, since glib_timeout still has pointer.
-               // What we do instead, is clearing the array up a bit when adding a new timeout in on_start.
-       }
-
-       void finalize_if_empty(std::tuple<Vs...> v)
-       {
-               // TODO: I don't like this, somehow.
-               // Refactor suggestions are more than welcome.
-               if (!waiting.contains(v) && !waiting[v].empty())
-                       return;
-
-               signal(v);
-
-               // Again, we'd like to remove the now empty vector.
-               // However, doing so is again sus, since other functions (for example on_client_disappeared)
-               // might actually iterate on waiting, and this invalidates the vector.
-               // Again, let's clean up in on_start instead.
-       }
-
-       void signal(std::tuple<Vs...> v)
-       {
-               GError *err = nullptr;
-               if (!g_dbus_connection_emit_signal(connection, nullptr, bus_object.data(), bus_iface.data(), completion_signal.data(),
-                               tuple_to_g_variant(std::tuple_cat(std::make_tuple(session_uid), v)), &err))
-                       g_error_throw(err, "Failed to emit a signal: ");
-       }
-
-       static void glib_client_disappeared(GDBusConnection *conn, const gchar *name, gpointer user_data)
-       // As this is called directly by GLib, we need to swallow exceptions here.
-       try {
-               auto self = static_cast<wait_manager *>(user_data);
-               self->on_client_disappeared(std::string(name));
-       } catch (const std::exception &ex) {
-               std::cerr << "Exception " << ex.what() << "\n" <<
-                       "while handling " << name << " disappearing\n";
-       }
-
-       struct timeout_data {
-               guint timeout_id;
-               wait_manager *self;
-               std::tuple<Vs...> v;
-               bool active;
-       };
-       static gboolean glib_timeout(gpointer user_data)
-       // As this is called directly by GLib, we need to swallow exceptions here.
-       try {
-               auto data = static_cast<timeout_data *>(user_data);
-               data->self->on_timeout(data->v);
-               data->active = false;
-               return G_SOURCE_REMOVE;
-       } catch (const std::exception &ex) {
-               std::cerr << "Exception " << ex.what() << "\n" <<
-                       "while timeout\n";
-               return G_SOURCE_REMOVE;
-       }
-
-       int session_uid;
-       GDBusConnection *connection;
-       std::string_view completion_signal;
-
-       std::unordered_map<std::string, guint> registered = {};
-       std::unordered_map<std::tuple<Vs...>, std::unordered_set<std::string>, tuple_hash<std::tuple<Vs...>>> waiting = {};
-
-       // We hold all the timeout_data, because I couldn't get memory management to work otherwise.
-       std::vector<std::unique_ptr<timeout_data>> timeouts = {};
-};
-
 struct sessiond_context {
        sessiond_context() : data(xml), id(bus_name, glib_bus_connected, glib_name_lost, this), loop()
        {
diff --git a/sessiond/src/wait_manager.hpp b/sessiond/src/wait_manager.hpp
new file mode 100644 (file)
index 0000000..80774dd
--- /dev/null
@@ -0,0 +1,207 @@
+#pragma once
+
+/* MIT License
+ *
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished
+ * to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE. */
+
+#include <algorithm>
+#include <exception>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include <gio/gio.h>
+
+#include "globals.hpp"
+#include "tuple_g_variant_helpers.hpp"
+#include "tuple_hash.hpp"
+
+template<typename ...Vs>
+struct wait_manager {
+       wait_manager(int session_uid, GDBusConnection *connection, std::string_view completion_signal)
+                       : session_uid(session_uid), connection(connection), completion_signal(completion_signal) {}
+       ~wait_manager()
+       {
+               for (auto &registration : registered)
+                       g_bus_unwatch_name(registration.second);
+
+               // We ought to swallow exceptions in the destructor.
+               // (std::uncaught_exception exists, but there's no need for it IMHO.)
+               try {
+                       for (auto &waiting_for : waiting) {
+                               waiting_for.second.clear();
+                               finalize_if_empty(waiting_for.first);
+                       }
+               } catch (const std::exception &ex) {
+                       std::cerr << "Exception " << ex.what() << "\n" <<
+                               "while finalizing the wait manager\n";
+               }
+
+               for (auto &timeout : timeouts)
+                       g_source_remove(timeout->timeout_id);
+       }
+
+       // Because of the rule of three, we also have to delete these.
+       wait_manager(const wait_manager &) = delete;
+       wait_manager &operator=(const wait_manager &) = delete;
+
+       void on_client_register(const std::string &name)
+       {
+               if (registered.contains(name))
+                       throw std::invalid_argument("Client already registered for waiting");
+               auto match = g_bus_watch_name(G_BUS_TYPE_SYSTEM, name.data(), G_BUS_NAME_WATCHER_FLAGS_NONE,
+                               nullptr, glib_client_disappeared, this, nullptr);
+               registered.emplace(name, std::move(match));
+       }
+
+       void on_start(std::tuple<Vs...> v)
+       {
+               // See the comment in on_timeout.
+               timeouts.erase(std::remove_if(timeouts.begin(), timeouts.end(),
+                                             [](const auto &td) { return !td->active; }),
+                              timeouts.end());
+
+               // See the comment in finalize.
+               std::erase_if(waiting, [](const auto &waits) { return waits.second.empty(); });
+
+               if (waiting.contains(v))
+                       return; // TODO: any error here?
+                               // This shouldn't happen, but it's unclear how to punish this correctly.
+                               // In particular, this will not happen by construction in case of switch_user,
+                               // since v contains the switch_id.
+
+               std::unordered_set<std::string> waits = {};
+               for (const auto &going_to_wait : registered)
+                       waits.emplace(going_to_wait.first);
+               if (waits.empty())
+                       signal(v);
+               else {
+                       auto td = std::make_unique<timeout_data>();
+                       td->self = this;
+                       td->v = v;
+                       td->active = true;
+                       td->timeout_id = g_timeout_add_seconds(timeout.count(), glib_timeout, td.get());
+
+                       waiting.emplace(v, std::move(waits));
+                       timeouts.emplace_back(std::move(td));
+               }
+       }
+
+       void on_client_done(std::tuple<Vs...> v, const std::string &name)
+       {
+               if (!waiting.contains(v))
+                       return; // This could be an error, but then we would have a race condition with the timeout.
+               drop_name(v, waiting[v], name);
+       }
+
+       void on_client_disappeared(const std::string &name)
+       {
+               registered.erase(name);
+               for (auto &waiting_for : waiting) {
+                       drop_name(waiting_for.first, waiting_for.second, name);
+               }
+       }
+
+       void drop_name(std::tuple<Vs...> v, std::unordered_set<std::string> &waits, const std::string &name)
+       {
+               waits.erase(name);
+               finalize_if_empty(v);
+       }
+
+       void on_timeout(std::tuple<Vs...> v)
+       {
+               if (!waiting.contains(v))
+                       return;
+               waiting[v].clear();
+
+               finalize_if_empty(v);
+
+               // One thing that we'd like to do here is removing the timeout from the array.
+               // However doing so is a bit sus, since glib_timeout still has pointer.
+               // What we do instead, is clearing the array up a bit when adding a new timeout in on_start.
+       }
+
+       void finalize_if_empty(std::tuple<Vs...> v)
+       {
+               // TODO: I don't like this, somehow.
+               // Refactor suggestions are more than welcome.
+               if (!waiting.contains(v) && !waiting[v].empty())
+                       return;
+
+               signal(v);
+
+               // Again, we'd like to remove the now empty vector.
+               // However, doing so is again sus, since other functions (for example on_client_disappeared)
+               // might actually iterate on waiting, and this invalidates the vector.
+               // Again, let's clean up in on_start instead.
+       }
+
+       void signal(std::tuple<Vs...> v)
+       {
+               GError *err = nullptr;
+               if (!g_dbus_connection_emit_signal(connection, nullptr, bus_object.data(), bus_iface.data(), completion_signal.data(),
+                               tuple_to_g_variant(std::tuple_cat(std::make_tuple(session_uid), v)), &err))
+                       g_error_throw(err, "Failed to emit a signal: ");
+       }
+
+       static void glib_client_disappeared(GDBusConnection *conn, const gchar *name, gpointer user_data)
+       // As this is called directly by GLib, we need to swallow exceptions here.
+       try {
+               auto self = static_cast<wait_manager *>(user_data);
+               self->on_client_disappeared(std::string(name));
+       } catch (const std::exception &ex) {
+               std::cerr << "Exception " << ex.what() << "\n" <<
+                       "while handling " << name << " disappearing\n";
+       }
+
+       struct timeout_data {
+               guint timeout_id;
+               wait_manager *self;
+               std::tuple<Vs...> v;
+               bool active;
+       };
+       static gboolean glib_timeout(gpointer user_data)
+       // As this is called directly by GLib, we need to swallow exceptions here.
+       try {
+               auto data = static_cast<timeout_data *>(user_data);
+               data->self->on_timeout(data->v);
+               data->active = false;
+               return G_SOURCE_REMOVE;
+       } catch (const std::exception &ex) {
+               std::cerr << "Exception " << ex.what() << "\n" <<
+                       "while timeout\n";
+               return G_SOURCE_REMOVE;
+       }
+
+       int session_uid;
+       GDBusConnection *connection;
+       std::string_view completion_signal;
+
+       std::unordered_map<std::string, guint> registered = {};
+       std::unordered_map<std::tuple<Vs...>, std::unordered_set<std::string>, tuple_hash<std::tuple<Vs...>>> waiting = {};
+
+       // We hold all the timeout_data, because I couldn't get memory management to work otherwise.
+       std::vector<std::unique_ptr<timeout_data>> timeouts = {};
+};