Skip to content
Snippets Groups Projects
Commit 562e1269 authored by Nicolas Pope's avatar Nicolas Pope
Browse files

Safe callback code

parent 8b50faa0
No related branches found
No related tags found
No related merge requests found
Pipeline #27274 passed
#ifndef _FTL_HANDLE_HPP_
#define _FTL_HANDLE_HPP_
#include <ftl/threads.hpp>
#include <functional>
#include <unordered_map>
namespace ftl {
struct Handle;
struct BaseHandler {
virtual void remove(const Handle &)=0;
inline Handle make_handle(BaseHandler*, int);
protected:
MUTEX mutex_;
int id_=0;
};
/**
* A `Handle` is used to manage registered callbacks, allowing them to be
* removed safely whenever the `Handle` instance is destroyed.
*/
struct Handle {
friend struct BaseHandler;
/**
* Cancel the timer job. If currently executing it will block and wait for
* the job to complete.
*/
inline void cancel() { if (handler_) handler_->remove(*this); handler_ = nullptr; }
inline int id() const { return id_; }
Handle() : handler_(nullptr), id_(0) {}
Handle(const Handle &)=delete;
Handle &operator=(const Handle &)=delete;
inline Handle(Handle &&h) : handler_(nullptr) {
if (handler_) handler_->remove(*this);
handler_ = h.handler_;
h.handler_ = nullptr;
id_ = h.id_;
}
inline Handle &operator=(Handle &&h) {
if (handler_) handler_->remove(*this);
handler_ = h.handler_;
h.handler_ = nullptr;
id_ = h.id_;
return *this;
}
inline ~Handle() { if (handler_) handler_->remove(*this); }
private:
BaseHandler *handler_;
int id_;
Handle(BaseHandler *h, int id) : handler_(h), id_(id) {}
};
template <typename ...ARGS>
struct Handler : BaseHandler {
Handle on(const std::function<bool(ARGS...)> &f) {
std::unique_lock<std::mutex> lk(mutex_);
int id = id_++;
callbacks_[id] = f;
return make_handle(this, id);
}
void trigger(ARGS ...args) {
std::unique_lock<std::mutex> lk(mutex_);
try {
for (auto &f : callbacks_) {
f.second(std::forward<ARGS...>(args...));
}
} catch (const std::exception &e) {
LOG(ERROR) << "Exception in callback: " << e.what();
}
}
void remove(const Handle &h) override {
std::unique_lock<std::mutex> lk(mutex_);
callbacks_.erase(h.id());
}
private:
std::unordered_map<int, std::function<bool(ARGS...)>> callbacks_;
};
}
ftl::Handle ftl::BaseHandler::make_handle(BaseHandler *h, int id) {
return ftl::Handle(h, id);
}
#endif
\ No newline at end of file
......@@ -28,6 +28,15 @@ target_include_directories(timer_unit PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../inc
target_link_libraries(timer_unit ftlcommon
Threads::Threads ${OS_LIBS})
### Handle Unit ################################################################
add_executable(handle_unit
$<TARGET_OBJECTS:CatchTest>
./handle_unit.cpp
)
target_include_directories(handle_unit PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include")
target_link_libraries(handle_unit ftlcommon
Threads::Threads ${OS_LIBS})
### URI ########################################################################
add_executable(msgpack_unit
$<TARGET_OBJECTS:CatchTest>
......
#include "catch.hpp"
#define LOGURU_REPLACE_GLOG 1
#include <loguru.hpp>
#include <ftl/handle.hpp>
using ftl::Handler;
using ftl::Handle;
TEST_CASE( "Handle release on cancel" ) {
Handler<int> handler;
int calls = 0;
auto h = handler.on([&calls](int i) {
calls += i;
return true;
});
handler.trigger(5);
REQUIRE(calls == 5);
h.cancel();
handler.trigger(5);
REQUIRE(calls == 5);
}
TEST_CASE( "Handle multiple triggers" ) {
Handler<int> handler;
int calls = 0;
auto h = handler.on([&calls](int i) {
calls += i;
return true;
});
handler.trigger(5);
REQUIRE(calls == 5);
handler.trigger(5);
REQUIRE(calls == 10);
}
TEST_CASE( "Handle release on destruct" ) {
Handler<int> handler;
int calls = 0;
{
auto h = handler.on([&calls](int i) {
calls += i;
return true;
});
handler.trigger(5);
REQUIRE(calls == 5);
}
handler.trigger(5);
REQUIRE(calls == 5);
}
TEST_CASE( "Handle moving" ) {
SECTION("old handle cannot cancel") {
Handler<int> handler;
int calls = 0;
auto h = handler.on([&calls](int i) {
calls += i;
return true;
});
handler.trigger(5);
REQUIRE(calls == 5);
auto h2 = std::move(h);
h.cancel();
handler.trigger(5);
REQUIRE(calls == 10);
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment