memory: do not release pointers after emitting a signal

A signal called is allowed to free ourselves, in which case we're not allowed to use this anymore. Only perform the housekeeping of removing stale events before emit, and in registerListener.
This commit is contained in:
Vaxry 2024-07-08 23:06:28 +02:00
parent a8c3a13570
commit c342d5ca44

View file

@ -9,14 +9,10 @@ using namespace Hyprutils::Memory;
#define WP CWeakPointer
void Hyprutils::Signal::CSignal::emit(std::any data) {
bool dirty = false;
std::vector<SP<CSignalListener>> listeners;
for (auto& l : m_vListeners) {
if (l.expired()) {
dirty = true;
if (l.expired())
continue;
}
listeners.emplace_back(l.lock());
}
@ -29,10 +25,9 @@ void Hyprutils::Signal::CSignal::emit(std::any data) {
for (auto& l : listeners) {
// if there is only one lock, it means the event is only held by the listeners
// vector and was removed during our iteration
if (l.strongRef() == 1) {
dirty = true;
if (l.strongRef() == 1)
continue;
}
l->emit(data);
}
@ -43,13 +38,17 @@ void Hyprutils::Signal::CSignal::emit(std::any data) {
// release SPs
listeners.clear();
if (dirty)
std::erase_if(m_vListeners, [](const auto& other) { return other.expired(); });
// 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
}
CHyprSignalListener Hyprutils::Signal::CSignal::registerListener(std::function<void(std::any)> handler) {
CHyprSignalListener listener = makeShared<CSignalListener>(handler);
m_vListeners.emplace_back(listener);
// housekeeping: remove any stale listeners
std::erase_if(m_vListeners, [](const auto& other) { return other.expired(); });
return listener;
}