diff options
author | Alexander Mohr <alexander.m.mohr@mercedes-benz.com> | 2023-01-11 10:04:50 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-11 10:04:50 +0100 |
commit | 2224cddf140c060d92455ad3ee585e3abfc38eb2 (patch) | |
tree | b3c9ce2df3a286cbd302ec0f6ba28faf0bad49ca /tests/gtest_dlt_user.cpp | |
parent | 8c3c0a2a6eae04022a3b1a8a2387563bc3aee936 (diff) | |
download | DLT-daemon-2224cddf140c060d92455ad3ee585e3abfc38eb2.tar.gz |
dlt-user: Fix crashes in dlt_free during dlt_init (#362)
If dlt_free is called while dlt_init is running, the dlt-user library
crashes because the init and free where not thread safe.
This commit introduces a new field dlt_user_initialising
which will prevent entering dlt_free while dlt_init is running.
Correctness is ensured by a unit test
Signed-off-by: Alexander Mohr <alexander.m.mohr@mercedes-benz.com>
Diffstat (limited to 'tests/gtest_dlt_user.cpp')
-rw-r--r-- | tests/gtest_dlt_user.cpp | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/tests/gtest_dlt_user.cpp b/tests/gtest_dlt_user.cpp index cc7851a..aa8b28e 100644 --- a/tests/gtest_dlt_user.cpp +++ b/tests/gtest_dlt_user.cpp @@ -30,6 +30,7 @@ #include <string.h> #include <stdint.h> #include <float.h> +#include <chrono> extern "C" { #include "dlt_user.h" @@ -5249,6 +5250,92 @@ TEST(t_dlt_user_is_logLevel_enabled, nullpointer) } /*/////////////////////////////////////// */ +/* t_dlt_user_shutdown_while_init_is_running */ + +struct ShutdownWhileInitParams { + ShutdownWhileInitParams() = default; + // delete copy constructor + ShutdownWhileInitParams(const ShutdownWhileInitParams&) = delete; + + std::chrono::time_point<std::chrono::steady_clock> stop_time; + + pthread_cond_t dlt_free_done = PTHREAD_COND_INITIALIZER; + pthread_mutex_t dlt_free_mtx = PTHREAD_MUTEX_INITIALIZER; + bool has_error = false; + +}; + +void* dlt_free_call_and_deadlock_detection(void *arg) { + auto *params = static_cast<ShutdownWhileInitParams *>(arg); + + // allow thread to be canceled + int old_thread_type; + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_thread_type); + + dlt_free(); + + // signal that we are done + pthread_mutex_lock(¶ms->dlt_free_mtx); + pthread_cond_signal(¶ms->dlt_free_done); + pthread_mutex_unlock(¶ms->dlt_free_mtx); + return nullptr; +} + +void *dlt_free_thread(void *arg) { + auto *params = static_cast<ShutdownWhileInitParams *>(arg); + while (std::chrono::steady_clock::now() < params->stop_time && !params->has_error) { + + // pthread cond_timedwait expects an absolute time to wait + struct timespec abs_time{}; + clock_gettime(CLOCK_REALTIME, &abs_time); + abs_time.tv_sec += 3; // wait at most 3 seconds + + pthread_t dlt_free_deadlock_detection_thread_id; + + pthread_mutex_lock(¶ms->dlt_free_mtx); + pthread_create(&dlt_free_deadlock_detection_thread_id, nullptr, dlt_free_call_and_deadlock_detection, params); + const auto err = pthread_cond_timedwait(¶ms->dlt_free_done, ¶ms->dlt_free_mtx, &abs_time); + pthread_mutex_unlock(¶ms->dlt_free_mtx); + + if (err == ETIMEDOUT) { + fprintf(stderr, "\n%s: detected DLT-deadlock!\n", __func__); + params->has_error = true; + + // cancel thread after timeout, so join won't block forever. + pthread_cancel(dlt_free_deadlock_detection_thread_id); + } + + pthread_join(dlt_free_deadlock_detection_thread_id, nullptr); + } + + return nullptr; +} + +TEST(t_dlt_user_shutdown_while_init_is_running, normal) { + const auto max_runtime = std::chrono::seconds(15); + const auto stop_time = std::chrono::steady_clock::now() + max_runtime; + + struct ShutdownWhileInitParams args{}; + args.stop_time = stop_time; + + pthread_t dlt_free_thread_id; + pthread_create(&dlt_free_thread_id, nullptr, dlt_free_thread, &args); + + while (std::chrono::steady_clock::now() < stop_time && !args.has_error) { + dlt_init(); + } + + pthread_join(dlt_free_thread_id, nullptr); + EXPECT_FALSE(args.has_error); + + const auto last_init = dlt_init(); + const auto last_free = dlt_free(); + + EXPECT_EQ(last_init, DLT_RETURN_OK); + EXPECT_EQ(last_free, DLT_RETURN_OK); +} + +/*/////////////////////////////////////// */ /* main */ int main(int argc, char **argv) { |