falco/unit_tests/falco/test_atomic_signal_handler.cpp
Jason Dellaluce cd1c5f911c refactor(userspace): move falco logger under falco engine
Signed-off-by: Jason Dellaluce <jasondellaluce@gmail.com>
2024-05-23 09:29:23 +02:00

128 lines
3.6 KiB
C++

// SPDX-License-Identifier: Apache-2.0
/*
Copyright (C) 2023 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <falco/atomic_signal_handler.h>
#include <engine/logger.h>
#include <gtest/gtest.h>
#include <chrono>
#include <future>
#include <memory>
#include <vector>
TEST(AtomicSignalHandler, lock_free_implementation)
{
ASSERT_TRUE(falco::atomic_signal_handler().is_lock_free());
}
TEST(AtomicSignalHandler, handle_once_wait_consistency)
{
constexpr const auto thread_num = 10;
constexpr const std::chrono::seconds thread_wait_sec{2};
constexpr const std::chrono::seconds handler_wait_sec{1};
// have a shared signal handler
falco::atomic_signal_handler handler;
// launch a bunch of threads all syncing on the same handler
struct task_result_t
{
bool handled;
std::chrono::seconds duration_secs;
};
std::vector<std::future<task_result_t>> futures;
for (int i = 0; i < thread_num; i++)
{
futures.emplace_back(std::async(std::launch::async,
[&handler, thread_wait_sec]() {
auto start = std::chrono::high_resolution_clock::now();
task_result_t res;
res.handled = false;
while (!handler.handled())
{
if (handler.triggered())
{
res.handled = handler.handle([thread_wait_sec]() {
std::this_thread::sleep_for(thread_wait_sec);
});
}
}
auto diff = std::chrono::high_resolution_clock::now() - start;
res.duration_secs = std::chrono::duration_cast<std::chrono::seconds>(diff);
return res;
}));
}
// wait a bit, then trigger the signal handler from the main thread
auto total_handled = 0;
auto start = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(handler_wait_sec);
handler.trigger();
for (int i = 0; i < thread_num; i++)
{
// wait for all threads to finish and get the results from the futures
auto res = futures[i].get();
if (res.handled)
{
total_handled++;
}
ASSERT_GE(res.duration_secs, thread_wait_sec);
}
// check that the total time is consistent with the expectations
auto diff = std::chrono::high_resolution_clock::now() - start;
auto secs = std::chrono::duration_cast<std::chrono::seconds>(diff);
ASSERT_GE(secs, thread_wait_sec + handler_wait_sec);
// check that only one thread handled the signal
ASSERT_EQ(total_handled, 1);
}
TEST(AtomicSignalHandler, handle_and_reset)
{
auto do_nothing = []{};
falco::atomic_signal_handler handler;
ASSERT_FALSE(handler.triggered());
ASSERT_FALSE(handler.handled());
ASSERT_FALSE(handler.handle(do_nothing));
handler.trigger();
ASSERT_TRUE(handler.triggered());
ASSERT_FALSE(handler.handled());
ASSERT_TRUE(handler.handle(do_nothing));
ASSERT_TRUE(handler.triggered());
ASSERT_TRUE(handler.handled());
ASSERT_FALSE(handler.handle(do_nothing));
handler.trigger();
ASSERT_TRUE(handler.triggered());
ASSERT_FALSE(handler.handled());
ASSERT_TRUE(handler.handle(do_nothing));
ASSERT_TRUE(handler.triggered());
ASSERT_TRUE(handler.handled());
ASSERT_FALSE(handler.handle(do_nothing));
handler.reset();
ASSERT_FALSE(handler.triggered());
ASSERT_FALSE(handler.handled());
ASSERT_FALSE(handler.handle(do_nothing));
}