/** * SPDX license identifier: MPL-2.0 * * Copyright (C) 2012, BMW AG * * \author Christian Linke, christian.linke@bmw.de BMW 2011,2012 * \author Aleksandar Donchev, aleksander.donchev@partner.bmw.de BMW 2017 * * \copyright * 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/. * * \file CAmSocketHandler.h * For further information see http://www.genivi.org/. */ #ifndef SOCKETHANDLER_H_ #define SOCKETHANDLER_H_ #include #include #include #include #include #include #include #include #include #include #include #include "audiomanagertypes.h" #ifdef WITH_TIMERFD # include # include # include # include # include #endif // ifdef WITH_TIMERFD namespace am { #define MAX_NS 1000000000L #define MAX_TIMERHANDLE UINT16_MAX #define MAX_POLLHANDLE UINT16_MAX typedef uint16_t sh_pollHandle_t; //!< this is a handle for a filedescriptor to be used with the SocketHandler typedef sh_pollHandle_t sh_timerHandle_t; //!< this is a handle for a timer to be used with the SocketHandler /** * prototype for poll prepared callback */ class IAmShPollPrepare { public: virtual void Call(const sh_pollHandle_t handle, void *userData) = 0; virtual ~IAmShPollPrepare() {} }; /** * prototype for poll fired callback */ class IAmShPollFired { public: virtual void Call(const pollfd pollfd, const sh_pollHandle_t handle, void *userData) = 0; virtual ~IAmShPollFired() {} }; /** * prototype for poll check callback */ class IAmShPollCheck { public: virtual bool Call(const sh_pollHandle_t handle, void *userData) = 0; virtual ~IAmShPollCheck() {} }; /** * prototype for dispatch callback */ class IAmShPollDispatch { public: virtual bool Call(const sh_pollHandle_t handle, void *userData) = 0; virtual ~IAmShPollDispatch() {} }; /** * prototype for the timer callback */ class IAmShTimerCallBack { public: IAmShTimerCallBack(){} virtual void Call(const sh_timerHandle_t handle, void *userData) = 0; virtual ~IAmShTimerCallBack(){} }; /**make private, not public * template for a callback */ template class TAmShPollFired : public IAmShPollFired { private: TClass *mInstance; void (TClass::*mFunction)(const pollfd pollfd, const sh_pollHandle_t handle, void *userData); public: TAmShPollFired(TClass *instance, void (TClass::*function)(const pollfd pollfd, const sh_pollHandle_t handle, void *userData)) : mInstance(instance) , mFunction(function) {} virtual void Call(const pollfd pollfd, const sh_pollHandle_t handle, void *userData) { (*mInstance.*mFunction)(pollfd, handle, userData); } }; /** * template for a callback */ template class TAmShPollCheck : public IAmShPollCheck { private: TClass *mInstance; bool (TClass::*mFunction)(const sh_pollHandle_t handle, void *userData); public: TAmShPollCheck(TClass *instance, bool (TClass::*function)(const sh_pollHandle_t handle, void *userData)) : mInstance(instance) , mFunction(function) {} virtual bool Call(const sh_pollHandle_t handle, void *userData) { return ((*mInstance.*mFunction)(handle, userData)); } }; /** * template for a callback */ template class TAmShPollDispatch : public IAmShPollDispatch { private: TClass *mInstance; bool (TClass::*mFunction)(const sh_pollHandle_t handle, void *userData); public: TAmShPollDispatch(TClass *instance, bool (TClass::*function)(const sh_pollHandle_t handle, void *userData)) : mInstance(instance) , mFunction(function) {} virtual bool Call(const sh_pollHandle_t handle, void *userData) { return ((*mInstance.*mFunction)(handle, userData)); } }; /** * template to create the functor for a class */ template class TAmShTimerCallBack : public IAmShTimerCallBack { private: TClass *mInstance; void (TClass::*mFunction)(sh_timerHandle_t handle, void *userData); public: TAmShTimerCallBack(TClass *instance, void (TClass::*function)(sh_timerHandle_t handle, void *userData)) : IAmShTimerCallBack() , mInstance(instance) , mFunction(function) {} virtual void Call(sh_timerHandle_t handle, void *userData) { (*mInstance.*mFunction)(handle, userData); } }; /** * template for a callback */ template class TAmShPollPrepare : public IAmShPollPrepare { private: TClass *mInstance; void (TClass::*mFunction)(const sh_pollHandle_t handle, void *userData); public: TAmShPollPrepare(TClass *instance, void (TClass::*function)(const sh_pollHandle_t handle, void *userData)) : mInstance(instance) , mFunction(function) {} virtual void Call(const sh_pollHandle_t handle, void *userData) { (*mInstance.*mFunction)(handle, userData); } }; /** * The am::CAmSocketHandler implements a mainloop for the AudioManager. Plugins and different parts of the AudioManager add their filedescriptors to the handler * to get called on communication of the filedescriptors.\n * More information can be found here : \ref mainl */ class CAmSocketHandler { typedef enum : uint8_t { ADD = 0u, // new, uninitialized element which needs to be added to ppoll array UPDATE = 1u, // update of event information therefore update ppoll array VALID = 2u, // it is a valid element in ppoll array REMOVE = 3u, // remove from ppoll array and internal map INVALID = 4u // uninit element requested to be removed from internal map only } poll_states_e; struct sh_poll_s //!< struct that holds information about polls { sh_pollHandle_t handle; //!< handle to uniquely adress a filedesriptor pollfd pollfdValue; //!< the array for polling the filedescriptors std::function prepareCB; // preperation callback std::function firedCB; // fired callback std::function checkCB; // check callback std::function dispatchCB; // dispatch callback void *userData; poll_states_e state; sh_poll_s() : handle(0) , pollfdValue() , prepareCB() , firedCB() , checkCB() , dispatchCB() , userData(0) , state(ADD) {} }; struct sh_timer_s //!< struct that holds information of timers { sh_timerHandle_t handle; //!< the handle of the timer #ifdef WITH_TIMERFD int fd; itimerspec countdown; //!< the countdown, this value is decreased every time the timer is up #else timespec countdown; //!< the countdown, this value is decreased every time the timer is up #endif std::function callback; // timer callback void *userData; sh_timer_s() : handle(0) #ifdef WITH_TIMERFD , fd(-1) #endif , countdown() , callback() , userData(0) {} }; struct sh_signal_s { sh_pollHandle_t handle; //!< handle to uniquely adress a filedesriptor std::function callback; void *userData; sh_signal_s() : handle(0) , callback() , userData(0) {} }; struct sh_identifier_s { std::set pollHandles; uint16_t limit; uint16_t lastUsedID; sh_identifier_s(const uint16_t pollLimit = UINT16_MAX) : pollHandles() , limit(pollLimit) , lastUsedID(0) {} }; typedef std::reverse_iterator rListTimerIter; //!< typedef for reverseiterator on timer lists typedef std::vector VectorPollfd_t; //!< vector of filedescriptors typedef std::map MapShPoll_t; //!< list for the callbacks typedef std::vector VectorSignalHandlers_t; //!< list for the callbacks typedef enum : uint8_t { NO_ERROR = 0u, // OK FD_ERROR = 1u, // Invalid file descriptor MT_ERROR = 2u // Multi-thread issue } internal_codes_e; typedef uint8_t internal_codes_t; int mEventFd; int mSignalFd; bool mDispatchDone; // this starts / stops the mainloop MapShPoll_t mMapShPoll; //!< list that holds all information for the ppoll sh_identifier_s mSetPollKeys; //! A set of all used ppoll keys sh_identifier_s mSetTimerKeys; //! A set of all used timer keys std::list mListTimer; //!< list of all timers #ifndef WITH_TIMERFD std::list mListActiveTimer; //!< list of all currently active timers #endif sh_identifier_s mSetSignalhandlerKeys; //! A set of all used signal handler keys VectorSignalHandlers_t mSignalHandlers; internal_codes_t mInternalCodes; #ifndef WITH_TIMERFD timespec mStartTime; //!< here the actual time is saved for timecorrection #endif private: bool fdIsValid(const int fd) const; void wakeupWorker(const std::string &func, const uint64_t value = 1u); timespec *insertTime(timespec &buffertime); #ifdef WITH_TIMERFD am_Error_e createTimeFD(const itimerspec &timeouts, int &fd); #else void timerUp(); void timerCorrection(); /** * compares countdown values * @param a * @param b * @return true if b greater a */ inline static bool compareCountdown(const sh_timer_s &a, const sh_timer_s &b) { return ((a.countdown.tv_sec == b.countdown.tv_sec) ? (a.countdown.tv_nsec < b.countdown.tv_nsec) : (a.countdown.tv_sec < b.countdown.tv_sec)); } /** * Subtracts b from a * @param a * @param b * @return subtracted value */ inline static timespec timespecSub(const timespec &a, const timespec &b) { timespec result; if ((a.tv_sec < b.tv_sec) || ((a.tv_sec == b.tv_sec) && (a.tv_nsec <= b.tv_nsec))) { result.tv_sec = result.tv_nsec = 0; } else { result.tv_sec = a.tv_sec - b.tv_sec; if (a.tv_nsec < b.tv_nsec) { result.tv_nsec = a.tv_nsec + MAX_NS - b.tv_nsec; result.tv_sec--; /* Borrow a second. */ } else { result.tv_nsec = a.tv_nsec - b.tv_nsec; } } return (result); } /** * adds timespec values * @param a * @param b * @return the added values */ inline timespec timespecAdd(const timespec &a, const timespec &b) { timespec result; result.tv_sec = a.tv_sec + b.tv_sec; result.tv_nsec = a.tv_nsec + b.tv_nsec; if (result.tv_nsec >= MAX_NS) { result.tv_sec++; result.tv_nsec = result.tv_nsec - MAX_NS; } return (result); } /** * comapares timespec values * @param a * @param b * @return */ inline int timespecCompare(const timespec &a, const timespec &b) { // less if (a.tv_sec < b.tv_sec) { return (-1); } // greater else if (a.tv_sec > b.tv_sec) { return (1); } // less else if (a.tv_nsec < b.tv_nsec) { return (-1); } // greater else if (a.tv_nsec > b.tv_nsec) { return (1); } // equal return (0); } #endif // ifdef WITH_TIMERFD /** * functor to prepare all fire events * @param a * @return */ inline static void prepare(sh_poll_s &row); /** * functor to return all fired events * @param a * @return */ inline static void fire(const sh_poll_s &a); /** * functor to help find the items that do not need dispatching * @param a * @return */ inline static bool noDispatching(const sh_poll_s *a); /** * checks if dispatching is already finished * @param a * @return */ inline static bool dispatchingFinished(const sh_poll_s *a); /** * timer fire callback * @param a * @return */ inline static void callTimer(sh_timer_s &a); /** * next handle id * @param std::set handles * @return handle */ bool nextHandle(sh_identifier_s &handle); public: CAmSocketHandler(); ~CAmSocketHandler(); /** * install the signal fd */ am_Error_e listenToSignals(const std::vector &listSignals); am_Error_e addFDPoll(const int fd, const short event, std::function prepare, std::function fired, std::function check, std::function dispatch, void *userData, sh_pollHandle_t &handle); am_Error_e addFDPoll(const int fd, const short event, IAmShPollPrepare *prepare, IAmShPollFired *fired, IAmShPollCheck *check, IAmShPollDispatch *dispatch, void *userData, sh_pollHandle_t &handle); am_Error_e removeFDPoll(const sh_pollHandle_t handle); am_Error_e updateEventFlags(const sh_pollHandle_t handle, const short events); am_Error_e addSignalHandler(std::function callback, sh_pollHandle_t &handle, void *userData); am_Error_e removeSignalHandler(const sh_pollHandle_t handle); am_Error_e addTimer(const timespec &timeouts, IAmShTimerCallBack *callback, sh_timerHandle_t & handle, void *userData, #ifndef WITH_TIMERFD const bool __attribute__((__unused__)) repeats = false #else const bool repeats = false #endif ); am_Error_e addTimer(const timespec &timeouts, std::function callback, sh_timerHandle_t & handle, void *userData, #ifndef WITH_TIMERFD const bool __attribute__((__unused__)) repeats = false #else const bool repeats = false #endif ); am_Error_e removeTimer(const sh_timerHandle_t handle); am_Error_e restartTimer(const sh_timerHandle_t handle); am_Error_e updateTimer(const sh_timerHandle_t handle, const timespec &timeouts); am_Error_e stopTimer(const sh_timerHandle_t handle); void start_listenting(); void stop_listening(); void exit_mainloop(); bool fatalErrorOccurred(); }; } /* namespace am */ #endif /* SOCKETHANDLER_H_ */