diff options
-rw-r--r-- | Makefile.am | 35 | ||||
-rw-r--r-- | src/CommonAPI/MainLoopContext.h | 323 | ||||
-rw-r--r-- | src/CommonAPI/Runtime.cpp | 5 | ||||
-rw-r--r-- | src/CommonAPI/Runtime.h | 15 |
4 files changed, 360 insertions, 18 deletions
diff --git a/Makefile.am b/Makefile.am index c3a79fb..bc817d1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,9 +14,9 @@ MOSTLYCLEANFILES += ${DX_CLEANFILES} # ------------------------------------------------------------------------------ MAINTAINERCLEANFILES = \ - Makefile.in \ - aclocal.m4 \ - configure + Makefile.in \ + aclocal.m4 \ + configure clean-local: -rm -rf src-gen @@ -27,34 +27,35 @@ maintainer-clean-local: -rm -f m4/libtool*.m4 -rm -f m4/lt*.m4 - + lib_LTLIBRARIES = libCommonAPI.la libCommonAPI_la_SOURCES = \ - src/CommonAPI/Runtime.cpp + src/CommonAPI/Runtime.cpp CommonAPI_includedir=$(includedir)/CommonAPI-${VERSION}/CommonAPI CommonAPI_include_HEADERS = \ - src/CommonAPI/Attribute.h \ + src/CommonAPI/Attribute.h \ src/CommonAPI/AttributeExtension.h \ src/CommonAPI/ByteBuffer.h \ src/CommonAPI/Event.h \ src/CommonAPI/Factory.h \ - src/CommonAPI/InputStream.h \ + src/CommonAPI/InputStream.h \ + src/CommonAPI/MainLoopContext.h \ src/CommonAPI/MiddlewareInfo.h \ - src/CommonAPI/OutputStream.h \ + src/CommonAPI/OutputStream.h \ src/CommonAPI/Proxy.h \ - src/CommonAPI/Runtime.h \ - src/CommonAPI/SerializableStruct.h \ - src/CommonAPI/SerializableVariant.h \ - src/CommonAPI/SerializableVariant.hpp \ - src/CommonAPI/Stub.h \ - src/CommonAPI/types.h + src/CommonAPI/Runtime.h \ + src/CommonAPI/SerializableStruct.h \ + src/CommonAPI/SerializableVariant.h \ + src/CommonAPI/SerializableVariant.hpp \ + src/CommonAPI/Stub.h \ + src/CommonAPI/types.h libCommonAPI_la_LIBADD = -ldl libCommonAPI_la_LDFLAGS = \ - ${AM_LDFLAGS} \ - -version-info ${LIBCOMMONAPI_CURRENT}:${LIBCOMMONAPI_REVISION}:${LIBCOMMONAPI_AGE} + ${AM_LDFLAGS} \ + -version-info ${LIBCOMMONAPI_CURRENT}:${LIBCOMMONAPI_REVISION}:${LIBCOMMONAPI_AGE} pkgconfigdir = ${libdir}/pkgconfig pkgconfig_DATA = CommonAPI.pc @@ -63,7 +64,7 @@ pkgconfig_DATA = CommonAPI.pc if ENABLE_TESTS check_PROGRAMS = \ - VariantTest + VariantTest TESTS = ${check_PROGRAMS} diff --git a/src/CommonAPI/MainLoopContext.h b/src/CommonAPI/MainLoopContext.h new file mode 100644 index 0000000..13bc4d9 --- /dev/null +++ b/src/CommonAPI/MainLoopContext.h @@ -0,0 +1,323 @@ +/* Copyright (C) 2013 BMW Group + * Author: Manfred Bathelt (manfred.bathelt@bmw.de) + * Author: Juergen Gehring (juergen.gehring@bmw.de) + * 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 http://mozilla.org/MPL/2.0/. */ + +#ifndef COMMONAPI_MAIN_LOOP_CONTEXT_H_ +#define COMMONAPI_MAIN_LOOP_CONTEXT_H_ + + +#include <stdint.h> +#include <poll.h> +#include <limits> +#include <vector> +#include <chrono> + + +namespace CommonAPI { + + +enum class DispatchPriority { + VERY_HIGH, + HIGH, + DEFAULT, + LOW, + VERY_LOW +}; + + +static int64_t getCurrentTimeInMs() { + return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); +} + + +/** + * \brief Describes a basic element that periodically needs to be dispatched. + * + * A DispatchSource is not directly related to a file descriptor, but + * may be dependent on a watch that manages a file descriptor. If this + * is the case, the corresponding Watch will provide information about + * which DispatchSources are dependent. + */ +struct DispatchSource { + virtual ~DispatchSource() {} + + /** + * Indicates whether this source is ready to be dispatched. + * "Prepare" will be called before polling the file descriptors. + * + * @return 'true' if the source is ready to be dispatched. + */ + virtual bool prepare(int64_t& timeout) = 0; + + /** + * Indicates whether this source is ready to be dispatched. + * "Check" will be called after polling the file descriptors. + * + * @return 'true' if the source is ready to be dispatched. + */ + virtual bool check() = 0; + + /** + * The return value indicates whether this dispatch source currently has + * more data to dispatch. The mainloop may chose to ignore the return value. + * + * @return 'true' if there currently is more to dispatch, 'false' if not. + */ + virtual bool dispatch() = 0; +}; + + +/** + * \brief Describes an element that manages a file descriptor. + * + * The watch is ready to be dispatched whenever it's managed file descriptor + * has events in it's revents-field. + * + * It is possible that there are DispatchSources of which the dispatch readiness + * directly depends on the dispatching of the watch. If this is the case, such + * DispatchSources can be retrieved from this Watch. + */ +struct Watch { + virtual ~Watch() {} + + /** + * \brief Dispatches the watch. + * + * Should only be called once the associated file descriptor has events ready. + * + * @param eventFlags The events that shall be retrieved from the file descriptor. + */ + virtual void dispatch(unsigned int eventFlags) = 0; + + /** + * \brief Returns the file descriptor that is managed by this watch. + * + * @return The associated file descriptor. + */ + virtual const pollfd& getAssociatedFileDescriptor() = 0; + + /** + * \brief Returns a vector of all dispatch sources that depend on the watched file descriptor. + * + * The returned vector will not be empty if and only if there are any sources + * that depend on availability of data of the watched file descriptor. Whenever this + * Watch is dispatched, those sources likely also need to be dispatched. + */ + virtual const std::vector<DispatchSource*>& getDependentDispatchSources() = 0; +}; + + +constexpr int64_t TIMEOUT_INFINITE = std::numeric_limits<int64_t>::max(); +constexpr int64_t TIMEOUT_NONE = 0; + + +/** + * \brief Describes a basic timeout. + * + * Timeouts will be taken into consideration when waiting in a call to poll + * for a file descriptor to become ready. When the lowest known timeout expires, + * the call to poll will return, regardless of whether a file descriptor was ready + * or not. + */ +struct Timeout { + virtual ~Timeout() {} + + /** + * Needs to be called when this timeout is expired. + * + * @return 'true' if the timeout shall be rescheduled, 'false' if it shall be removed. + */ + virtual bool dispatch() = 0; + + /** + * \brief The timeout interval in milliseconds. + * + * Returns TIMEOUT_INFINITE for "dispatch never", TIMEOUT_NONE for "dispatch immediately", + * or any positive value as an interval of time in milliseconds that needs to pass before + * this timeout is to be dispatched. + */ + virtual int64_t getTimeoutInterval() const = 0; + + /** + * \brief Returns the point in time at which this timeout needs to be dispatched next. + * + * After a initialization and after each dispatch, this timeout will re-calculate it's next + * ready time. This value may be ignored if a different mechanism for monitoring timeout intervals + * is used. + */ + virtual int64_t getReadyTime() const = 0; +}; + + +typedef std::function<void(DispatchSource*, const DispatchPriority)> DispatchSourceAddedCallback; +typedef std::function<void(DispatchSource*)> DispatchSourceRemovedCallback; +typedef std::function<void(Watch*, const DispatchPriority)> WatchAddedCallback; +typedef std::function<void(Watch*)> WatchRemovedCallback; +typedef std::function<void(Timeout*, const DispatchPriority)> TimeoutSourceAddedCallback; +typedef std::function<void(Timeout*)> TimeoutSourceRemovedCallback; +typedef std::function<void()> WakeupCallback; + +typedef std::list<std::pair<DispatchSourceAddedCallback, DispatchSourceRemovedCallback>> DispatchSourceListenerList; +typedef std::list<std::pair<WatchAddedCallback, WatchRemovedCallback>> WatchListenerList; +typedef std::list<std::pair<TimeoutSourceAddedCallback, TimeoutSourceRemovedCallback>> TimeoutSourceListenerList; +typedef std::list<WakeupCallback> WakeupListenerList; + +typedef DispatchSourceListenerList::iterator DispatchSourceListenerSubscription; +typedef WatchListenerList::iterator WatchListenerSubscription; +typedef TimeoutSourceListenerList::iterator TimeoutSourceListenerSubscription; +typedef WakeupListenerList::iterator WakeupListenerSubscription; + + +/** + * \brief Provides hooks for your Main Loop implementation. + * + * By registering callbacks with this class, you will be notified about all DispatchSources, + * Watches, Timeouts and Wakeup-Events that need to be handled by your Main Loop implementation. + * + */ +class MainLoopContext { + public: + MainLoopContext() {} + MainLoopContext(const MainLoopContext&) = delete; + MainLoopContext& operator=(const MainLoopContext&) = delete; + MainLoopContext(MainLoopContext&&) = delete; + MainLoopContext& operator=(MainLoopContext&&) = delete; + + /** + * \brief Registers for all DispatchSources that are added or removed. + */ + inline DispatchSourceListenerSubscription subscribeForDispatchSources(DispatchSourceAddedCallback dispatchAddedCallback, DispatchSourceRemovedCallback dispatchRemovedCallback) { + dispatchSourceListeners_.emplace_front(dispatchAddedCallback, dispatchRemovedCallback); + return dispatchSourceListeners_.begin(); + } + + /** + * \brief Registers for all Watches that are added or removed. + */ + inline WatchListenerSubscription subscribeForWatches(WatchAddedCallback watchAddedCallback, WatchRemovedCallback watchRemovedCallback) { + watchListeners_.emplace_front(watchAddedCallback, watchRemovedCallback); + return watchListeners_.begin(); + } + + /** + * \brief Registers for all Timeouts that are added or removed. + */ + inline TimeoutSourceListenerSubscription subscribeForTimeouts(TimeoutSourceAddedCallback timeoutAddedCallback, TimeoutSourceRemovedCallback timeoutRemovedCallback) { + timeoutSourceListeners_.emplace_front(timeoutAddedCallback, timeoutRemovedCallback); + return timeoutSourceListeners_.begin(); + } + + /** + * \brief Registers for all Wakeup-Events that need to interrupt a call to "poll". + */ + inline WakeupListenerSubscription subscribeForWakeupEvents(WakeupCallback wakeupCallback) { + wakeupListeners_.emplace_front(wakeupCallback); + return wakeupListeners_.begin(); + } + + /** + * \brief Unsubscribes your listeners for DispatchSources. + */ + inline void unsubscribeForDispatchSources(DispatchSourceListenerSubscription subscription) { + dispatchSourceListeners_.erase(subscription); + } + + /** + * \brief Unsubscribes your listeners for Watches. + */ + inline void unsubscribeForWatches(WatchListenerSubscription subscription) { + watchListeners_.erase(subscription); + } + + /** + * \brief Unsubscribes your listeners for Timeouts. + */ + inline void unsubscribeForTimeouts(TimeoutSourceListenerSubscription subscription) { + timeoutSourceListeners_.erase(subscription); + } + + /** + * \brief Unsubscribes your listeners for Wakeup-Events. + */ + inline void unsubscribeForWakeupEvents(WakeupListenerSubscription subscription) { + wakeupListeners_.erase(subscription); + } + + /** + * \brief Notifies all listeners about a new DispatchSource. + */ + inline void registerDispatchSource(DispatchSource* dispatchSource, const DispatchPriority dispatchPriority = DispatchPriority::DEFAULT) { + for(auto listener = dispatchSourceListeners_.begin(); listener != dispatchSourceListeners_.end(); ++listener) { + listener->first(dispatchSource, dispatchPriority); + } + } + + /** + * \brief Notifies all listeners about the removal of a DispatchSource. + */ + inline void deregisterDispatchSource(DispatchSource* dispatchSource) { + for(auto listener = dispatchSourceListeners_.begin(); listener != dispatchSourceListeners_.end(); ++listener) { + listener->second(dispatchSource); + } + } + + /** + * \brief Notifies all listeners about a new Watch. + */ + inline void registerWatch(Watch* watch, const DispatchPriority dispatchPriority = DispatchPriority::DEFAULT) { + for(auto listener = watchListeners_.begin(); listener != watchListeners_.end(); ++listener) { + listener->first(watch, dispatchPriority); + } + } + + /** + * \brief Notifies all listeners about the removal of a Watch. + */ + inline void deregisterWatch(Watch* watch) { + for(auto listener = watchListeners_.begin(); listener != watchListeners_.end(); ++listener) { + listener->second(watch); + } + } + + /** + * \brief Notifies all listeners about a new Timeout. + */ + inline void registerTimeoutSource(Timeout* timeoutEvent, const DispatchPriority dispatchPriority = DispatchPriority::DEFAULT) { + for(auto listener = timeoutSourceListeners_.begin(); listener != timeoutSourceListeners_.end(); ++listener) { + listener->first(timeoutEvent, dispatchPriority); + } + } + + /** + * \brief Notifies all listeners about the removal of a Timeout. + */ + inline void deregisterTimeoutSource(Timeout* timeoutEvent) { + for(auto listener = timeoutSourceListeners_.begin(); listener != timeoutSourceListeners_.end(); ++listener) { + listener->second(timeoutEvent); + } + } + + /** + * \brief Notifies all listeners about a wakeup event that just happened. + */ + inline void wakeup() { + for(auto listener = wakeupListeners_.begin(); listener != wakeupListeners_.end(); ++listener) { + (*listener)(); + } + } + + private: + DispatchSourceListenerList dispatchSourceListeners_; + WatchListenerList watchListeners_; + TimeoutSourceListenerList timeoutSourceListeners_; + WakeupListenerList wakeupListeners_; +}; + + +} // namespace CommonAPI + + +#endif /* MAIN_LOOP_CONTEXT_H_ */ diff --git a/src/CommonAPI/Runtime.cpp b/src/CommonAPI/Runtime.cpp index 2ab8bf7..d7185df 100644 --- a/src/CommonAPI/Runtime.cpp +++ b/src/CommonAPI/Runtime.cpp @@ -53,4 +53,9 @@ std::shared_ptr<Runtime> Runtime::load(const std::string& middlewareName) { } +std::shared_ptr<MainLoopContext> Runtime::getNewMainLoopContext() const { + return std::make_shared<MainLoopContext>(); } + + +} // namespace CommonAPI diff --git a/src/CommonAPI/Runtime.h b/src/CommonAPI/Runtime.h index 154cb67..e52f740 100644 --- a/src/CommonAPI/Runtime.h +++ b/src/CommonAPI/Runtime.h @@ -10,6 +10,7 @@ #include "MiddlewareInfo.h" #include "Factory.h" +#include "MainLoopContext.h" #include <memory> #include <fstream> @@ -25,6 +26,7 @@ namespace CommonAPI { class Factory; class Runtime; +class MainLoopContext; /** * \brief Represents the CommonAPI runtime bindings available. @@ -58,6 +60,17 @@ class Runtime { static void registerRuntimeLoader(std::string middlewareName, MiddlewareRuntimeLoadFunction middlewareRuntimeLoadFunction); virtual ~Runtime() {} + + /** + * \brief Returns new MainLoopContext + * + * Creates and returns a new MainLoopContext object. This context can be used to take + * complete control over the order and time of execution of the abstract middleware + * dispatching mechanism. + * + * @return A new MainLoopContext object + */ + std::shared_ptr<MainLoopContext> getNewMainLoopContext() const; /** * \brief Create a factory for the loaded runtime * @@ -65,7 +78,7 @@ class Runtime { * * @return Factory object for the loaded runtime */ - virtual std::shared_ptr<Factory> createFactory() = 0; + virtual std::shared_ptr<Factory> createFactory(std::shared_ptr<MainLoopContext> = std::shared_ptr<MainLoopContext>(NULL)) = 0; }; |