Compare commits

..

3 commits

Author SHA1 Message Date
Tom Englund
51a4f93ce8
signals: check if listeners is empty in emitInternal (#96)
* signals: use tuple reference and check if listeners is empty

use forward_as_tuple as ít creates a temporary tuple reference instead
of copying/moving each argument. also if guard the emitInternal to only
create locals if there is actually any listeners.

* signal: revert forward_as_tuple

cant use references as signals expect a copy/move.
2025-12-20 17:56:12 +00:00
Tom Englund
5ac060bfcf
signal: check for trivially copyable (#95)
use is_trivially_copyable instead because is_arithmetic_v exludes small
POD structs, enums, and certain trivially copyable types.

also add a if guard in emit if we have no listeners, no point emitting.
2025-12-19 16:12:51 +00:00
1c527b30fe
cli/logger: flush stdout after logging 2025-12-17 20:09:42 +00:00
3 changed files with 31 additions and 22 deletions

View file

@ -26,10 +26,13 @@ namespace Hyprutils {
template <typename... Args> template <typename... Args>
class CSignalT : public CSignalBase { class CSignalT : public CSignalBase {
template <typename T> template <typename T>
using RefArg = std::conditional_t<std::is_reference_v<T> || std::is_arithmetic_v<T>, T, const T&>; using RefArg = std::conditional_t<std::is_trivially_copyable_v<T>, T, const T&>;
public: public:
void emit(RefArg<Args>... args) { void emit(RefArg<Args>... args) {
if (m_vListeners.empty() && m_vStaticListeners.empty())
return;
if constexpr (sizeof...(Args) == 0) if constexpr (sizeof...(Args) == 0)
emitInternal(nullptr); emitInternal(nullptr);
else { else {

View file

@ -134,6 +134,7 @@ void CLoggerImpl::log(eLogLevel level, const std::string_view& msg, const std::s
if (m_stdoutEnabled) { if (m_stdoutEnabled) {
try { try {
std::println("{}{}", m_colorEnabled ? logPrefixColor : logPrefix, logMsg); std::println("{}{}", m_colorEnabled ? logPrefixColor : logPrefix, logMsg);
std::fflush(stdout);
} catch (std::exception& e) { } catch (std::exception& e) {
; // this could be e.g. stdout closed ; // this could be e.g. stdout closed
} }

View file

@ -10,18 +10,18 @@ using namespace Hyprutils::Memory;
#define WP CWeakPointer #define WP CWeakPointer
void Hyprutils::Signal::CSignalBase::emitInternal(void* args) { void Hyprutils::Signal::CSignalBase::emitInternal(void* args) {
if (!m_vListeners.empty()) {
std::vector<SP<CSignalListener>> listeners; std::vector<SP<CSignalListener>> listeners;
listeners.reserve(m_vListeners.size()); listeners.reserve(m_vListeners.size());
for (auto& l : m_vListeners) {
for (const auto& l : m_vListeners) {
if (l.expired()) if (l.expired())
continue; continue;
listeners.emplace_back(l.lock()); listeners.emplace_back(l.lock());
} }
auto statics = m_vStaticListeners; for (const auto& l : listeners) {
for (auto& l : listeners) {
// if there is only one lock, it means the event is only held by the listeners // if there is only one lock, it means the event is only held by the listeners
// vector and was removed during our iteration // vector and was removed during our iteration
if (l.strongRef() == 1) if (l.strongRef() == 1)
@ -30,12 +30,17 @@ void Hyprutils::Signal::CSignalBase::emitInternal(void* args) {
l->emitInternal(args); l->emitInternal(args);
} }
for (auto& l : statics) {
l->emitInternal(args);
}
// release SPs // release SPs
listeners.clear(); listeners.clear();
}
if (!m_vStaticListeners.empty()) {
const auto statics = m_vStaticListeners;
for (const auto& l : statics) {
l->emitInternal(args);
}
}
// we cannot release any expired refs here as one of the listeners could've removed this object and // we cannot release any expired refs here as one of the listeners could've removed this object and
// as such we'd be doing a UAF // as such we'd be doing a UAF