LCOV - code coverage report
Current view: top level - util - close_notifier.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 92.0 % 25 23
Test Date: 2026-11-10 10:38:07 Functions: 100.0 % 12 12

            Line data    Source code
       1              : #ifndef THINGER_UTIL_CLOSE_NOTIFIER_HPP
       2              : #define THINGER_UTIL_CLOSE_NOTIFIER_HPP
       3              : 
       4              : #include <optional>
       5              : #include <tuple>
       6              : #include <utility>
       7              : #include <boost/asio/any_io_executor.hpp>
       8              : #include <boost/asio/post.hpp>
       9              : #include "logger.hpp"
      10              : #include "types.hpp"
      11              : 
      12              : namespace thinger::util {
      13              : 
      14              : // The close callback of a connection (see asio::websocket::on_close(),
      15              : // http::websocket_connection::on_close() and http::sse_connection::on_close()), with what the
      16              : // connection tells it (`Args`): called once, the connection closed, always posted to its
      17              : // executor, so never from within the call that closed it (e.g. a send from a loop over the
      18              : // connections, which may then remove this one), and what it throws is logged. Set before or
      19              : // after the close: set once closed, it is posted at once. Used on the thread of the connection,
      20              : // as the connection itself.
      21              : template<typename... Args>
      22              : class close_notifier {
      23              : public:
      24              :     using callback_type = sync_callback<void(Args...)>;
      25              : 
      26         7549 :     explicit close_notifier(boost::asio::any_io_executor executor) : executor_(std::move(executor)) {}
      27              : 
      28          702 :     void set(callback_type callback) {
      29          702 :         if (released_) return;
      30          702 :         if (closed_) {
      31           27 :             if (callback) post(std::move(callback), *closed_);
      32           27 :             return;
      33              :         }
      34          675 :         callback_ = std::move(callback);
      35              :     }
      36              : 
      37              :     // The connection closed (once: later calls are ignored)
      38         6509 :     void notify(Args... args) {
      39         6509 :         if (closed_ || released_) return;
      40         5030 :         closed_.emplace(std::move(args)...);
      41              :         // released here: it may own the connection
      42         5030 :         auto callback = std::exchange(callback_, nullptr);
      43         5030 :         if (callback) post(std::move(callback), *closed_);
      44         5030 :     }
      45              : 
      46              :     // Drop the callback: the connection will never be told closed (e.g. its socket was
      47              :     // released, or it is destroyed open), so neither any callback set afterwards. Once told
      48              :     // closed, a callback set afterwards is still posted
      49         7546 :     void release() {
      50         7546 :         if (!closed_) released_ = true;
      51         7546 :         callback_ = nullptr;
      52         7546 :     }
      53              : 
      54              : private:
      55          681 :     void post(callback_type callback, std::tuple<Args...> args) {
      56         1303 :         boost::asio::post(executor_, [callback = std::move(callback), args = std::move(args)]() mutable {
      57              :             try {
      58          622 :                 std::apply(callback, std::move(args));
      59           54 :             } catch (const std::exception& e) {
      60           27 :                 LOG_ERROR("error in the close callback of a connection: {}", e.what());
      61            0 :             } catch (...) {
      62            0 :                 LOG_ERROR("unknown error in the close callback of a connection");
      63              :             }
      64              :         });
      65          681 :     }
      66              : 
      67              :     boost::asio::any_io_executor executor_;
      68              :     callback_type callback_;
      69              :     // What the connection told once closed
      70              :     std::optional<std::tuple<Args...>> closed_;
      71              :     bool released_ = false;
      72              : };
      73              : 
      74              : } // namespace thinger::util
      75              : 
      76              : #endif // THINGER_UTIL_CLOSE_NOTIFIER_HPP
        

Generated by: LCOV version 2.0-1