Skip to content

Commit

Permalink
HookSystem: improve callback safety
Browse files Browse the repository at this point in the history
  • Loading branch information
vaxerski committed Apr 20, 2024
1 parent 1055e6b commit 4ad739e
Show file tree
Hide file tree
Showing 18 changed files with 89 additions and 105 deletions.
2 changes: 1 addition & 1 deletion src/debug/HyprNotificationOverlay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include <pango/pangocairo.h>

CHyprNotificationOverlay::CHyprNotificationOverlay() {
g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) {
static auto P = g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) {
if (m_dNotifications.size() == 0)
return;

Expand Down
8 changes: 4 additions & 4 deletions src/desktop/Workspace.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ class CWorkspace {
void markInert();

private:
void init(PHLWORKSPACE self);
void init(PHLWORKSPACE self);

HOOK_CALLBACK_FN* m_pFocusedWindowHook = nullptr;
bool m_bInert = true;
std::weak_ptr<CWorkspace> m_pSelf;
std::shared_ptr<HOOK_CALLBACK_FN> m_pFocusedWindowHook;
bool m_bInert = true;
std::weak_ptr<CWorkspace> m_pSelf;
};

inline bool valid(const PHLWORKSPACE& ref) {
Expand Down
6 changes: 3 additions & 3 deletions src/helpers/WLClasses.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,9 @@ struct STabletPad {
};

struct SIdleInhibitor {
wlr_idle_inhibitor_v1* pWlrInhibitor = nullptr;
CWindow* pWindow = nullptr;
HOOK_CALLBACK_FN* onWindowDestroy = nullptr;
wlr_idle_inhibitor_v1* pWlrInhibitor = nullptr;
CWindow* pWindow = nullptr;
std::shared_ptr<HOOK_CALLBACK_FN> onWindowDestroy;

DYNLISTENER(Destroy);

Expand Down
4 changes: 2 additions & 2 deletions src/hyprerror/HyprError.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ CHyprError::CHyprError() {
m_fFadeOpacity.create(AVARTYPE_FLOAT, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), AVARDAMAGE_NONE);
m_fFadeOpacity.registerVar();

g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) {
static auto P = g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) {
if (!m_bIsCreated)
return;

g_pHyprRenderer->damageMonitor(g_pCompositor->m_pLastMonitor);
m_bMonitorChanged = true;
});

g_pHookSystem->hookDynamic("preRender", [&](void* self, SCallbackInfo& info, std::any param) {
static auto P2 = g_pHookSystem->hookDynamic("preRender", [&](void* self, SCallbackInfo& info, std::any param) {
if (!m_bIsCreated)
return;

Expand Down
2 changes: 1 addition & 1 deletion src/managers/CursorManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ CCursorManager::CCursorManager() {

updateTheme();

g_pHookSystem->hookDynamic("monitorLayoutChanged", [this](void* self, SCallbackInfo& info, std::any param) { this->updateTheme(); });
static auto P = g_pHookSystem->hookDynamic("monitorLayoutChanged", [this](void* self, SCallbackInfo& info, std::any param) { this->updateTheme(); });
}

void CCursorManager::dropBufferRef(CCursorManager::CCursorBuffer* ref) {
Expand Down
54 changes: 29 additions & 25 deletions src/managers/HookSystemManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,40 @@ CHookSystemManager::CHookSystemManager() {
}

// returns the pointer to the function
HOOK_CALLBACK_FN* CHookSystemManager::hookDynamic(const std::string& event, HOOK_CALLBACK_FN fn, HANDLE handle) {
const auto PVEC = getVecForEvent(event);
const auto PFN = &m_lCallbackFunctions.emplace_back(fn);
PVEC->emplace_back(SCallbackFNPtr{PFN, handle});
return PFN;
std::shared_ptr<HOOK_CALLBACK_FN> CHookSystemManager::hookDynamic(const std::string& event, HOOK_CALLBACK_FN fn, HANDLE handle) {
std::shared_ptr<HOOK_CALLBACK_FN> hookFN = std::make_shared<HOOK_CALLBACK_FN>(fn);
m_mRegisteredHooks[event].emplace_back(SCallbackFNPtr{.fn = hookFN, .handle = handle});
return hookFN;
}

void CHookSystemManager::hookStatic(const std::string& event, HOOK_CALLBACK_FN* fn, HANDLE handle) {
const auto PVEC = getVecForEvent(event);
PVEC->emplace_back(SCallbackFNPtr{fn, handle});
}
void CHookSystemManager::unhook(std::shared_ptr<HOOK_CALLBACK_FN> fn) {
for (auto& [k, v] : m_mRegisteredHooks) {
std::erase_if(v, [&](const auto& other) {
std::shared_ptr<HOOK_CALLBACK_FN> fn_ = other.fn.lock();

void CHookSystemManager::unhook(HOOK_CALLBACK_FN* fn) {
std::erase_if(m_lCallbackFunctions, [&](const auto& other) { return &other == fn; });
for (auto& [k, v] : m_lpRegisteredHooks) {
std::erase_if(v, [&](const auto& other) { return other.fn == fn; });
return fn_.get() == fn.get();
});
}
}

void CHookSystemManager::emit(const std::vector<SCallbackFNPtr>* callbacks, SCallbackInfo& info, std::any data) {
void CHookSystemManager::emit(std::vector<SCallbackFNPtr>* const callbacks, SCallbackInfo& info, std::any data) {
if (callbacks->empty())
return;

std::vector<HANDLE> faultyHandles;
bool needsDeadCleanup = false;

for (auto& cb : *callbacks) {

m_bCurrentEventPlugin = false;

if (!cb.handle) {
// we don't guard hl hooks
(*cb.fn)(cb.fn, info, data);

if (std::shared_ptr<HOOK_CALLBACK_FN> fn = cb.fn.lock())
(*fn)(fn.get(), info, data);
else
needsDeadCleanup = true;
continue;
}

Expand All @@ -48,9 +50,12 @@ void CHookSystemManager::emit(const std::vector<SCallbackFNPtr>* callbacks, SCal
continue;

try {
if (!setjmp(m_jbHookFaultJumpBuf))
(*cb.fn)(cb.fn, info, data);
else {
if (!setjmp(m_jbHookFaultJumpBuf)) {
if (std::shared_ptr<HOOK_CALLBACK_FN> fn = cb.fn.lock())
(*fn)(fn.get(), info, data);
else
needsDeadCleanup = true;
} else {
// this module crashed.
throw std::exception();
}
Expand All @@ -61,19 +66,18 @@ void CHookSystemManager::emit(const std::vector<SCallbackFNPtr>* callbacks, SCal
}
}

if (needsDeadCleanup)
std::erase_if(*callbacks, [](const auto& fn) { return !fn.fn.lock(); });

if (!faultyHandles.empty()) {
for (auto& h : faultyHandles)
g_pPluginSystem->unloadPlugin(g_pPluginSystem->getPluginByHandle(h), true);
}
}

std::vector<SCallbackFNPtr>* CHookSystemManager::getVecForEvent(const std::string& event) {
auto IT = std::find_if(m_lpRegisteredHooks.begin(), m_lpRegisteredHooks.end(), [&](const auto& other) { return other.first == event; });

if (IT != m_lpRegisteredHooks.end())
return &IT->second;

Debug::log(LOG, "[hookSystem] New hook event registered: {}", event);
if (!m_mRegisteredHooks.contains(event))
Debug::log(LOG, "[hookSystem] New hook event registered: {}", event);

return &m_lpRegisteredHooks.emplace_back(std::make_pair<>(event, std::vector<SCallbackFNPtr>{})).second;
return &m_mRegisteredHooks[event];
}
20 changes: 10 additions & 10 deletions src/managers/HookSystemManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
typedef std::function<void(void*, SCallbackInfo& info, std::any data)> HOOK_CALLBACK_FN;

struct SCallbackFNPtr {
HOOK_CALLBACK_FN* fn = nullptr;
HANDLE handle = nullptr;
std::weak_ptr<HOOK_CALLBACK_FN> fn;
HANDLE handle = nullptr;
};

#define EMIT_HOOK_EVENT(name, param) \
Expand All @@ -40,21 +40,21 @@ class CHookSystemManager {
public:
CHookSystemManager();

// returns the pointer to the function
HOOK_CALLBACK_FN* hookDynamic(const std::string& event, HOOK_CALLBACK_FN fn, HANDLE handle = nullptr);
void hookStatic(const std::string& event, HOOK_CALLBACK_FN* fn, HANDLE handle = nullptr);
void unhook(HOOK_CALLBACK_FN* fn);
// returns the pointer to the function.
// losing this pointer (letting it get destroyed)
// will equal to unregistering the callback.
[[nodiscard("Losing this pointer instantly unregisters the callback")]] std::shared_ptr<HOOK_CALLBACK_FN> hookDynamic(const std::string& event, HOOK_CALLBACK_FN fn,
HANDLE handle = nullptr);
void unhook(std::shared_ptr<HOOK_CALLBACK_FN> fn);

void emit(const std::vector<SCallbackFNPtr>* callbacks, SCallbackInfo& info, std::any data = 0);
void emit(std::vector<SCallbackFNPtr>* const callbacks, SCallbackInfo& info, std::any data = 0);
std::vector<SCallbackFNPtr>* getVecForEvent(const std::string& event);

bool m_bCurrentEventPlugin = false;
jmp_buf m_jbHookFaultJumpBuf;

private:
// todo: this is slow. Maybe static ptrs should be somehow allowed. unique ptr for vec?
std::list<std::pair<std::string, std::vector<SCallbackFNPtr>>> m_lpRegisteredHooks;
std::list<HOOK_CALLBACK_FN> m_lCallbackFunctions;
std::unordered_map<std::string, std::vector<SCallbackFNPtr>> m_mRegisteredHooks;
};

inline std::unique_ptr<CHookSystemManager> g_pHookSystem;
2 changes: 1 addition & 1 deletion src/managers/KeybindManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ CKeybindManager::CKeybindManager() {

m_tScrollTimer.reset();

g_pHookSystem->hookDynamic("configReloaded", [this](void* hk, SCallbackInfo& info, std::any param) {
static auto P = g_pHookSystem->hookDynamic("configReloaded", [this](void* hk, SCallbackInfo& info, std::any param) {
// clear cuz realloc'd
m_pActiveKeybind = nullptr;
m_vPressedSpecialBinds.clear();
Expand Down
2 changes: 1 addition & 1 deletion src/managers/input/InputMethodRelay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include "../../Compositor.hpp"

CInputMethodRelay::CInputMethodRelay() {
g_pHookSystem->hookDynamic("keyboardFocus", [&](void* self, SCallbackInfo& info, std::any param) { onKeyboardFocus(std::any_cast<wlr_surface*>(param)); });
static auto P = g_pHookSystem->hookDynamic("keyboardFocus", [&](void* self, SCallbackInfo& info, std::any param) { onKeyboardFocus(std::any_cast<wlr_surface*>(param)); });
}

void CInputMethodRelay::onNewIME(wlr_input_method_v2* pIME) {
Expand Down
18 changes: 3 additions & 15 deletions src/plugins/PluginAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,18 @@ APICALL const char* __hyprland_api_get_hash() {
return GIT_COMMIT_HASH;
}

APICALL bool HyprlandAPI::registerCallbackStatic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN* fn) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);

if (!PLUGIN)
return false;

g_pHookSystem->hookStatic(event, fn, handle);
PLUGIN->registeredCallbacks.emplace_back(std::make_pair<>(event, fn));

return true;
}

APICALL HOOK_CALLBACK_FN* HyprlandAPI::registerCallbackDynamic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN fn) {
APICALL std::shared_ptr<HOOK_CALLBACK_FN> HyprlandAPI::registerCallbackDynamic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN fn) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);

if (!PLUGIN)
return nullptr;

auto* const PFN = g_pHookSystem->hookDynamic(event, fn, handle);
auto PFN = g_pHookSystem->hookDynamic(event, fn, handle);
PLUGIN->registeredCallbacks.emplace_back(std::make_pair<>(event, PFN));
return PFN;
}

APICALL bool HyprlandAPI::unregisterCallback(HANDLE handle, HOOK_CALLBACK_FN* fn) {
APICALL bool HyprlandAPI::unregisterCallback(HANDLE handle, std::shared_ptr<HOOK_CALLBACK_FN> fn) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);

if (!PLUGIN)
Expand Down
12 changes: 2 additions & 10 deletions src/plugins/PluginAPI.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,28 +132,20 @@ namespace HyprlandAPI {
*/
APICALL Hyprlang::CConfigValue* getConfigValue(HANDLE handle, const std::string& name);

/*
Register a static (pointer) callback to a selected event.
Pointer must be kept valid until unregisterCallback() is called.
returns: true on success, false on fail
*/
APICALL bool registerCallbackStatic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN* fn);

/*
Register a dynamic (function) callback to a selected event.
Pointer will be free'd by Hyprland on unregisterCallback().
returns: a pointer to the newly allocated function. nullptr on fail.
*/
APICALL HOOK_CALLBACK_FN* registerCallbackDynamic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN fn);
APICALL std::shared_ptr<HOOK_CALLBACK_FN> registerCallbackDynamic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN fn);

/*
Unregisters a callback. If the callback was dynamic, frees the memory.
returns: true on success, false on fail
*/
APICALL bool unregisterCallback(HANDLE handle, HOOK_CALLBACK_FN* fn);
APICALL bool unregisterCallback(HANDLE handle, std::shared_ptr<HOOK_CALLBACK_FN> fn);

/*
Calls a hyprctl command.
Expand Down
24 changes: 12 additions & 12 deletions src/plugins/PluginSystem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ class IHyprWindowDecoration;

class CPlugin {
public:
std::string name = "";
std::string description = "";
std::string author = "";
std::string version = "";
std::string name = "";
std::string description = "";
std::string author = "";
std::string version = "";

std::string path = "";
std::string path = "";

bool m_bLoadedWithConfig = false;
bool m_bLoadedWithConfig = false;

HANDLE m_pHandle = nullptr;
HANDLE m_pHandle = nullptr;

std::vector<IHyprLayout*> registeredLayouts;
std::vector<IHyprWindowDecoration*> registeredDecorations;
std::vector<std::pair<std::string, HOOK_CALLBACK_FN*>> registeredCallbacks;
std::vector<std::string> registeredDispatchers;
std::vector<std::shared_ptr<SHyprCtlCommand>> registeredHyprctlCommands;
std::vector<IHyprLayout*> registeredLayouts;
std::vector<IHyprWindowDecoration*> registeredDecorations;
std::vector<std::pair<std::string, std::shared_ptr<HOOK_CALLBACK_FN>>> registeredCallbacks;
std::vector<std::string> registeredDispatchers;
std::vector<std::shared_ptr<SHyprCtlCommand>> registeredHyprctlCommands;
};

class CPluginSystem {
Expand Down
22 changes: 11 additions & 11 deletions src/protocols/Screencopy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,21 @@ class CScreencopyClient {
CScreencopyClient();
~CScreencopyClient();

int ref = 0;
wl_resource* resource = nullptr;
int ref = 0;
wl_resource* resource = nullptr;

eClientOwners clientOwner = CLIENT_SCREENCOPY;
eClientOwners clientOwner = CLIENT_SCREENCOPY;

int frameCounter = 0;
int framesInLastHalfSecond = 0;
CTimer lastMeasure;
CTimer lastFrame;
bool sentScreencast = false;
int frameCounter = 0;
int framesInLastHalfSecond = 0;
CTimer lastMeasure;
CTimer lastFrame;
bool sentScreencast = false;

void onTick();
HOOK_CALLBACK_FN* tickCallback = nullptr;
void onTick();
std::shared_ptr<HOOK_CALLBACK_FN> tickCallback;

bool operator==(const CScreencopyClient& other) const {
bool operator==(const CScreencopyClient& other) const {
return resource == other.resource;
}
};
Expand Down
2 changes: 1 addition & 1 deletion src/protocols/TearingControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include "../Compositor.hpp"

CTearingControlProtocol::CTearingControlProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
g_pHookSystem->hookDynamic("destroyWindow", [this](void* self, SCallbackInfo& info, std::any param) { this->onWindowDestroy(std::any_cast<CWindow*>(param)); });
static auto P = g_pHookSystem->hookDynamic("destroyWindow", [this](void* self, SCallbackInfo& info, std::any param) { this->onWindowDestroy(std::any_cast<CWindow*>(param)); });
}

void CTearingControlProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
Expand Down
6 changes: 3 additions & 3 deletions src/protocols/XDGOutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ void CXDGOutputProtocol::bindManager(wl_client* client, void* data, uint32_t ver
}

CXDGOutputProtocol::CXDGOutputProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
g_pHookSystem->hookDynamic("monitorLayoutChanged", [this](void* self, SCallbackInfo& info, std::any param) { this->updateAllOutputs(); });
g_pHookSystem->hookDynamic("configReloaded", [this](void* self, SCallbackInfo& info, std::any param) { this->updateAllOutputs(); });
g_pHookSystem->hookDynamic("monitorRemoved", [this](void* self, SCallbackInfo& info, std::any param) {
static auto P = g_pHookSystem->hookDynamic("monitorLayoutChanged", [this](void* self, SCallbackInfo& info, std::any param) { this->updateAllOutputs(); });
static auto P2 = g_pHookSystem->hookDynamic("configReloaded", [this](void* self, SCallbackInfo& info, std::any param) { this->updateAllOutputs(); });
static auto P3 = g_pHookSystem->hookDynamic("monitorRemoved", [this](void* self, SCallbackInfo& info, std::any param) {
const auto PMONITOR = std::any_cast<CMonitor*>(param);
for (auto& o : m_vXDGOutputs) {
if (o->monitor == PMONITOR)
Expand Down
2 changes: 1 addition & 1 deletion src/render/OpenGL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ CHyprOpenGLImpl::CHyprOpenGLImpl() {
Debug::log(WARN, "!RENDERER: Using the legacy GLES2 renderer!");
#endif

g_pHookSystem->hookDynamic("preRender", [&](void* self, SCallbackInfo& info, std::any data) { preRender(std::any_cast<CMonitor*>(data)); });
static auto P = g_pHookSystem->hookDynamic("preRender", [&](void* self, SCallbackInfo& info, std::any data) { preRender(std::any_cast<CMonitor*>(data)); });

RASSERT(eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT), "Couldn't unset current EGL!");

Expand Down
Loading

0 comments on commit 4ad739e

Please sign in to comment.