diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt
index 86d9086..bf7f61a 100644
--- a/src/logid/CMakeLists.txt
+++ b/src/logid/CMakeLists.txt
@@ -33,7 +33,9 @@ add_executable(logid
backend/hidpp20/features/DeviceName.cpp
backend/hidpp20/features/Reset.cpp
backend/dj/Report.cpp
- util/mutex_queue.h)
+ util/mutex_queue.h
+ util/thread.cpp
+ util/ExceptionHandler.cpp)
set_target_properties(logid PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
diff --git a/src/logid/util/ExceptionHandler.cpp b/src/logid/util/ExceptionHandler.cpp
new file mode 100644
index 0000000..3019806
--- /dev/null
+++ b/src/logid/util/ExceptionHandler.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019-2020 PixlOne
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#include
+#include "../util.h"
+#include "ExceptionHandler.h"
+#include "../backend/hidpp10/Error.h"
+#include "../backend/hidpp20/Error.h"
+
+using namespace logid;
+
+void ExceptionHandler::Default(std::exception& error)
+{
+ try {
+ throw error;
+ } catch(backend::hidpp10::Error& e) {
+ log_printf(WARN, "HID++ 1.0 error ignored on detached thread: %s",
+ error.what());
+ } catch(backend::hidpp20::Error& e) {
+ log_printf(WARN, "HID++ 2.0 error ignored on detached thread: %s",
+ error.what());
+ } catch(std::system_error& e) {
+ log_printf(WARN, "System error ignored on detached thread: %s",
+ error.what());
+ } catch(std::exception& e) {
+ log_printf(WARN, "Error ignored on detached thread: %s",
+ error.what());
+ }
+}
\ No newline at end of file
diff --git a/src/logid/util/ExceptionHandler.h b/src/logid/util/ExceptionHandler.h
new file mode 100644
index 0000000..daf9bea
--- /dev/null
+++ b/src/logid/util/ExceptionHandler.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019-2020 PixlOne
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#ifndef LOGID_EXCEPTIONHANDLER_H
+#define LOGID_EXCEPTIONHANDLER_H
+
+#include
+
+namespace logid {
+namespace ExceptionHandler
+{
+ void Default(std::exception& e);
+}}
+
+#endif //LOGID_EXCEPTIONHANDLER_H
diff --git a/src/logid/util/thread.cpp b/src/logid/util/thread.cpp
new file mode 100644
index 0000000..3460bd7
--- /dev/null
+++ b/src/logid/util/thread.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2019-2020 PixlOne
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#include "thread.h"
+
+using namespace logid;
+
+thread::thread(const std::function& function,
+ const std::function& exception_handler)
+ : _function (std::make_shared>(function)),
+ _exception_handler (std::make_shared> (exception_handler))
+{
+}
+
+thread::~thread()
+{
+ if(_thread)
+ if(_thread->joinable())
+ _thread->detach();
+}
+
+void thread::spawn(const std::function& function,
+ const std::function& exception_handler)
+{
+ std::thread([function, exception_handler](){
+ thread t(function, exception_handler);
+ t.runSync();
+ }).detach();
+}
+
+void thread::run()
+{
+ _thread = std::make_shared([f=this->_function,
+ eh=this->_exception_handler]() {
+ try {
+ (*f)();
+ } catch (std::exception& e) {
+ (*eh)(e);
+ }
+ });
+}
+
+void thread::wait()
+{
+ if(_thread)
+ if(_thread->joinable())
+ _thread->join();
+}
+
+void thread::runSync()
+{
+ try {
+ (*_function)();
+ } catch(std::exception& e) {
+ (*_exception_handler)(e);
+ }
+}
\ No newline at end of file
diff --git a/src/logid/util/thread.h b/src/logid/util/thread.h
new file mode 100644
index 0000000..311a1f6
--- /dev/null
+++ b/src/logid/util/thread.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2019-2020 PixlOne
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#ifndef LOGID_THREAD_H
+#define LOGID_THREAD_H
+
+#include
+#include
+#include
+#include "ExceptionHandler.h"
+
+namespace logid
+{
+ class thread
+ {
+ public:
+ explicit thread(const std::function& function,
+ const std::function&
+ exception_handler={[](std::exception& e)
+ {ExceptionHandler::Default(e);}});
+
+ ~thread();
+
+ /* This function spawns a new thread and forgets about it,
+ * safe equivalent to std::thread{...}.detach()
+ */
+ static void spawn(const std::function& function,
+ const std::function&
+ exception_handler={[](std::exception& e)
+ {ExceptionHandler::Default(e);}});
+
+ void run();
+ void wait();
+ void runSync();
+ private:
+ std::shared_ptr> _function;
+ std::shared_ptr>
+ _exception_handler;
+ std::shared_ptr _thread = nullptr;
+ };
+}
+
+#endif //LOGID_THREAD_H