Skip to content

Commit

Permalink
feat: Implement API for event loop and timers.
Browse files Browse the repository at this point in the history
Ticket: MEN-6127
Changelog: None

Signed-off-by: Kristian Amlie <[email protected]>
  • Loading branch information
Kristian Amlie committed Jan 10, 2023
1 parent 9bdcf5a commit 5a2fc62
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
*.dirstamp

# Test binaries
*_test*
*_test

# Build artifacts
/mender
Expand Down
2 changes: 1 addition & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ test:
stage: test
image: debian:11
before_script:
- apt update && apt install -yyq g++ cmake git make lcov pkg-config liblmdb++-dev
- apt update && apt install -yyq g++ cmake git make lcov pkg-config liblmdb++-dev libboost-dev
script:
- cmake .
- make coverage
Expand Down
11 changes: 11 additions & 0 deletions common/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
set(json_sources "$<$<STREQUAL:${PLATFORM},linux_x86>:nlohmann/nlohmann_json.cpp>")
set(kv_db_sources "$<$<STREQUAL:${PLATFORM},linux_x86>:lmdb/lmdb_kv_db.cpp>")
set(events_sources "$<$<STREQUAL:${PLATFORM},linux_x86>:boost/events.cpp>")

add_library(common_json STATIC json/platform/${json_sources})
target_include_directories(common_json PRIVATE ../)
Expand All @@ -23,3 +24,13 @@ if(${kv_db_sources} MATCHES ".*lmdb.*")
target_link_libraries(common_kv_db PUBLIC "${LMDB_LIB}")
endif()
endif()

add_library(common_events STATIC events/platform/${events_sources})
target_include_directories(common_events PRIVATE ../)
target_compile_options(common_events PUBLIC -DMENDER_EVENTS_USE_BOOST=1)

add_executable(events_test EXCLUDE_FROM_ALL events_test.cpp)
target_link_libraries(events_test PUBLIC common_events GTest::gtest_main)
target_include_directories(events_test PRIVATE ../)
gtest_discover_tests(events_test)
add_dependencies(check events_test)
67 changes: 67 additions & 0 deletions common/events.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2023 Northern.tech AS
//
// 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 <functional>
#include <system_error>

typedef std::function<void(std::error_code)> EventHandler;

#ifdef MENDER_EVENTS_USE_BOOST
#include <boost/asio.hpp>
#endif // MENDER_EVENTS_USE_BOOST

namespace mender::events {

#ifdef MENDER_EVENTS_USE_BOOST
namespace asio = boost::asio;
#endif // MENDER_EVENTS_USE_BOOST

class EventLoop {
public:
void Run();
void Stop();

private:
#ifdef MENDER_EVENTS_USE_BOOST
asio::io_context ctx_;

friend class Timer;
#endif // MENDER_EVENTS_USE_BOOST
};

class Timer {
public:
Timer(EventLoop &loop);

template <typename Duration>
void Wait(Duration duration) {
timer_.expires_after(duration);
timer_.wait();
}

template <typename Duration>
void AsyncWait(Duration duration, EventHandler handler) {
timer_.expires_after(duration);
timer_.async_wait(handler);
}

void Cancel();

private:
#ifdef MENDER_EVENTS_USE_BOOST
asio::steady_timer timer_;
#endif // MENDER_EVENTS_USE_BOOST
};

} // namespace mender::events
39 changes: 39 additions & 0 deletions common/events/platform/boost/events.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2023 Northern.tech AS
//
// 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 <common/events.hpp>

#include <boost/asio.hpp>

namespace asio = boost::asio;

namespace mender::events {

void EventLoop::Run() {
ctx_.run();
}

void EventLoop::Stop() {
ctx_.stop();
}

Timer::Timer(EventLoop &loop) :
timer_(loop.ctx_) {
}

void Timer::Cancel() {
timer_.cancel();
}

} // namespace mender::events
111 changes: 111 additions & 0 deletions common/events_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright 2023 Northern.tech AS
//
// 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 <common/events.hpp>

#include <gtest/gtest.h>

#include <thread>
#include <array>

namespace events = mender::events;

TEST(Events, Timers) {
using std::chrono::seconds;
using std::chrono::steady_clock;

auto short_wait = seconds(1);
auto medium_wait = seconds(5);
auto long_wait = seconds(10);

// Since we'll be waiting quite a bit, run test cases in parallel.
std::thread test_threads[] = {
// Synchronous wait.
std::thread([&]() {
auto check_point = steady_clock::now();
events::EventLoop loop;
events::Timer timer(loop);

timer.Wait(short_wait);
EXPECT_GE(steady_clock::now(), check_point + short_wait);
}),

// Asynchronous wait.
std::thread([&]() {
auto check_point = steady_clock::now();
events::EventLoop loop;
events::Timer timer(loop);

timer.AsyncWait(short_wait, [](std::error_code ec) {});
loop.Run();
EXPECT_GE(steady_clock::now(), check_point + short_wait);
}),

// Asynchronous wait with cancel.
std::thread([&]() {
auto check_point = steady_clock::now();
events::EventLoop loop;
events::Timer timer(loop);

timer.AsyncWait(long_wait, [](std::error_code ec) {});
timer.Cancel();
loop.Run();
EXPECT_LT(steady_clock::now(), check_point + long_wait);
}),

// Two asynchronous waits.
std::thread([&]() {
auto check_point = steady_clock::now();
events::EventLoop loop;
events::Timer timer(loop);
events::Timer timer2(loop);

timer.AsyncWait(short_wait, [](std::error_code ec) {});
timer2.AsyncWait(medium_wait, [](std::error_code ec) {});
loop.Run();
EXPECT_GE(steady_clock::now(), check_point + medium_wait);
}),

// Two asynchronous waits with cancel.
std::thread([&]() {
auto check_point = steady_clock::now();
events::EventLoop loop;
events::Timer timer(loop);
events::Timer timer2(loop);

timer.AsyncWait(short_wait, [&timer2](std::error_code ec) { timer2.Cancel(); });
timer2.AsyncWait(long_wait, [](std::error_code ec) {});
loop.Run();
EXPECT_GE(steady_clock::now(), check_point + short_wait);
EXPECT_LT(steady_clock::now(), check_point + long_wait);
}),

// Stop event loop.
std::thread([&]() {
auto check_point = steady_clock::now();
events::EventLoop loop;
events::Timer timer(loop);
events::Timer timer2(loop);

timer.AsyncWait(short_wait, [&loop](std::error_code ec) { loop.Stop(); });
timer2.AsyncWait(long_wait, [](std::error_code ec) {});
loop.Run();
EXPECT_GE(steady_clock::now(), check_point + short_wait);
EXPECT_LT(steady_clock::now(), check_point + long_wait);
}),
};
for (auto &thr : test_threads) {
thr.join();
}
}
2 changes: 1 addition & 1 deletion tests/Dockerfile.daemon
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
FROM ubuntu:20.04 AS build

ENV DEBIAN_FRONTEND=noninteractive
RUN apt update && apt install -y cmake make git build-essential jq curl pkg-config liblmdb++-dev
RUN apt update && apt install -y cmake make git build-essential jq curl pkg-config liblmdb++-dev libboost-dev

WORKDIR /cpp/src/github.com/mendersoftware/mender
COPY ./ .
Expand Down

0 comments on commit 5a2fc62

Please sign in to comment.