summaryrefslogtreecommitdiff
path: root/AudioManagerDaemon/src/CAmSocketHandler.cpp
diff options
context:
space:
mode:
authorchristian mueller <christian.ei.mueller@bmw.de>2012-03-01 19:12:55 +0100
committerchristian mueller <christian.ei.mueller@bmw.de>2012-03-01 19:12:55 +0100
commitdfcafbec34e0e454d9995551998af5d40a27c7ad (patch)
treed93d70fea7feb7004f6a2ce9bfce9b83d3469e42 /AudioManagerDaemon/src/CAmSocketHandler.cpp
parentffccf1e2003cea4230413ccfdfd4cd6515484e4f (diff)
downloadaudiomanager-dfcafbec34e0e454d9995551998af5d40a27c7ad.tar.gz
* fix [GAM-28] problems in routingsender.cpp
* [GAM-27] major refactoring: bringing everything to standardized names * Enhancement of CAmSerializer.h * improvement of documentation * adoption & regeneration of inerfaces
Diffstat (limited to 'AudioManagerDaemon/src/CAmSocketHandler.cpp')
-rw-r--r--AudioManagerDaemon/src/CAmSocketHandler.cpp498
1 files changed, 498 insertions, 0 deletions
diff --git a/AudioManagerDaemon/src/CAmSocketHandler.cpp b/AudioManagerDaemon/src/CAmSocketHandler.cpp
new file mode 100644
index 0000000..2d5c7d7
--- /dev/null
+++ b/AudioManagerDaemon/src/CAmSocketHandler.cpp
@@ -0,0 +1,498 @@
+/**
+ * Copyright (C) 2011, BMW AG
+ *
+ * GeniviAudioMananger AudioManagerDaemon
+ *
+ * \file CAmDbusWrapper.cpp
+ *
+ * \date 20-Oct-2011 3:42:04 PM
+ * \author Christian Mueller (christian.ei.mueller@bmw.de)
+ *
+ * \section License
+ * GNU Lesser General Public License, version 2.1, with special exception (GENIVI clause)
+ * Copyright (C) 2011, BMW AG Christian Mueller Christian.ei.mueller@bmw.de
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License, version 2.1, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License, version 2.1, for more details.
+ * You should have received a copy of the GNU Lesser General Public License, version 2.1, along with this program; if not, see <http://www.gnu.org/licenses/lgpl-2.1.html>.
+ * Note that the copyright holders assume that the GNU Lesser General Public License, version 2.1, may also be applicable to programs even in cases in which the program is not a library in the technical sense.
+ * Linking AudioManager statically or dynamically with other modules is making a combined work based on AudioManager. You may license such other modules under the GNU Lesser General Public License, version 2.1. If you do not want to license your linked modules under the GNU Lesser General Public License, version 2.1, you may use the program under the following exception.
+ * As a special exception, the copyright holders of AudioManager give you permission to combine AudioManager with software programs or libraries that are released under any license unless such a combination is not permitted by the license of such a software program or library. You may copy and distribute such a system following the terms of the GNU Lesser General Public License, version 2.1, including this special exception, for AudioManager and the licenses of the other code concerned.
+ * Note that people who make modified versions of AudioManager are not obligated to grant this special exception for their modified versions; it is their choice whether to do so. The GNU Lesser General Public License, version 2.1, gives permission to release a modified version without this exception; this exception also makes it possible to release a modified version which carries forward this exception.
+ *
+ */
+
+#include "shared/CAmSocketHandler.h"
+#include <config.h>
+#include <cassert>
+#include <sys/fcntl.h>
+#include <sys/errno.h>
+#include <sys/poll.h>
+#include <algorithm>
+#include <time.h>
+#include <features.h>
+#include <csignal>
+#include "shared/CAmDltWrapper.h"
+
+//todo: implement time correction if timer was interrupted by call
+//todo: change hitlist to a list that holds all information, because entering and removing items will be cheaper than with std::vector
+
+namespace am {
+
+CAmSocketHandler::CAmSocketHandler() :
+ mListPoll(), //
+ mListTimer(), //
+ mListActiveTimer(), //
+ mNextTimer(), //
+ mLastInsertedHandle(0), //
+ mLastInsertedPollHandle(0), //
+ mRecreatePollfds(true), //
+ mTimeout()
+{
+ mTimeout.tv_nsec = -1;
+ mTimeout.tv_sec = -1;
+ gDispatchDone = 0;
+}
+
+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()
+{
+ gDispatchDone=0;
+ int16_t pollStatus;
+ std::list<int16_t> hitList;
+
+ //init the timer
+ initTimer();
+
+ //prepare the signalmask
+ sigset_t sigmask;
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGINT);
+ sigaddset(&sigmask, SIGQUIT);
+ sigaddset(&sigmask, SIGTERM);
+ sigaddset(&sigmask, SIGHUP);
+ sigaddset(&sigmask, SIGQUIT);
+
+ while (!gDispatchDone)
+ {
+ //first we go through the registered filedescriptors and check if someone needs preparation:
+ mListPoll_t::iterator prepIter = mListPoll.begin();
+ CAmShPollPrepare *prep = NULL;
+ for (; prepIter != mListPoll.end(); ++prepIter)
+ {
+ if ((prep = prepIter->prepareCB) != NULL)
+ prep->Call(prepIter->handle, prepIter->userData);
+ }
+
+ 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;
+ }
+
+ //block until something is on a filedescriptor
+#ifdef WITH_PPOLL
+
+ 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);
+ }
+ }
+
+#else
+ //sigprocmask (SIG_SETMASK, &mask, &oldmask);
+ if((pollStatus=poll(&mfdPollingArray[0],mfdPollingArray.size(),timespec2ms(mTimeout)))<0)
+ {
+
+ if(errno==EINTR)
+ {
+ //a signal was received, that means it's time to go...
+ //todo: add things to do here before going to sleep
+ exit(0);
+ }
+ logError("SocketHandler::start_listenting poll returned with error",errno);
+ exit(0);
+ }
+ //sigprocmask (SIG_SETMASK, &oldmask, NULL);
+#endif
+
+ 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
+ mListPoll_t listPoll(mListPoll);
+
+ //get all indexes of the fired events and save them int hitList
+ hitList.clear();
+ std::vector<pollfd>::iterator it = mfdPollingArray.begin();
+ do
+ {
+ it = std::find_if(it, mfdPollingArray.end(), onlyFiredEvents);
+ if (it != mfdPollingArray.end())
+ hitList.push_back(std::distance(mfdPollingArray.begin(), it++));
+
+ } while (it != mfdPollingArray.end());
+
+ //stage 1, call firedCB for all matched events, but only if callback is not zero!
+ std::list<int16_t>::iterator hListIt = hitList.begin();
+ for (; hListIt != hitList.end(); ++hListIt)
+ {
+ CAmShPollFired* fire = NULL;
+ if ((fire = listPoll.at(*hListIt).firedCB) != NULL)
+ fire->Call(mfdPollingArray.at(*hListIt), listPoll.at(*hListIt).handle, listPoll.at(*hListIt).userData);
+ }
+
+ //stage 2, lets ask around if some dispatching is necessary, if not, they are taken from the hitlist
+ hListIt = hitList.begin();
+ for (; hListIt != hitList.end(); ++hListIt)
+ {
+ CAmShPollCheck* check = NULL;
+ if ((check = listPoll.at(*hListIt).checkCB) != NULL)
+ {
+ if (!check->Call(listPoll.at(*hListIt).handle, listPoll.at(*hListIt).userData))
+ {
+ hListIt = hitList.erase(hListIt);
+ }
+ }
+ }
+
+ //stage 3, the ones left need to dispatch, we do this as long as there is something to dispatch..
+ do
+ {
+ hListIt = hitList.begin();
+ for (; hListIt != hitList.end(); ++hListIt)
+ {
+ CAmShPollDispatch *dispatch = NULL;
+ if ((dispatch = listPoll.at(*hListIt).dispatchCB) != NULL)
+ {
+ if (!dispatch->Call(listPoll.at(*hListIt).handle, listPoll.at(*hListIt).userData))
+ {
+ hListIt = hitList.erase(hListIt);
+ }
+ }
+ else //there is no dispatch function, so we just remove the file from the list...
+ {
+ hListIt = hitList.erase(hListIt);
+ }
+ }
+ } while (!hitList.empty());
+
+ }
+ else //Timerevent
+ {
+ //this was a timer event, we need to take care about the timers
+ timerUp();
+ }
+ }
+}
+
+/**
+ * exits the loop
+ */
+void CAmSocketHandler::stop_listening()
+{
+ gDispatchDone = 1;
+}
+
+/**
+ * Adds a filedescriptor to the polling loop
+ * @param fd this is a valid filedescriptor
+ * @param event the event flags
+ * @param callback the callback that shall be called if the filedescriptor poll succeeded
+ * @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, CAmShPollPrepare *prepare, CAmShPollFired *fired, CAmShPollCheck *check, CAmShPollDispatch *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 fd the filedescriptor to be removed
+ * @return E_OK in case of sucess, E_NON_EXISTENT or E_UNKNOWN if the fd in not registered
+ */
+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 time until the callback is fired
+ * @param callback the callback
+ * @param handle the handle that is created for the timer is returned. Can be used to remove the timer
+ * @return E_OK in case of success
+ */
+am_Error_e CAmSocketHandler::addTimer(const timespec timeouts, CAmShTimerCallBack*& 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 !
+ timerItem.handle = handle;
+ timerItem.countdown = timeouts;
+ timerItem.timeout = timeouts;
+ timerItem.callback = callback;
+ timerItem.userData = userData;
+
+ //add timer to the list
+ mListActiveTimer.push_back(timerItem);
+ mListTimer.push_back(timerItem);
+
+ //very important: sort the list so that the smallest value is front
+ mListActiveTimer.sort(compareCountdown);
+ mTimeout = mListActiveTimer.front().countdown;
+ 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 interval
+ * @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::restartTimer(const sh_timerHandle_t handle, const timespec timeouts)
+{
+ sh_timer_s timerItem;
+ std::list<sh_timer_s>::iterator it = mListTimer.begin();
+ for (; it != mListTimer.end(); ++it)
+ {
+ if (it->handle == handle)
+ {
+ timerItem = *it;
+ break;
+ }
+ }
+
+ if (timeouts.tv_nsec != -1 && timeouts.tv_sec != -1)
+ {
+ timerItem.timeout = timeouts;
+ }
+
+ mListActiveTimer.push_back(timerItem);
+
+ //very important: sort the list so that the smallest value is front
+ mListActiveTimer.sort(compareCountdown);
+ mTimeout = mListActiveTimer.front().countdown;
+ return E_OK;
+}
+
+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);
+ if (!mListActiveTimer.empty())
+ {
+ mTimeout = mListActiveTimer.front().countdown;
+ }
+ else
+ {
+ mTimeout.tv_nsec = -1;
+ mTimeout.tv_sec = -1;
+ }
+ return E_OK;
+ }
+ }
+ return E_NON_EXISTENT;
+}
+
+/**
+ * updates the eventFlags of a poll
+ * @param fd the filedescriptor of the poll
+ * @param event the event flags
+ * @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 valid
+ * @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);
+}
+
+/**
+ * whenever a timer is up, this function needs to be called.
+ * Removes the fired timer, calls the callback and resets mTimeout
+ */
+void CAmSocketHandler::timerUp()
+{
+ //first fire the event
+ mListActiveTimer.front().callback->Call(mListActiveTimer.front().handle, mListActiveTimer.front().userData);
+
+ //then remove the first timer, the one who fired
+ mListActiveTimer.pop_front();
+ if (!mListActiveTimer.empty())
+ {
+ //substract the old value from all timers in the list
+ std::for_each(mListActiveTimer.begin(), mListActiveTimer.end(), CAmShSubstractTime(mTimeout));
+ mTimeout = mListActiveTimer.front().countdown;
+ }
+ else
+ {
+ mTimeout.tv_nsec = -1;
+ mTimeout.tv_sec = -1;
+ }
+}
+
+/**
+ * init the timers
+ */
+void CAmSocketHandler::initTimer()
+{
+ if (!mListActiveTimer.empty())
+ {
+ mTimeout = mListActiveTimer.front().countdown;
+ }
+ else
+ {
+ mTimeout.tv_nsec = -1;
+ mTimeout.tv_sec = -1;
+ }
+}
+
+/**
+ * convert timespec to milliseconds
+ * @param time time in timespec
+ * @return time in milliseconds
+ */
+inline int CAmSocketHandler::timespec2ms(const timespec & time)
+{
+ return (time.tv_nsec == -1 && time.tv_sec == -1) ? -1 : time.tv_sec * 1000 + time.tv_nsec / 1000000;
+}
+
+inline timespec* CAmSocketHandler::insertTime(timespec& buffertime)
+{
+ buffertime.tv_nsec = mTimeout.tv_nsec;
+ buffertime.tv_sec = mTimeout.tv_sec;
+ return (mTimeout.tv_nsec == -1 && mTimeout.tv_sec == -1) ? NULL : &buffertime;
+}
+
+/**
+ * functor to easy substract from each countdown
+ * @param t value to substract from
+ */
+void CAmSocketHandler::CAmShSubstractTime::operator ()(sh_timer_s & t) const
+{
+ int val = 0;
+ if ((val = t.countdown.tv_nsec - param.tv_nsec) < 0)
+ {
+ t.countdown.tv_nsec = 1000000000 + val;
+ t.countdown.tv_sec--;
+ }
+ else
+ {
+ t.countdown.tv_nsec = val;
+ }
+ (t.countdown.tv_sec - param.tv_sec) < 0 ? 0 : (t.countdown.tv_sec -= param.tv_sec);
+}
+
+void CAmSocketHandler::CAmShCopyPollfd::operator ()(const sh_poll_s & row)
+{
+ pollfd temp = row.pollfdValue;
+ temp.revents = 0;
+ mArray.push_back(temp);
+}
+}