diff --git a/CMakeLists.txt b/CMakeLists.txt index 39853ee..3cc8deb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,7 +88,7 @@ if (MSVC) CMAKE_C_FLAGS_RELEASE ) foreach(CompilerFlag ${CompilerFlags}) - string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") + string(REGEX REPLACE "/M[DT]d?" "/MT" ${CompilerFlag} "${${CompilerFlag}}") endforeach() add_compile_options("/EHsc") #We require exception handling else() @@ -173,6 +173,12 @@ set(HEADER_FILES src/channel/TreeView.h ) +if(NOT ThreadPool_FOUND) + set(SOURCE_FILES ${SOURCE_FILES} + src/misc/threads.cpp + ) +endif() + if(FEATURE_LOGGING) set(SOURCE_FILES ${SOURCE_FILES} src/log/LogUtils.cpp diff --git a/src/misc/task_executor.cpp b/src/misc/task_executor.cpp index a519ead..90bc520 100644 --- a/src/misc/task_executor.cpp +++ b/src/misc/task_executor.cpp @@ -3,9 +3,10 @@ // #include "./task_executor.h" -#include +#include "./threads.h" #include #include +#include using std::chrono::system_clock; using namespace ts; @@ -315,7 +316,7 @@ void task_executor::executor(std::shared_ptr executor_context) continue; } - auto execute_timestamp = system_clock::now(); + auto execute_timestamp = std::chrono::system_clock::now(); std::chrono::system_clock::time_point next_timestamp{}; if(task_context->task_recurring_head) { if(task_context->task_recurring_head->scheduled_invoke <= execute_timestamp) { @@ -338,7 +339,9 @@ void task_executor::executor(std::shared_ptr executor_context) } task->last_invoked = execute_timestamp; - task->scheduled_invoke = std::max(system_clock::now(), execute_timestamp + task->interval); + + auto expected_next_invoke = execute_timestamp + std::chrono::duration_cast(task->interval); + task->scheduled_invoke = std::max(std::chrono::system_clock::now(), expected_next_invoke); task_lock.lock(); executor_context->executing_recurring_task = nullptr; diff --git a/src/misc/threads.cpp b/src/misc/threads.cpp new file mode 100644 index 0000000..da9172a --- /dev/null +++ b/src/misc/threads.cpp @@ -0,0 +1,93 @@ +#include "./threads.h" +#include + +#ifdef WIN32 +#include +#else +#include +#endif + +std::string threads::name(std::thread &thread) { +#ifdef WIN32 + static std::string _empty{}; + return _empty; +#else + char buffer[255]; /* min 16 characters */ + pthread_getname_np(thread.native_handle(), buffer, 255); + return std::string{buffer}; +#endif +} + +bool threads::name(std::thread &thread, const std::string_view &name) { +#ifdef WIN32 + return false; +#else + char buffer[255]; /* min 16 characters */ + + memcpy(buffer, name.data(), name.length()); + buffer[name.length()] = '\0'; + buffer[16] = '\0'; /* cut of the name after 16 characters */ + + auto error = pthread_setname_np(thread.native_handle(), buffer); + return error == 0; +#endif +} + +bool threads::save_join(std::thread &thread, bool rd) { + try { + if(thread.joinable()) + thread.join(); + } catch(const std::system_error& ex) { + if(ex.code() == std::errc::resource_deadlock_would_occur) { + if(rd) + return false; + throw; + } else if(ex.code() == std::errc::no_such_process) { + return false; + } else if(ex.code() == std::errc::invalid_argument) { + return false; + } else { + throw; + } + } + return true; +} + +bool threads::timed_join(std::thread &thread, const std::chrono::nanoseconds &timeout) { +#ifdef WIN32 + auto result = WaitForSingleObject(thread.native_handle(), (DWORD) std::chrono::floor(timeout).count()); + if(result != 0) + return false; + if(thread.joinable()) + thread.join(); + return result; +#else + struct timespec ts{}; + if (clock_gettime(CLOCK_REALTIME, &ts) == -1) + return false; /* failed to get clock time */ + + auto seconds = std::chrono::floor(timeout); + auto nanoseconds = std::chrono::ceil(timeout) - seconds; + ts.tv_sec += seconds.count(); + ts.tv_nsec += nanoseconds.count(); + if(ts.tv_nsec >= 1e9) { + ts.tv_sec += 1; + ts.tv_nsec -= 1e9; + } + auto result = pthread_timedjoin_np(thread.native_handle(), nullptr, &ts); + if(result > 0 && result != ESRCH) return false; + + /* let the std lib set their flags */ + std::thread _empty{}; /* could be destroyed even with an "active" thread handle because we overwrote the std::terminate() function with a macro */ + _empty = std::move(thread); + + /* + * Undefined behaviour: + * We destroy everything in a non trivial class, + * But when we take a closer look its just a wrapper around the native_handle type which could be an DWORD or a pthread_t which are both trivial destructible! + */ + memset(&_empty, 0, sizeof(_empty)); // NOLINT(bugprone-undefined-memory-manipulation) + + return true; +#endif +} diff --git a/src/misc/threads.h b/src/misc/threads.h new file mode 100644 index 0000000..f746f3e --- /dev/null +++ b/src/misc/threads.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +namespace threads { + extern bool name(std::thread& /* thread */, const std::string_view& /* name */); + extern std::string name(std::thread& /* thread */); + + /* + * This function will not throw an error if the thread has already been joined. + * It returns true if join succeeded, false on any error (like thread has already be joined) + */ + extern bool save_join(std::thread& /* thread */, bool /* ignore resource deadlock */ = false); + + extern bool timed_join(std::thread& /* thread */, const std::chrono::nanoseconds& /* timeout */); + + template + inline bool timed_join(std::thread& thread, const std::chrono::time_point& timeout) { + auto now = Clock::now(); + if(now > timeout) + timeout = now; + return timed_join(thread, timeout - now); + } +} diff --git a/src/protocol/CryptHandler.cpp b/src/protocol/CryptHandler.cpp index b7bd050..4b720ee 100644 --- a/src/protocol/CryptHandler.cpp +++ b/src/protocol/CryptHandler.cpp @@ -1,6 +1,6 @@ //#define NO_OPEN_SSL /* because we're lazy and dont want to build this lib extra for the TeaClient */ #define FIXEDINT_H_INCLUDED /* else it will be included by ge */ -#include +#include #include #include #include