Loading plugins/media/checkout_pool.h +26 −4 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #pragma once #include <concepts> #include <functional> #include <memory> #include <mutex> Loading Loading @@ -106,13 +107,34 @@ public: private: friend class Handle; // A call site that forgets to Handle::discard() an object left mid-response // (e.g. a libhttppp::HttpClient stream that wasn't drained to completion, // or one the peer has asked to close) would otherwise hand a desynced // connection to the next caller — neither the pool nor HttpClient itself // catches that on its own (only a genuinely closed socket gets reset). // Detect the same signals HttpClient already exposes (isStreaming(), // wantsClose()) via ad-hoc requires-expressions (a named `concept` can't // be declared at class scope), and refuse to pool an object that reports // either one, so this is safe by construction even if a future call site // skips discard(). Types without these methods (e.g. plain // mediadb::Client) are pooled as before — nothing to detect. static bool _unsafeToPool(const T &obj) { bool unsafe = false; if constexpr (requires { { obj.isStreaming() } -> std::convertible_to<bool>; }) unsafe = unsafe || obj.isStreaming(); if constexpr (requires { { obj.wantsClose() } -> std::convertible_to<bool>; }) unsafe = unsafe || obj.wantsClose(); return unsafe; } void release(std::unique_ptr<T> obj) { std::lock_guard<std::mutex> lk(_mtx); if (_avail.size() < _maxSize) if (_avail.size() < _maxSize && !_unsafeToPool(*obj)) _avail.push_back(std::move(obj)); // else: pool already full (more concurrent checkouts than // maxSize happened) — just let this instance be destroyed instead // of growing the pool unbounded. // else: pool already full (more concurrent checkouts than maxSize // happened), or the object isn't safe to hand to the next caller — // just let this instance be destroyed instead of growing the pool // unbounded or leaking a desynced connection into circulation. } std::mutex _mtx; Loading Loading
plugins/media/checkout_pool.h +26 −4 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #pragma once #include <concepts> #include <functional> #include <memory> #include <mutex> Loading Loading @@ -106,13 +107,34 @@ public: private: friend class Handle; // A call site that forgets to Handle::discard() an object left mid-response // (e.g. a libhttppp::HttpClient stream that wasn't drained to completion, // or one the peer has asked to close) would otherwise hand a desynced // connection to the next caller — neither the pool nor HttpClient itself // catches that on its own (only a genuinely closed socket gets reset). // Detect the same signals HttpClient already exposes (isStreaming(), // wantsClose()) via ad-hoc requires-expressions (a named `concept` can't // be declared at class scope), and refuse to pool an object that reports // either one, so this is safe by construction even if a future call site // skips discard(). Types without these methods (e.g. plain // mediadb::Client) are pooled as before — nothing to detect. static bool _unsafeToPool(const T &obj) { bool unsafe = false; if constexpr (requires { { obj.isStreaming() } -> std::convertible_to<bool>; }) unsafe = unsafe || obj.isStreaming(); if constexpr (requires { { obj.wantsClose() } -> std::convertible_to<bool>; }) unsafe = unsafe || obj.wantsClose(); return unsafe; } void release(std::unique_ptr<T> obj) { std::lock_guard<std::mutex> lk(_mtx); if (_avail.size() < _maxSize) if (_avail.size() < _maxSize && !_unsafeToPool(*obj)) _avail.push_back(std::move(obj)); // else: pool already full (more concurrent checkouts than // maxSize happened) — just let this instance be destroyed instead // of growing the pool unbounded. // else: pool already full (more concurrent checkouts than maxSize // happened), or the object isn't safe to hand to the next caller — // just let this instance be destroyed instead of growing the pool // unbounded or leaking a desynced connection into circulation. } std::mutex _mtx; Loading