diff options
author | Christian Linke <christian.linke@bmw.de> | 2015-03-12 13:43:48 +0100 |
---|---|---|
committer | Christian Linke <christian.linke@bmw.de> | 2015-03-12 13:43:48 +0100 |
commit | 6ffbf25bacf589879198e49ac227cb56e261b7c9 (patch) | |
tree | e6b3ca48d22d06d43da638b1998ab1220ec7ab08 /AudioManagerUtilities/src/CAmSocketHandler.cpp | |
parent | c628fa5304171cd827f56aa475668d99d26ad28a (diff) | |
download | audiomanager-6ffbf25bacf589879198e49ac227cb56e261b7c9.tar.gz |
* fully rework. Plugins have now a new own git repository
* building out of source of plugins without AM workss
* build system cmake contructions is different now
Signed-off-by: Christian Linke <christian.linke@bmw.de>
Diffstat (limited to 'AudioManagerUtilities/src/CAmSocketHandler.cpp')
-rw-r--r-- | AudioManagerUtilities/src/CAmSocketHandler.cpp | 546 |
1 files changed, 546 insertions, 0 deletions
diff --git a/AudioManagerUtilities/src/CAmSocketHandler.cpp b/AudioManagerUtilities/src/CAmSocketHandler.cpp new file mode 100644 index 0000000..2e8806a --- /dev/null +++ b/AudioManagerUtilities/src/CAmSocketHandler.cpp @@ -0,0 +1,546 @@ +/** + * Copyright (C) 2012, BMW AG + * + * This file is part of GENIVI Project AudioManager. + * + * Contributions are licensed to the GENIVI Alliance under one or more + * Contribution License Agreements. + * + * \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/. + * + * + * \author Christian Linke, christian.linke@bmw.de BMW 2011,2012 + * + * \file CAmSocketHandler.cpp + * For further information see http://www.genivi.org/. + * + */ + +#include "CAmSocketHandler.h" +//#include <config.h> +#include <cassert> +#include <sys/fcntl.h> +#include <sys/errno.h> +#include <sys/poll.h> +#include <time.h> +#include <algorithm> +#include <features.h> +#include <csignal> +#include <unistd.h> +#include "CAmDltWrapper.h" + +namespace am +{ + +CAmSocketHandler::CAmSocketHandler() : + receiverCallbackT(this, &CAmSocketHandler::receiverCallback),// + checkerCallbackT(this, &CAmSocketHandler::checkerCallback),// + mPipe(), // + mDispatchDone(1),// + mListPoll(), // + mListTimer(), // + mListActiveTimer(), // + mLastInsertedHandle(0), // + mLastInsertedPollHandle(0), // + mRecreatePollfds(true), // + mStartTime() // +{ + if (pipe(mPipe) == -1) + { + logError("CAmSerializer could not create pipe!"); + } + + //add the pipe to the poll - nothing needs to be proccessed here we just need the pipe to trigger the ppoll + short event = 0; + sh_pollHandle_t handle; + event |= POLLIN; + addFDPoll(mPipe[0], event, NULL, &receiverCallbackT, &checkerCallbackT, NULL, NULL, handle); +} + +CAmSocketHandler::~CAmSocketHandler() +{ +} + +//todo: maybe have some: give me more time returned? +/** + * start the block listening for filedescriptors. This is the mainloop. + */ +void CAmSocketHandler::start_listenting() +{ + mDispatchDone = 0; + int16_t pollStatus; + + //prepare the signalmask + sigset_t sigmask; + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGINT); + sigaddset(&sigmask, SIGQUIT); + sigaddset(&sigmask, SIGTERM); + sigaddset(&sigmask, SIGHUP); + sigaddset(&sigmask, SIGQUIT); + + clock_gettime(CLOCK_MONOTONIC, &mStartTime); + while (!mDispatchDone) + { + //first we go through the registered filedescriptors and check if someone needs preparation: + std::for_each(mListPoll.begin(), mListPoll.end(), CAmShCallPrep()); + + if (mRecreatePollfds) + { + mfdPollingArray.clear(); + //there was a change in the setup, so we need to recreate the fdarray from the list + std::for_each(mListPoll.begin(), mListPoll.end(), CAmShCopyPollfd(mfdPollingArray)); + mRecreatePollfds = false; + } + + timerCorrection(); + + //block until something is on a filedescriptor + + timespec buffertime; + if ((pollStatus = ppoll(&mfdPollingArray[0], mfdPollingArray.size(), insertTime(buffertime), &sigmask)) < 0) + { + if (errno == EINTR) + { + //a signal was received, that means it's time to go... + pollStatus = 0; + } + else + { + logError("SocketHandler::start_listenting ppoll returned with error", errno); + exit(0); + } + } + + if (pollStatus != 0) //only check filedescriptors if there was a change + { + //todo: here could be a timer that makes sure naughty plugins return! + + //freeze mListPoll by copying it - otherwise we get problems when we want to manipulate it during the next lines + std::list<sh_poll_s> listPoll; + mListPoll_t::iterator listmPollIt; + + //remove all filedescriptors who did not fire + std::vector<pollfd>::iterator it = mfdPollingArray.begin(); + do + { + it = std::find_if(it, mfdPollingArray.end(), eventFired); + if (it != mfdPollingArray.end()) + { + listmPollIt = mListPoll.begin(); + std::advance(listmPollIt, std::distance(mfdPollingArray.begin(), it)); + listPoll.push_back(*listmPollIt); + listPoll.back().pollfdValue = *it; + it++; + } + } while (it != mfdPollingArray.end()); + + //stage 1, call firedCB + std::for_each(listPoll.begin(), listPoll.end(), CAmShCallFire()); + + //stage 2, lets ask around if some dispatching is necessary, the ones who need stay on the list + listPoll.remove_if(noDispatching); + + //stage 3, the ones left need to dispatch, we do this as long as there is something to dispatch.. + do + { + listPoll.remove_if(dispatchingFinished); + } while (!listPoll.empty()); + + } + else //Timerevent + { + //this was a timer event, we need to take care about the timers + timerUp(); + } + } +} + +/** + * exits the loop + */ +void CAmSocketHandler::stop_listening() +{ + mDispatchDone = 1; + + //this is for all running timers only - we need to handle the additional offset here + if (!mListActiveTimer.empty()) + { + timespec currentTime, correctionTime; + clock_gettime(CLOCK_MONOTONIC, ¤tTime); + correctionTime = timespecSub(currentTime, mStartTime); + std::for_each(mListActiveTimer.begin(), mListActiveTimer.end(), CAmShSubstractTime(correctionTime)); + } + +} + +/** + * Adds a filedescriptor to the polling loop + * @param fd the filedescriptor + * @param event the event flags + * @param prepare a callback that is called before the loop is entered + * @param fired a callback that is called when the filedescriptor needs to be read + * @param check a callback that is called to check if further actions are neccessary + * @param dispatch a callback that is called to dispatch the received data + * @param userData a pointer to userdata that is always passed around + * @param handle the handle of this poll + * @return E_OK if the descriptor was added, E_NON_EXISTENT if the fd is not valid + */ +am_Error_e CAmSocketHandler::addFDPoll(const int fd, const short event, IAmShPollPrepare *prepare, IAmShPollFired *fired, IAmShPollCheck *check, IAmShPollDispatch *dispatch, void *userData, sh_pollHandle_t & handle) +{ + if (!fdIsValid(fd)) + return (E_NON_EXISTENT); + + sh_poll_s pollData; + pollData.pollfdValue.fd = fd; + pollData.handle = ++mLastInsertedPollHandle; + pollData.pollfdValue.events = event; + pollData.pollfdValue.revents = 0; + pollData.userData = userData; + pollData.prepareCB = prepare; + pollData.firedCB = fired; + pollData.checkCB = check; + pollData.dispatchCB = dispatch; + + //add new data to the list + mListPoll.push_back(pollData); + + mRecreatePollfds = true; + + handle = pollData.handle; + return (E_OK); +} + +/** + * removes a filedescriptor from the poll loop + * @param handle + * @return + */ +am_Error_e CAmSocketHandler::removeFDPoll(const sh_pollHandle_t handle) +{ + mListPoll_t::iterator iterator = mListPoll.begin(); + + for (; iterator != mListPoll.end(); ++iterator) + { + if (iterator->handle == handle) + { + iterator = mListPoll.erase(iterator); + mRecreatePollfds = true; + return (E_OK); + } + } + return (E_UNKNOWN); +} + +/** + * adds a timer to the list of timers. The callback will be fired when the timer is up. + * This is not a high precise timer, it is very coarse. It is meant to be used for timeouts when waiting + * for an answer via a filedescriptor. + * One time timer. If you need again a timer, you need to add a new timer in the callback of the old one. + * @param timeouts timeouts time until the callback is fired + * @param callback callback the callback + * @param handle handle the handle that is created for the timer is returned. Can be used to remove the timer + * @param userData pointer always passed with the call + * @return E_OK in case of success + */ +am_Error_e CAmSocketHandler::addTimer(const timespec timeouts, IAmShTimerCallBack* callback, sh_timerHandle_t& handle, void * userData) +{ + assert(!((timeouts.tv_sec==0) && (timeouts.tv_nsec==0))); + assert(callback!=NULL); + + sh_timer_s timerItem; + + //create a new handle for the timer + handle = ++mLastInsertedHandle; //todo: overflow ruling !o + timerItem.handle = handle; + timerItem.countdown = timeouts; + timerItem.callback = callback; + timerItem.userData = userData; + + mListTimer.push_back(timerItem); + + //we add here the time difference between startTime and currenttime, because this time will be substracted later on in timecorrection + timespec currentTime; + clock_gettime(CLOCK_MONOTONIC, ¤tTime); + if (!mDispatchDone) //the mainloop is started + timerItem.countdown = timespecAdd(timeouts, timespecSub(currentTime, mStartTime)); + + mListActiveTimer.push_back(timerItem); + mListActiveTimer.sort(compareCountdown); + return (E_OK); +} + +/** + * removes a timer from the list of timers + * @param handle the handle to the timer + * @return E_OK in case of success, E_UNKNOWN if timer was not found. + */ +am_Error_e CAmSocketHandler::removeTimer(const sh_timerHandle_t handle) +{ + assert(handle!=0); + + //stop the current timer + stopTimer(handle); + + std::list<sh_timer_s>::iterator it(mListTimer.begin()); + for (; it != mListTimer.end(); ++it) + { + if (it->handle == handle) + { + it = mListTimer.erase(it); + return (E_OK); + } + } + return (E_UNKNOWN); +} + +/** + * restarts a timer and updates with a new interva + * @param handle handle to the timer + * @param timeouts new timout time + * @return E_OK on success, E_NON_EXISTENT if the handle was not found + */ +am_Error_e CAmSocketHandler::updateTimer(const sh_timerHandle_t handle, const timespec timeouts) +{ + //update the mList .... + sh_timer_s timerItem; + std::list<sh_timer_s>::iterator it(mListTimer.begin()), activeIt(mListActiveTimer.begin()); + bool found(false); + for (; it != mListTimer.end(); ++it) + { + if (it->handle == handle) + { + it->countdown = timeouts; + timerItem = *it; + found = true; + break; + } + } + if (!found) + return (E_NON_EXISTENT); + + found = false; + + //we add here the time difference between startTime and currenttime, because this time will be substracted later on in timecorrection + timespec currentTime, timeoutsCorrected; + currentTime.tv_nsec=timeoutsCorrected.tv_nsec=0; + currentTime.tv_sec=timeoutsCorrected.tv_sec=0; + clock_gettime(CLOCK_MONOTONIC, ¤tTime); + if (!mDispatchDone) //the mainloop is started + timeoutsCorrected = timespecAdd(timeouts, timespecSub(currentTime, mStartTime)); + + for (; activeIt != mListActiveTimer.end(); ++activeIt) + { + if (activeIt->handle == handle) + { + activeIt->countdown = timeoutsCorrected; + found = true; + break; + } + } + + if (!found) + timerItem.countdown = timeoutsCorrected; + mListActiveTimer.push_back(timerItem); + + mListActiveTimer.sort(compareCountdown); + return (E_OK); +} + +/** + * restarts a timer with the original value + * @param handle + * @return E_OK on success, E_NON_EXISTENT if the handle was not found + */ +am_Error_e CAmSocketHandler::restartTimer(const sh_timerHandle_t handle) +{ + sh_timer_s timerItem; //!<the original timer value + //find the original value + std::list<sh_timer_s>::iterator it(mListTimer.begin()), activeIt(mListActiveTimer.begin()); + bool found(false); + for (; it != mListTimer.end(); ++it) + { + if (it->handle == handle) + { + timerItem = *it; + found = true; + break; + } + } + if (!found) + return (E_NON_EXISTENT); + + found = false; + + //we add here the time difference between startTime and currenttime, because this time will be substracted later on in timecorrection + timespec currentTime, timeoutsCorrected; + clock_gettime(CLOCK_MONOTONIC, ¤tTime); + if (!mDispatchDone) //the mainloop is started + { + timeoutsCorrected = timespecAdd(timerItem.countdown, timespecSub(currentTime, mStartTime)); + timerItem.countdown = timeoutsCorrected; + } + + for (; activeIt != mListActiveTimer.end(); ++activeIt) + { + if (activeIt->handle == handle) + { + activeIt->countdown = timerItem.countdown; + found = true; + break; + } + } + + if (!found) + mListActiveTimer.push_back(timerItem); + + mListActiveTimer.sort(compareCountdown); + + return (E_OK); +} + +/** + * stops a timer + * @param handle + * @return E_OK on success, E_NON_EXISTENT if the handle was not found + */ +am_Error_e CAmSocketHandler::stopTimer(const sh_timerHandle_t handle) +{ + //go through the list and remove the timer with the handle + std::list<sh_timer_s>::iterator it(mListActiveTimer.begin()); + for (; it != mListActiveTimer.end(); ++it) + { + if (it->handle == handle) + { + it = mListActiveTimer.erase(it); + return (E_OK); + } + } + return (E_NON_EXISTENT); +} + +/** + * updates the eventFlags of a poll + * @param handle + * @param events + * @return @return E_OK on succsess, E_NON_EXISTENT if fd was not found + */ +am_Error_e CAmSocketHandler::updateEventFlags(const sh_pollHandle_t handle, const short events) +{ + mListPoll_t::iterator iterator = mListPoll.begin(); + + for (; iterator != mListPoll.end(); ++iterator) + { + if (iterator->handle == handle) + { + iterator->pollfdValue.events = events; + mRecreatePollfds = true; + return (E_OK); + } + } + return (E_UNKNOWN); +} + +/** + * checks if a filedescriptor is validCAmShSubstractTime + * @param fd the filedescriptor + * @return true if the fd is valid + */ +bool CAmSocketHandler::fdIsValid(const int fd) const +{ + return (fcntl(fd, F_GETFL) != -1 || errno != EBADF); +} + +/** + * timer is up. + */ +void CAmSocketHandler::timerUp() +{ + //find out the timedifference to starttime + timespec currentTime, diffTime; + clock_gettime(CLOCK_MONOTONIC, ¤tTime); + diffTime = timespecSub(currentTime, mStartTime); + + //now we need to substract the diffTime from all timers and see if they are up + std::list<sh_timer_s>::reverse_iterator overflowIter = std::find_if(mListActiveTimer.rbegin(), mListActiveTimer.rend(), CAmShCountdownUp(diffTime)); + + //copy all fired timers into a list + std::vector<sh_timer_s> tempList(overflowIter, mListActiveTimer.rend()); + + //erase all fired timers + std::list<sh_timer_s>::iterator it(overflowIter.base()); + mListActiveTimer.erase(mListActiveTimer.begin(), it); + + //call the callbacks for the timers + std::for_each(tempList.begin(), tempList.end(), CAmShCallTimer()); +} + +/** + * correct timers and fire the ones who are up + */ +void CAmSocketHandler::timerCorrection() +{ + //get the current time and calculate the correction value + timespec currentTime, correctionTime; + clock_gettime(CLOCK_MONOTONIC, ¤tTime); + correctionTime = timespecSub(currentTime, mStartTime); + mStartTime = currentTime; + + if (!mListActiveTimer.empty()) + { + + //subtract the correction value from all items in the list + std::for_each(mListActiveTimer.begin(), mListActiveTimer.end(), CAmShSubstractTime(correctionTime)); + + //find the last occurrence of zero -> timer overflowed + std::list<sh_timer_s>::reverse_iterator overflowIter = std::find_if(mListActiveTimer.rbegin(), mListActiveTimer.rend(), CAmShCountdownZero()); + + //only if a timer overflowed + if (overflowIter != mListActiveTimer.rend()) + { + //copy all timers that need to be called to a new list + std::vector<sh_timer_s> tempList(overflowIter, mListActiveTimer.rend()); + + //erase all fired timers + std::list<sh_timer_s>::iterator it(overflowIter.base()); + mListActiveTimer.erase(mListActiveTimer.begin(), it); + + //call the callbacks for the timers + std::for_each(tempList.begin(), tempList.end(), CAmShCallTimer()); + } + } +} + +void CAmSocketHandler::exit_mainloop() +{ + //end the while loop + stop_listening(); + + //fire the ending filedescriptor + int p(1); + write(mPipe[1], &p, sizeof(p)); +} + +/** + * is used to set the pointer for the ppoll command + * @param buffertime + * @return + */ +inline timespec* CAmSocketHandler::insertTime(timespec& buffertime) +{ + if (!mListActiveTimer.empty()) + { + buffertime = mListActiveTimer.front().countdown; + return (&buffertime); + } + else + { + return (NULL); + } +} + +} + |