ADD: first version
This commit is contained in:
parent
7759fae428
commit
2c1b0f4c28
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
build
|
||||||
|
compile_commands.json
|
82
CMakeLists.txt
Normal file
82
CMakeLists.txt
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
#
|
||||||
|
# Copyright 2021 Dominik Meyer <dmeyer@federationhq.de>
|
||||||
|
# This file is part of the EventManager distribution hosted at https://gitea.federationhq.de/byterazor/EventManager.git
|
||||||
|
#
|
||||||
|
|
||||||
|
cmake_minimum_required (VERSION 3.1 FATAL_ERROR)
|
||||||
|
project (EventManager VERSION 0.0.1 LANGUAGES CXX)
|
||||||
|
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules )
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
|
||||||
|
include(compdb)
|
||||||
|
|
||||||
|
find_package (Threads REQUIRED)
|
||||||
|
|
||||||
|
option(EM_TESTS "ENABLE/DISABLE all tests for EventManager" ON)
|
||||||
|
IF(${EM_TESTS})
|
||||||
|
message(STATUS "EventManager tests enabled")
|
||||||
|
add_subdirectory(libs/Catch2)
|
||||||
|
include(CTest)
|
||||||
|
include(libs/Catch2/contrib/Catch.cmake)
|
||||||
|
ELSE()
|
||||||
|
message(STATUS "EventManager tests disabled")
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# all source files for the server library
|
||||||
|
#
|
||||||
|
SET(EVENTMANAGER_SOURCES
|
||||||
|
include/EventManager/Event.hpp
|
||||||
|
src/EventManager/Event.cpp
|
||||||
|
include/EventManager/Participant.hpp
|
||||||
|
src/EventManager/Participant.cpp
|
||||||
|
include/EventManager/Manager.hpp
|
||||||
|
src/EventManager/Manager.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(objlib OBJECT ${EVENTMANAGER_SOURCES})
|
||||||
|
set_property(TARGET objlib PROPERTY POSITION_INDEPENDENT_CODE 1)
|
||||||
|
target_include_directories(objlib
|
||||||
|
PUBLIC
|
||||||
|
include
|
||||||
|
PRIVATE
|
||||||
|
src
|
||||||
|
)
|
||||||
|
target_link_libraries(objlib PUBLIC Threads::Threads)
|
||||||
|
|
||||||
|
add_library(eventmanager SHARED $<TARGET_OBJECTS:objlib>)
|
||||||
|
target_include_directories(eventmanager
|
||||||
|
PUBLIC
|
||||||
|
include
|
||||||
|
PRIVATE
|
||||||
|
src
|
||||||
|
)
|
||||||
|
target_link_libraries(eventmanager PUBLIC Threads::Threads)
|
||||||
|
|
||||||
|
add_library(eventmanager-static STATIC $<TARGET_OBJECTS:objlib>)
|
||||||
|
target_include_directories(eventmanager-static
|
||||||
|
PUBLIC
|
||||||
|
include
|
||||||
|
PRIVATE
|
||||||
|
src
|
||||||
|
)
|
||||||
|
target_link_libraries(eventmanager-static PUBLIC Threads::Threads)
|
||||||
|
|
||||||
|
#
|
||||||
|
# add tests as executable
|
||||||
|
#
|
||||||
|
add_executable(test_event tests/test_event.cpp)
|
||||||
|
target_link_libraries(test_event Catch2::Catch2 eventmanager-static)
|
||||||
|
catch_discover_tests(test_event)
|
||||||
|
|
||||||
|
add_executable(test_basic tests/test_basic.cpp)
|
||||||
|
target_link_libraries(test_basic Catch2::Catch2 eventmanager-static)
|
||||||
|
catch_discover_tests(test_basic)
|
31
cmake/Modules/compdb.cmake
Normal file
31
cmake/Modules/compdb.cmake
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
#
|
||||||
|
# Copyright 2021 Dominik Meyer <dmeyer@federationhq.de>
|
||||||
|
# This file is part of the EventManager distribution hosted at https://gitea.federationhq.de/byterazor/EventManager.git
|
||||||
|
|
||||||
|
|
||||||
|
find_program(COMPDB_PATH
|
||||||
|
NAME compdb
|
||||||
|
PATHS ~/.local/bin/
|
||||||
|
/bin
|
||||||
|
/sbin
|
||||||
|
/usr/bin
|
||||||
|
/usr/sbin
|
||||||
|
/usr/local/bin
|
||||||
|
/usr/local/sbin
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (COMPDB_PATH)
|
||||||
|
IF(NOT TARGET COMPD)
|
||||||
|
add_custom_target(COMPD
|
||||||
|
ALL
|
||||||
|
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
COMMAND ${COMPDB_PATH} -p ${CMAKE_CURRENT_BINARY_DIR} list >compile_commands.json
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
132
cmake/Modules/doxygen.cmake
Normal file
132
cmake/Modules/doxygen.cmake
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2018 by George Cave - gcave@stablecoder.ca
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
find_package(Doxygen)
|
||||||
|
|
||||||
|
option(BUILD_DOCUMENTATION "Build API documentation using Doxygen. (make doc)"
|
||||||
|
${DOXYGEN_FOUND})
|
||||||
|
|
||||||
|
# Builds doxygen documentation with a default 'Doxyfile.in' or with a specified
|
||||||
|
# one, and can make the results installable (under the `doc` install target)
|
||||||
|
#
|
||||||
|
# This can only be used once per project, as each target generated is as
|
||||||
|
# `doc-${PROJECT_NAME}` unless TARGET_NAME is specified.
|
||||||
|
# ~~~
|
||||||
|
# Optional Arguments:
|
||||||
|
#
|
||||||
|
# ADD_TO_DOC
|
||||||
|
# If specified, adds this generated target to be a dependency of the more general
|
||||||
|
# `doc` target.
|
||||||
|
#
|
||||||
|
# INSTALLABLE
|
||||||
|
# Adds the generated documentation to the generic `install` target, under the
|
||||||
|
# `documentation` installation group.
|
||||||
|
#
|
||||||
|
# PROCESS_DOXYFILE
|
||||||
|
# If set, then will process the found Doxyfile through the CMAKE `configure_file`
|
||||||
|
# function for macro replacements before using it. (@ONLY)
|
||||||
|
#
|
||||||
|
# TARGET_NAME <str>
|
||||||
|
# The name to give the doc target. (Default: doc-${PROJECT_NAME})
|
||||||
|
#
|
||||||
|
# OUTPUT_DIR <str>
|
||||||
|
# The directory to place the generated output. (Default: ${CMAKE_CURRENT_BINARY_DIR}/doc)
|
||||||
|
#
|
||||||
|
# INSTALL_PATH <str>
|
||||||
|
# The path to install the documenttation under. (if not specified, defaults to
|
||||||
|
# 'share/${PROJECT_NAME})
|
||||||
|
#
|
||||||
|
# DOXYFILE_PATH <str>
|
||||||
|
# The given doxygen file to use/process. (Defaults to'${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile')
|
||||||
|
# ~~~
|
||||||
|
function(build_docs)
|
||||||
|
set(OPTIONS ADD_TO_DOC INSTALLABLE PROCESS_DOXYFILE)
|
||||||
|
set(SINGLE_VALUE_KEYWORDS
|
||||||
|
TARGET_NAME
|
||||||
|
INSTALL_PATH
|
||||||
|
DOXYFILE_PATH
|
||||||
|
OUTPUT_DIR)
|
||||||
|
set(MULTI_VALUE_KEYWORDS)
|
||||||
|
cmake_parse_arguments(build_docs
|
||||||
|
"${OPTIONS}"
|
||||||
|
"${SINGLE_VALUE_KEYWORDS}"
|
||||||
|
"${MULTI_VALUE_KEYWORDS}"
|
||||||
|
${ARGN})
|
||||||
|
|
||||||
|
if(BUILD_DOCUMENTATION)
|
||||||
|
if(NOT DOXYGEN_FOUND)
|
||||||
|
message(FATAL_ERROR "Doxygen is needed to build the documentation.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT build_docs_DOXYFILE_PATH)
|
||||||
|
set(DOXYFILE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile)
|
||||||
|
elseif(EXISTS ${build_docs_DOXYFILE_PATH})
|
||||||
|
set(DOXYFILE_PATH ${build_docs_DOXYFILE_PATH})
|
||||||
|
else()
|
||||||
|
set(DOXYFILE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${build_docs_DOXYFILE_PATH})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT EXISTS ${DOXYFILE_PATH})
|
||||||
|
message(
|
||||||
|
SEND_ERROR
|
||||||
|
"Could not find Doxyfile to use for procesing documentation at: ${DOXYFILE_PATH}"
|
||||||
|
)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(build_docs_PROCESS_DOXYFILE)
|
||||||
|
set(DOXYFILE ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
|
||||||
|
configure_file(${DOXYFILE_PATH} ${DOXYFILE} @ONLY)
|
||||||
|
else()
|
||||||
|
set(DOXYFILE ${DOXYFILE_PATH})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(build_docs_OUTPUT_DIR)
|
||||||
|
set(OUT_DIR ${build_docs_OUTPUT_DIR})
|
||||||
|
else()
|
||||||
|
set(OUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/doc)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(MAKE_DIRECTORY ${OUT_DIR})
|
||||||
|
|
||||||
|
if(build_docs_TARGET_NAME)
|
||||||
|
set(TARGET_NAME ${build_docs_TARGET_NAME})
|
||||||
|
else()
|
||||||
|
set(TARGET_NAME doc-${PROJECT_NAME})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_custom_target(${TARGET_NAME}
|
||||||
|
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE}
|
||||||
|
WORKING_DIRECTORY ${OUT_DIR}
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
if(build_docs_ADD_TO_DOC)
|
||||||
|
if(NOT TARGET doc)
|
||||||
|
add_custom_target(doc)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_dependencies(doc ${TARGET_NAME})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(build_docs_INSTALLABLE)
|
||||||
|
if(NOT build_docs_INSTALL_PATH)
|
||||||
|
set(build_docs_INSTALL_PATH share/${PROJECT_NAME})
|
||||||
|
endif()
|
||||||
|
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc/
|
||||||
|
COMPONENT documentation
|
||||||
|
DESTINATION ${build_docs_INSTALL_PATH})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endfunction()
|
99
include/EventManager/Event.hpp
Normal file
99
include/EventManager/Event.hpp
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Dominik Meyer <dmeyer@federationhq.de>
|
||||||
|
* This file is part of the EventManager distribution hosted at https://gitea.federationhq.de/byterazor/EventManager.git
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file */
|
||||||
|
#include <cstdint>
|
||||||
|
#include <atomic>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace EventManager
|
||||||
|
{
|
||||||
|
|
||||||
|
// forward declaration of a participant
|
||||||
|
class Participant;
|
||||||
|
|
||||||
|
/// Eventtype to notify all participants that a shutdown is immanent
|
||||||
|
const static std::uint32_t EVENT_TYPE_SHUTDOWN = 0;
|
||||||
|
|
||||||
|
class Event
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
/// the type of the event
|
||||||
|
std::uint32_t type_;
|
||||||
|
|
||||||
|
/// the id which uniquly identifies the event
|
||||||
|
std::uint64_t id_;
|
||||||
|
|
||||||
|
/// a possible response id, identifying if this event is in repsonse to another event
|
||||||
|
std::uint64_t responseId_;
|
||||||
|
|
||||||
|
/// identifies if this event is a response to another event
|
||||||
|
std::atomic<bool> isResponse_;
|
||||||
|
|
||||||
|
/// emitter of the event
|
||||||
|
std::shared_ptr<EventManager::Participant> emitter_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief constructor for creating a simple event
|
||||||
|
*
|
||||||
|
* @param type - what kinf of event is this
|
||||||
|
*/
|
||||||
|
Event(std::uint32_t type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructor to create a response Event
|
||||||
|
*/
|
||||||
|
Event(std::uint32_t type, const EventManager::Event &event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructor to create a response Event
|
||||||
|
*/
|
||||||
|
Event(std::uint32_t type, const std::shared_ptr<EventManager::Event> event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief return the id of the event
|
||||||
|
*/
|
||||||
|
std::uint64_t id() const { return id_;}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief return the response id if this event is a response
|
||||||
|
*/
|
||||||
|
std::uint64_t responseId() const {
|
||||||
|
if (!isResponse_)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("is not a response event");
|
||||||
|
}
|
||||||
|
return responseId_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief check if the event is a response
|
||||||
|
*/
|
||||||
|
bool isResponse() const { return isResponse_;}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief return the type of the event
|
||||||
|
*/
|
||||||
|
std::uint32_t type() const { return type_;}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief set the emitter of the event
|
||||||
|
*/
|
||||||
|
void emitter(std::shared_ptr<EventManager::Participant> participant) { emitter_=participant;}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief return the emitter of the event
|
||||||
|
*/
|
||||||
|
std::shared_ptr<EventManager::Participant> emitter() const {return emitter_;}
|
||||||
|
|
||||||
|
}; //
|
||||||
|
}; // namespace EventManager
|
190
include/EventManager/Manager.hpp
Normal file
190
include/EventManager/Manager.hpp
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Dominik Meyer <dmeyer@federationhq.de>
|
||||||
|
* This file is part of the EventManager distribution hosted at https://gitea.federationhq.de/byterazor/EventManager.git
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file */
|
||||||
|
#include <memory>
|
||||||
|
#include <atomic>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
#include <thread>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
#include <EventManager/Event.hpp>
|
||||||
|
|
||||||
|
namespace EventManager
|
||||||
|
{
|
||||||
|
|
||||||
|
// forward declaration of EventManager::Participant
|
||||||
|
class Participant;
|
||||||
|
|
||||||
|
class Manager
|
||||||
|
{
|
||||||
|
/// the thread the event manager is transmitting events in
|
||||||
|
std::unique_ptr<std::thread> mainThread_;
|
||||||
|
|
||||||
|
/// is the main thread running
|
||||||
|
std::atomic<bool> isMainThreadRunning_;
|
||||||
|
|
||||||
|
/// stop the main thread
|
||||||
|
std::atomic<bool> stopMainThread_;
|
||||||
|
|
||||||
|
/// the thread the event manager is scheduling plugins in
|
||||||
|
std::unique_ptr<std::thread> schedulingThread_;
|
||||||
|
|
||||||
|
/// is the scheduling thread running
|
||||||
|
std::atomic<bool> isSchedulingThreadRunning_;
|
||||||
|
|
||||||
|
/// stop the scheduling thread
|
||||||
|
std::atomic<bool> stopSchedulingThread_;
|
||||||
|
|
||||||
|
/// map holding all the event type and plugin combinations
|
||||||
|
std::map<std::uint32_t, std::list<std::shared_ptr<EventManager::Participant>>> eventMap_;
|
||||||
|
|
||||||
|
/// queue for incomng events
|
||||||
|
std::queue<std::shared_ptr<EventManager::Event>> eventQueue_;
|
||||||
|
|
||||||
|
/// mutex to protect the event queue
|
||||||
|
std::mutex mutexEventQueue_;
|
||||||
|
|
||||||
|
/// condition variable to wake of thread on new emit
|
||||||
|
std::condition_variable newEventInQueue_;
|
||||||
|
|
||||||
|
/// list of all plugins requiring scheduling
|
||||||
|
std::list<std::shared_ptr<EventManager::Participant>> schedulingParticipants_;
|
||||||
|
|
||||||
|
/// mutex to protect schedulingPlugins_
|
||||||
|
std::mutex mutexSchedulingParticipants_;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* all private methods
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief the method running in the main thread
|
||||||
|
*/
|
||||||
|
void mainProcess_();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief the method running in the scheduling thread
|
||||||
|
*/
|
||||||
|
void schedulingProcess_();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief process one event (call all the participants)
|
||||||
|
*/
|
||||||
|
void processEvent(const std::shared_ptr<EventManager::Event> event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief start the main thread
|
||||||
|
*/
|
||||||
|
void startMain_();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief stop the main thread
|
||||||
|
*/
|
||||||
|
void stopMain_();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief start the scheduling thread
|
||||||
|
*/
|
||||||
|
void startScheduling_();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief stop the scheduling thread
|
||||||
|
*/
|
||||||
|
void stopScheduling_();
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief The constructor for the event manager
|
||||||
|
*
|
||||||
|
* Just initializes all attributes to its starting value
|
||||||
|
*/
|
||||||
|
Manager() : mainThread_(nullptr), isMainThreadRunning_(false),
|
||||||
|
stopMainThread_(false), schedulingThread_(nullptr),
|
||||||
|
isSchedulingThreadRunning_(false), stopSchedulingThread_(false){}
|
||||||
|
|
||||||
|
|
||||||
|
~Manager();
|
||||||
|
/**
|
||||||
|
* @brief start the event manager
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief stop the event manager
|
||||||
|
*/
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief check if the eventmanager is running
|
||||||
|
*/
|
||||||
|
bool isRunning();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief emit an event and make sure it is delivered to all subscribed plugins
|
||||||
|
*
|
||||||
|
* @param event - the event to emit
|
||||||
|
*/
|
||||||
|
void emit(std::shared_ptr<EventManager::Event> event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief subscribe a plugin for the given event type
|
||||||
|
*
|
||||||
|
* @param type - the event type to subscribe to
|
||||||
|
* @param plugin - shared pointer to the plugin which subscribes
|
||||||
|
*/
|
||||||
|
void subscribe(std::uint32_t type, std::shared_ptr<EventManager::Participant> plugin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief unsubscribe a plugin from the given event type
|
||||||
|
*
|
||||||
|
* @param type - the event type to unsubscribe from
|
||||||
|
* @param plugin - shared pointer to the plugin which unsubscribes
|
||||||
|
*/
|
||||||
|
void unsubscribe(std::uint32_t type, std::shared_ptr<EventManager::Participant> plugin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief unsubscribe a plugin from the all event types
|
||||||
|
*
|
||||||
|
* @param plugin - shared pointer to the plugin which unsubscribes
|
||||||
|
*/
|
||||||
|
void unsubscribe(std::shared_ptr<EventManager::Participant> plugin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief check if there are any subscriptions within the event manager
|
||||||
|
*/
|
||||||
|
bool empty() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Wait for the EventManager to become empty.
|
||||||
|
*
|
||||||
|
* @param timeout - how many milliseconds to wait for EventManager becoming empty
|
||||||
|
*
|
||||||
|
* @return true - EventManager is empty
|
||||||
|
* @return false - EventManager is not empty
|
||||||
|
*/
|
||||||
|
bool waitEmpty(std::uint32_t timeoutMS) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief schedule the given plugin regularly
|
||||||
|
*
|
||||||
|
* @param plugin - the plugin to schedule
|
||||||
|
*/
|
||||||
|
void schedule(std::shared_ptr<EventManager::Participant> plugin);
|
||||||
|
|
||||||
|
}; // class Manager
|
||||||
|
|
||||||
|
}; //namespace EventManager
|
152
include/EventManager/Participant.hpp
Normal file
152
include/EventManager/Participant.hpp
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Dominik Meyer <dmeyer@federationhq.de>
|
||||||
|
* This file is part of the EventManager distribution hosted at https://gitea.federationhq.de/byterazor/EventManager.git
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file */
|
||||||
|
#include <memory>
|
||||||
|
#include <atomic>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
#include <EventManager/Event.hpp>
|
||||||
|
|
||||||
|
namespace EventManager
|
||||||
|
{
|
||||||
|
|
||||||
|
// forward declaration for the EventManager::Manager
|
||||||
|
class Manager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The entity participating in the event system.
|
||||||
|
*/
|
||||||
|
class Participant : public std::enable_shared_from_this<Participant>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// pointer to the event manager
|
||||||
|
std::shared_ptr<EventManager::Manager> manager_;
|
||||||
|
|
||||||
|
/// is the participant scheduled by the EventManager::Manager
|
||||||
|
std::atomic<bool> isScheduledByManager_;
|
||||||
|
|
||||||
|
/// queue for incomng events
|
||||||
|
std::queue<std::shared_ptr<EventManager::Event>> eventQueue_;
|
||||||
|
|
||||||
|
/// mutex to protect the event queue
|
||||||
|
std::mutex mutexEventQueue_;
|
||||||
|
|
||||||
|
/// condition variable to wake of thread on new trigger
|
||||||
|
std::condition_variable newEventInQueue_;
|
||||||
|
|
||||||
|
/// has the participant locked the queue itself already
|
||||||
|
std::atomic<bool> isQueueLocked_;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* all private methods
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Method called if the participant is scheduled by EventManager::Manager
|
||||||
|
*
|
||||||
|
* This method needs to be implemented by the child class.
|
||||||
|
* Please make sure this method returns as fast as possible!
|
||||||
|
* No endless loops are supported.
|
||||||
|
* Just process some incoming events and then return!
|
||||||
|
*/
|
||||||
|
virtual void schedule_() { throw std::runtime_error(std::string(__PRETTY_FUNCTION__) + " not implemented");}
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief check if events are available
|
||||||
|
*/
|
||||||
|
bool _hasEvents();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Lock the queue to process all events
|
||||||
|
*/
|
||||||
|
void _lockQueue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief UnLock the queue to process all events
|
||||||
|
*/
|
||||||
|
void _unlockQueue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief fetch one event from the queue
|
||||||
|
*/
|
||||||
|
std::shared_ptr<EventManager::Event> _fetchEvent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief wait for a new event
|
||||||
|
*/
|
||||||
|
void _waitForEvent();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This method subscribes the participant to an event type
|
||||||
|
*
|
||||||
|
* @param type - the event type to subscribe this participant to
|
||||||
|
*/
|
||||||
|
void _subscribe(std::uint32_t type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief unsubscribe the participant from the given event type
|
||||||
|
*/
|
||||||
|
void _unsubscribe(std::uint32_t type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief unsubscribe the participant from the all event types
|
||||||
|
*/
|
||||||
|
void _unsubscribe();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Method to emit an event to the event manager
|
||||||
|
*
|
||||||
|
* @param event - the event to emit
|
||||||
|
*/
|
||||||
|
void _emit(std::shared_ptr<EventManager::Event> event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief enable scheduling of this particpant through the EventManager::Manager
|
||||||
|
*/
|
||||||
|
void _enableScheduling();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief check if the participant is scheduled by event manager
|
||||||
|
*/
|
||||||
|
bool isScheduledByManager() const {return isScheduledByManager_;}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructor setting the participant up for use
|
||||||
|
*/
|
||||||
|
Participant();
|
||||||
|
|
||||||
|
void setManager(std::shared_ptr<EventManager::Manager> manager) { manager_=manager;_subscribe(EVENT_TYPE_SHUTDOWN);}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Method called by the EventManager::Manager to schedule the particpant
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void schedule() {schedule_();};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief emit an event to this participant
|
||||||
|
*/
|
||||||
|
void emit(std::shared_ptr<EventManager::Event> event);
|
||||||
|
|
||||||
|
};//
|
||||||
|
|
||||||
|
}; // namespace EventManager
|
34
src/EventManager/Event.cpp
Normal file
34
src/EventManager/Event.cpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Dominik Meyer <dmeyer@federationhq.de>
|
||||||
|
* This file is part of the EventManager distribution hosted at https://gitea.federationhq.de/byterazor/EventManager.git
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file */
|
||||||
|
#include <EventManager/Event.hpp>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
EventManager::Event::Event(std::uint32_t type) : type_(type), responseId_(0), isResponse_(false), emitter_(nullptr)
|
||||||
|
{
|
||||||
|
std::random_device rd;
|
||||||
|
std::mt19937 rng(rd());
|
||||||
|
|
||||||
|
std::uniform_int_distribution<std::mt19937::result_type> dist(1,std::numeric_limits<int>::max());
|
||||||
|
|
||||||
|
id_ = dist(rng);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventManager::Event::Event(std::uint32_t type, const EventManager::Event &event) : Event(type)
|
||||||
|
{
|
||||||
|
responseId_=event.id();
|
||||||
|
isResponse_=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventManager::Event::Event(std::uint32_t type, const std::shared_ptr<EventManager::Event> event) : Event(type)
|
||||||
|
{
|
||||||
|
responseId_=event->id();
|
||||||
|
isResponse_=true;
|
||||||
|
}
|
326
src/EventManager/Manager.cpp
Normal file
326
src/EventManager/Manager.cpp
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Dominik Meyer <dmeyer@federationhq.de>
|
||||||
|
* This file is part of the EventManager distribution hosted at https://gitea.federationhq.de/byterazor/EventManager.git
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file */
|
||||||
|
#include <EventManager/Manager.hpp>
|
||||||
|
#include <EventManager/Participant.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
void EventManager::Manager::startMain_()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (isMainThreadRunning_)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Main thread already running");
|
||||||
|
}
|
||||||
|
|
||||||
|
stopMainThread_=false;
|
||||||
|
|
||||||
|
mainThread_ = std::make_unique<std::thread>(&EventManager::Manager::mainProcess_,this);
|
||||||
|
|
||||||
|
std::int32_t timeout = 6000;
|
||||||
|
|
||||||
|
while(!isMainThreadRunning_ && timeout > 0)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
timeout-=100;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout <= 0)
|
||||||
|
{
|
||||||
|
stopMainThread_=true;
|
||||||
|
throw std::runtime_error("EventManager: can not start main thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Manager::startScheduling_()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (isSchedulingThreadRunning_)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Scheduling thread already running");
|
||||||
|
}
|
||||||
|
|
||||||
|
stopSchedulingThread_=false;
|
||||||
|
|
||||||
|
schedulingThread_ = std::make_unique<std::thread>(&EventManager::Manager::schedulingProcess_,this);
|
||||||
|
|
||||||
|
std::int32_t timeout = 6000;
|
||||||
|
|
||||||
|
while(!isSchedulingThreadRunning_ && timeout > 0)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
timeout-=100;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout <= 0)
|
||||||
|
{
|
||||||
|
stopSchedulingThread_=true;
|
||||||
|
throw std::runtime_error("EventManager: can not start scheduling thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void EventManager::Manager::stopMain_()
|
||||||
|
{
|
||||||
|
std::int32_t timeout = 6000;
|
||||||
|
stopMainThread_=true;
|
||||||
|
newEventInQueue_.notify_one();
|
||||||
|
|
||||||
|
while(isMainThreadRunning_ && timeout > 0)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
timeout-=100;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout <= 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("can not stop main thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
mainThread_->join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Manager::stopScheduling_()
|
||||||
|
{
|
||||||
|
std::int32_t timeout = 6000;
|
||||||
|
stopSchedulingThread_=true;
|
||||||
|
|
||||||
|
while(isSchedulingThreadRunning_ && timeout > 0)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
timeout-=100;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout <= 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("can not stop scheduling thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
schedulingThread_->join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Manager::start()
|
||||||
|
{
|
||||||
|
startMain_();
|
||||||
|
try {
|
||||||
|
startScheduling_();
|
||||||
|
} catch (std::exception &e)
|
||||||
|
{
|
||||||
|
stopMain_();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Manager::stop()
|
||||||
|
{
|
||||||
|
stopMain_();
|
||||||
|
stopScheduling_();
|
||||||
|
}
|
||||||
|
|
||||||
|
EventManager::Manager::~Manager()
|
||||||
|
{
|
||||||
|
if (isMainThreadRunning_)
|
||||||
|
{
|
||||||
|
stopMain_();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSchedulingThreadRunning_)
|
||||||
|
{
|
||||||
|
stopScheduling_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Manager::processEvent(const std::shared_ptr<EventManager::Event> event)
|
||||||
|
{
|
||||||
|
|
||||||
|
auto it = eventMap_.find(event->type());
|
||||||
|
|
||||||
|
if (it != eventMap_.end())
|
||||||
|
{
|
||||||
|
|
||||||
|
for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2)
|
||||||
|
{
|
||||||
|
if (event->emitter() != *it2)
|
||||||
|
{
|
||||||
|
(*it2)->emit(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Manager::mainProcess_()
|
||||||
|
{
|
||||||
|
isMainThreadRunning_=true;
|
||||||
|
|
||||||
|
while(!stopMainThread_)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(mutexEventQueue_);
|
||||||
|
newEventInQueue_.wait(lock);
|
||||||
|
while(!eventQueue_.empty())
|
||||||
|
{
|
||||||
|
std::shared_ptr<EventManager::Event> event = eventQueue_.front();
|
||||||
|
eventQueue_.pop();
|
||||||
|
processEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
isMainThreadRunning_=false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Manager::schedulingProcess_()
|
||||||
|
{
|
||||||
|
isSchedulingThreadRunning_=true;
|
||||||
|
|
||||||
|
while(!stopSchedulingThread_)
|
||||||
|
{
|
||||||
|
mutexSchedulingParticipants_.lock();
|
||||||
|
if (!schedulingParticipants_.empty())
|
||||||
|
{
|
||||||
|
for (auto it = schedulingParticipants_.begin(); it != schedulingParticipants_.end(); ++it)
|
||||||
|
{
|
||||||
|
(*it)->schedule();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutexSchedulingParticipants_.unlock();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
isSchedulingThreadRunning_=false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void EventManager::Manager::subscribe(std::uint32_t type, std::shared_ptr<EventManager::Participant> participant)
|
||||||
|
{
|
||||||
|
|
||||||
|
// check if participant is already registered
|
||||||
|
auto it = eventMap_.find(type);
|
||||||
|
|
||||||
|
if (it != eventMap_.end())
|
||||||
|
{
|
||||||
|
|
||||||
|
auto it2 = std::find(it->second.begin(), it->second.end(),participant);
|
||||||
|
|
||||||
|
if (it2 == it->second.end())
|
||||||
|
{
|
||||||
|
it->second.push_back(participant);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eventMap_[type].push_back(participant);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Manager::unsubscribe(std::uint32_t type, std::shared_ptr<EventManager::Participant> participant)
|
||||||
|
{
|
||||||
|
|
||||||
|
auto it = eventMap_.find(type);
|
||||||
|
|
||||||
|
if (it == eventMap_.end())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it2 = std::find(it->second.begin(), it->second.end(),participant);
|
||||||
|
|
||||||
|
if (it2 != it->second.end())
|
||||||
|
{
|
||||||
|
it->second.erase(it2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Manager::unsubscribe(std::shared_ptr<EventManager::Participant> participant)
|
||||||
|
{
|
||||||
|
|
||||||
|
for (auto it = eventMap_.begin(); it != eventMap_.end(); ++it)
|
||||||
|
{
|
||||||
|
unsubscribe(it->first,participant);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void EventManager::Manager::emit(const std::shared_ptr<EventManager::Event> event)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutexEventQueue_);
|
||||||
|
eventQueue_.push(event);
|
||||||
|
}
|
||||||
|
newEventInQueue_.notify_one();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventManager::Manager::isRunning()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (isMainThreadRunning_ && isSchedulingThreadRunning_)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventManager::Manager::empty() const
|
||||||
|
{
|
||||||
|
bool isEmpty=true;
|
||||||
|
|
||||||
|
for (auto it = eventMap_.begin(); it != eventMap_.end(); ++it)
|
||||||
|
{
|
||||||
|
if ( !(*it).second.empty())
|
||||||
|
{
|
||||||
|
isEmpty=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventManager::Manager::waitEmpty(std::uint32_t timeoutMS) const
|
||||||
|
{
|
||||||
|
std::uint32_t timeout=timeoutMS;
|
||||||
|
|
||||||
|
while(!empty() && timeout > 0)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
timeout-=100;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Manager::schedule(std::shared_ptr<EventManager::Participant> participant)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mutexSchedulingParticipants_);
|
||||||
|
|
||||||
|
auto it = std::find(schedulingParticipants_.begin(), schedulingParticipants_.end(), participant);
|
||||||
|
|
||||||
|
if (it == schedulingParticipants_.end())
|
||||||
|
{
|
||||||
|
schedulingParticipants_.push_back(participant);
|
||||||
|
}
|
||||||
|
}
|
120
src/EventManager/Participant.cpp
Normal file
120
src/EventManager/Participant.cpp
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Dominik Meyer <dmeyer@federationhq.de>
|
||||||
|
* This file is part of the EventManager distribution hosted at https://gitea.federationhq.de/byterazor/EventManager.git
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file */
|
||||||
|
#include <EventManager/Participant.hpp>
|
||||||
|
#include <EventManager/Manager.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
EventManager::Participant::Participant() : manager_(nullptr),
|
||||||
|
isScheduledByManager_(false), isQueueLocked_(false)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Participant::emit(std::shared_ptr<EventManager::Event> event)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutexEventQueue_);
|
||||||
|
eventQueue_.push(event);
|
||||||
|
}
|
||||||
|
newEventInQueue_.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventManager::Participant::_hasEvents()
|
||||||
|
{
|
||||||
|
if (isQueueLocked_)
|
||||||
|
{
|
||||||
|
return !eventQueue_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> guard(mutexEventQueue_);
|
||||||
|
|
||||||
|
|
||||||
|
return !eventQueue_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Participant::_lockQueue()
|
||||||
|
{
|
||||||
|
mutexEventQueue_.lock();
|
||||||
|
isQueueLocked_=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Participant::_unlockQueue()
|
||||||
|
{
|
||||||
|
mutexEventQueue_.unlock();
|
||||||
|
isQueueLocked_=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<EventManager::Event> EventManager::Participant::_fetchEvent()
|
||||||
|
{
|
||||||
|
if (!isQueueLocked_)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("queue not locked");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<EventManager::Event> event = eventQueue_.front();
|
||||||
|
eventQueue_.pop();
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Participant::_waitForEvent()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(mutexEventQueue_);
|
||||||
|
newEventInQueue_.wait(lock);
|
||||||
|
isQueueLocked_=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Participant::_enableScheduling()
|
||||||
|
{
|
||||||
|
if (manager_ == nullptr)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("no event manager set yet");
|
||||||
|
}
|
||||||
|
manager_->schedule(shared_from_this());
|
||||||
|
isScheduledByManager_=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Participant::_subscribe(std::uint32_t type)
|
||||||
|
{
|
||||||
|
if (manager_ == nullptr)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("no event manager set yet");
|
||||||
|
}
|
||||||
|
manager_->subscribe(type, shared_from_this());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Participant::_unsubscribe(std::uint32_t type)
|
||||||
|
{
|
||||||
|
if (manager_ == nullptr)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("no event manager set yet");
|
||||||
|
}
|
||||||
|
manager_->unsubscribe(type, shared_from_this());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Participant::_unsubscribe()
|
||||||
|
{
|
||||||
|
if (manager_ == nullptr)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("no event manager set yet");
|
||||||
|
}
|
||||||
|
manager_->unsubscribe(shared_from_this());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::Participant::_emit(std::shared_ptr<EventManager::Event> event)
|
||||||
|
{
|
||||||
|
if (manager_ == nullptr)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("no event manager set yet");
|
||||||
|
}
|
||||||
|
event->emitter(shared_from_this());
|
||||||
|
manager_->emit(event);
|
||||||
|
}
|
164
tests/test_basic.cpp
Normal file
164
tests/test_basic.cpp
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Dominik Meyer <dmeyer@federationhq.de>
|
||||||
|
* This file is part of the EventManager distribution hosted at https://gitea.federationhq.de/byterazor/EventManager.git
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file */
|
||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#include <EventManager/Event.hpp>
|
||||||
|
#include <EventManager/Manager.hpp>
|
||||||
|
#include <EventManager/Participant.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
|
||||||
|
const static std::uint32_t TEST_EVENT0 = 10;
|
||||||
|
const static std::uint32_t TEST_EVENT1 = 20;
|
||||||
|
|
||||||
|
class myParticipant : public EventManager::Participant
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool receivedEvent_;
|
||||||
|
|
||||||
|
std::uint32_t eventType_;
|
||||||
|
|
||||||
|
std::uint32_t id_;
|
||||||
|
|
||||||
|
void shutdown_() {
|
||||||
|
_unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
void schedule_() {
|
||||||
|
std::shared_ptr<EventManager::Event> event = nullptr;
|
||||||
|
|
||||||
|
_lockQueue();
|
||||||
|
if (_hasEvents())
|
||||||
|
{
|
||||||
|
event = _fetchEvent();
|
||||||
|
}
|
||||||
|
_unlockQueue();
|
||||||
|
if (event == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->type() == EventManager::EVENT_TYPE_SHUTDOWN)
|
||||||
|
{
|
||||||
|
shutdown_();
|
||||||
|
}
|
||||||
|
else if (event->type() == eventType_)
|
||||||
|
{
|
||||||
|
receivedEvent_=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
myParticipant(std::uint32_t id,std::uint32_t eventType) : EventManager::Participant()
|
||||||
|
{
|
||||||
|
id_=id;
|
||||||
|
receivedEvent_=false;
|
||||||
|
eventType_=eventType;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool eventReceived() const { return receivedEvent_;}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
_subscribe(eventType_);
|
||||||
|
_enableScheduling();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
SCENARIO("Basic Usage of EventManager", "[Manager]")
|
||||||
|
{
|
||||||
|
GIVEN("an EventManager::Manager and two participants")
|
||||||
|
{
|
||||||
|
|
||||||
|
std::shared_ptr<EventManager::Manager> manager;
|
||||||
|
REQUIRE_NOTHROW([&]()
|
||||||
|
{
|
||||||
|
manager = std::make_shared<EventManager::Manager>();
|
||||||
|
}());
|
||||||
|
|
||||||
|
REQUIRE(manager->empty() == true);
|
||||||
|
|
||||||
|
std::shared_ptr<myParticipant> participant0;
|
||||||
|
REQUIRE_NOTHROW([&]()
|
||||||
|
{
|
||||||
|
participant0 = std::make_shared<myParticipant>(0,TEST_EVENT0);
|
||||||
|
participant0->setManager(manager);
|
||||||
|
participant0->init();
|
||||||
|
}());
|
||||||
|
|
||||||
|
REQUIRE(manager->empty() == false);
|
||||||
|
|
||||||
|
std::shared_ptr<myParticipant> participant1;
|
||||||
|
REQUIRE_NOTHROW([&]()
|
||||||
|
{
|
||||||
|
participant1 = std::make_shared<myParticipant>(1,TEST_EVENT1);
|
||||||
|
participant1->setManager(manager);
|
||||||
|
participant1->init();
|
||||||
|
}());
|
||||||
|
|
||||||
|
REQUIRE(manager->empty() == false);
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW([&]()
|
||||||
|
{
|
||||||
|
manager->start();
|
||||||
|
}());
|
||||||
|
|
||||||
|
REQUIRE(manager->isRunning() == true);
|
||||||
|
|
||||||
|
WHEN("emitting shutdown event")
|
||||||
|
{
|
||||||
|
std::shared_ptr<EventManager::Event> shutdown = std::make_shared<EventManager::Event>(EventManager::EVENT_TYPE_SHUTDOWN);
|
||||||
|
|
||||||
|
manager->emit(shutdown);
|
||||||
|
manager->waitEmpty(3000);
|
||||||
|
|
||||||
|
|
||||||
|
THEN("participants are shutting down")
|
||||||
|
{
|
||||||
|
REQUIRE(manager->empty() == true);
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW([&]()
|
||||||
|
{
|
||||||
|
manager->stop();
|
||||||
|
}());
|
||||||
|
|
||||||
|
REQUIRE(manager->isRunning() == false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("emitting events event")
|
||||||
|
{
|
||||||
|
std::shared_ptr<EventManager::Event> shutdown = std::make_shared<EventManager::Event>(EventManager::EVENT_TYPE_SHUTDOWN);
|
||||||
|
std::shared_ptr<EventManager::Event> e0 = std::make_shared<EventManager::Event>(TEST_EVENT0);
|
||||||
|
std::shared_ptr<EventManager::Event> e1 = std::make_shared<EventManager::Event>(TEST_EVENT1);
|
||||||
|
manager->emit(e0);
|
||||||
|
manager->emit(e1);
|
||||||
|
manager->emit(shutdown);
|
||||||
|
manager->waitEmpty(3000);
|
||||||
|
|
||||||
|
|
||||||
|
THEN("participants recevied events and are shutting down")
|
||||||
|
{
|
||||||
|
REQUIRE(manager->empty() == true);
|
||||||
|
REQUIRE(participant0->eventReceived());
|
||||||
|
REQUIRE(participant1->eventReceived());
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW([&]()
|
||||||
|
{
|
||||||
|
manager->stop();
|
||||||
|
}());
|
||||||
|
|
||||||
|
REQUIRE(manager->isRunning() == false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
tests/test_event.cpp
Normal file
55
tests/test_event.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Dominik Meyer <dmeyer@federationhq.de>
|
||||||
|
* This file is part of the EventManager distribution hosted at https://gitea.federationhq.de/byterazor/EventManager.git
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file */
|
||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
#include <EventManager/Event.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
SCENARIO("Use Event Class", "[Event]")
|
||||||
|
{
|
||||||
|
GIVEN("nothing")
|
||||||
|
{
|
||||||
|
WHEN("creating an Event from scratch")
|
||||||
|
{
|
||||||
|
std::unique_ptr<EventManager::Event> e = std::make_unique<EventManager::Event>(10);
|
||||||
|
|
||||||
|
THEN("the attributes can be correctly fetched")
|
||||||
|
{
|
||||||
|
REQUIRE(e->id() >= std::numeric_limits<std::uint64_t>::min());
|
||||||
|
REQUIRE(e->id() <= std::numeric_limits<std::uint64_t>::max());
|
||||||
|
REQUIRE(e->isResponse() == false);
|
||||||
|
REQUIRE_THROWS([&]()
|
||||||
|
{
|
||||||
|
e->responseId();
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("creating an response Event")
|
||||||
|
{
|
||||||
|
std::unique_ptr<EventManager::Event> e = std::make_unique<EventManager::Event>(10);
|
||||||
|
std::unique_ptr<EventManager::Event> r = std::make_unique<EventManager::Event>(10, *e);
|
||||||
|
|
||||||
|
THEN("the attributes can be correctly fetched")
|
||||||
|
{
|
||||||
|
REQUIRE(r->id() >= std::numeric_limits<std::uint64_t>::min());
|
||||||
|
REQUIRE(r->id() <= std::numeric_limits<std::uint64_t>::max());
|
||||||
|
REQUIRE(r->isResponse() == true);
|
||||||
|
std::uint64_t rid=0;
|
||||||
|
REQUIRE_NOTHROW([&]()
|
||||||
|
{
|
||||||
|
rid=r->responseId();
|
||||||
|
}());
|
||||||
|
REQUIRE(rid == e->id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user