2 * Copyright (C) 2012, BMW AG
4 * This file is part of GENIVI Project AudioManager.
6 * Contributions are licensed to the GENIVI Alliance under one or more
7 * Contribution License Agreements.
10 * This Source Code Form is subject to the terms of the
11 * Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with
12 * this file, You can obtain one at http://mozilla.org/MPL/2.0/.
15 * \author Christian Mueller, christian.ei.mueller@bmw.de BMW 2011,2012
17 * \file CAmSocketHandler.cpp
18 * For further information see http://www.genivi.org/.
22 #include "shared/CAmSocketHandler.h"
25 #include <sys/fcntl.h>
26 #include <sys/errno.h>
33 #include "shared/CAmDltWrapper.h"
38 CAmSocketHandler::CAmSocketHandler() :
39 receiverCallbackT(this, &CAmSocketHandler::receiverCallback),//
40 checkerCallbackT(this, &CAmSocketHandler::checkerCallback),//
45 mListActiveTimer(), //
46 mLastInsertedHandle(0), //
47 mLastInsertedPollHandle(0), //
48 mRecreatePollfds(true), //
51 if (pipe(mPipe) == -1)
53 logError("CAmSerializer could not create pipe!");
56 //add the pipe to the poll - nothing needs to be proccessed here we just need the pipe to trigger the ppoll
58 sh_pollHandle_t handle;
60 addFDPoll(mPipe[0], event, NULL, &receiverCallbackT, &checkerCallbackT, NULL, NULL, handle);
63 CAmSocketHandler::~CAmSocketHandler()
67 //todo: maybe have some: give me more time returned?
69 * start the block listening for filedescriptors. This is the mainloop.
71 void CAmSocketHandler::start_listenting()
76 //prepare the signalmask
78 sigemptyset(&sigmask);
79 sigaddset(&sigmask, SIGINT);
80 sigaddset(&sigmask, SIGQUIT);
81 sigaddset(&sigmask, SIGTERM);
82 sigaddset(&sigmask, SIGHUP);
83 sigaddset(&sigmask, SIGQUIT);
85 clock_gettime(CLOCK_MONOTONIC, &mStartTime);
86 while (!mDispatchDone)
88 //first we go through the registered filedescriptors and check if someone needs preparation:
89 std::for_each(mListPoll.begin(), mListPoll.end(), CAmShCallPrep());
93 mfdPollingArray.clear();
94 //there was a change in the setup, so we need to recreate the fdarray from the list
95 std::for_each(mListPoll.begin(), mListPoll.end(), CAmShCopyPollfd(mfdPollingArray));
96 mRecreatePollfds = false;
101 //block until something is on a filedescriptor
104 if ((pollStatus = ppoll(&mfdPollingArray[0], mfdPollingArray.size(), insertTime(buffertime), &sigmask)) < 0)
108 //a signal was received, that means it's time to go...
113 logError("SocketHandler::start_listenting ppoll returned with error", errno);
118 if (pollStatus != 0) //only check filedescriptors if there was a change
120 //todo: here could be a timer that makes sure naughty plugins return!
122 //freeze mListPoll by copying it - otherwise we get problems when we want to manipulate it during the next lines
123 std::list<sh_poll_s> listPoll;
124 mListPoll_t::iterator listmPollIt;
126 //remove all filedescriptors who did not fire
127 std::vector<pollfd>::iterator it = mfdPollingArray.begin();
130 it = std::find_if(it, mfdPollingArray.end(), eventFired);
131 if (it != mfdPollingArray.end())
133 listmPollIt = mListPoll.begin();
134 std::advance(listmPollIt, std::distance(mfdPollingArray.begin(), it));
135 listPoll.push_back(*listmPollIt);
136 listPoll.back().pollfdValue = *it;
139 } while (it != mfdPollingArray.end());
141 //stage 1, call firedCB
142 std::for_each(listPoll.begin(), listPoll.end(), CAmShCallFire());
144 //stage 2, lets ask around if some dispatching is necessary, the ones who need stay on the list
145 listPoll.remove_if(noDispatching);
147 //stage 3, the ones left need to dispatch, we do this as long as there is something to dispatch..
150 listPoll.remove_if(dispatchingFinished);
151 } while (!listPoll.empty());
156 //this was a timer event, we need to take care about the timers
165 void CAmSocketHandler::stop_listening()
169 //this is for all running timers only - we need to handle the additional offset here
170 if (!mListActiveTimer.empty())
172 timespec currentTime, correctionTime;
173 clock_gettime(CLOCK_MONOTONIC, ¤tTime);
174 correctionTime = timespecSub(currentTime, mStartTime);
175 std::for_each(mListActiveTimer.begin(), mListActiveTimer.end(), CAmShSubstractTime(correctionTime));
181 * Adds a filedescriptor to the polling loop
182 * @param fd the filedescriptor
183 * @param event the event flags
184 * @param prepare a callback that is called before the loop is entered
185 * @param fired a callback that is called when the filedescriptor needs to be read
186 * @param check a callback that is called to check if further actions are neccessary
187 * @param dispatch a callback that is called to dispatch the received data
188 * @param userData a pointer to userdata that is always passed around
189 * @param handle the handle of this poll
190 * @return E_OK if the descriptor was added, E_NON_EXISTENT if the fd is not valid
192 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)
195 return (E_NON_EXISTENT);
198 pollData.pollfdValue.fd = fd;
199 pollData.handle = ++mLastInsertedPollHandle;
200 pollData.pollfdValue.events = event;
201 pollData.pollfdValue.revents = 0;
202 pollData.userData = userData;
203 pollData.prepareCB = prepare;
204 pollData.firedCB = fired;
205 pollData.checkCB = check;
206 pollData.dispatchCB = dispatch;
208 //add new data to the list
209 mListPoll.push_back(pollData);
211 mRecreatePollfds = true;
213 handle = pollData.handle;
218 * removes a filedescriptor from the poll loop
222 am_Error_e CAmSocketHandler::removeFDPoll(const sh_pollHandle_t handle)
224 mListPoll_t::iterator iterator = mListPoll.begin();
226 for (; iterator != mListPoll.end(); ++iterator)
228 if (iterator->handle == handle)
230 iterator = mListPoll.erase(iterator);
231 mRecreatePollfds = true;
239 * adds a timer to the list of timers. The callback will be fired when the timer is up.
240 * This is not a high precise timer, it is very coarse. It is meant to be used for timeouts when waiting
241 * for an answer via a filedescriptor.
242 * One time timer. If you need again a timer, you need to add a new timer in the callback of the old one.
243 * @param timeouts timeouts time until the callback is fired
244 * @param callback callback the callback
245 * @param handle handle the handle that is created for the timer is returned. Can be used to remove the timer
246 * @param userData pointer always passed with the call
247 * @return E_OK in case of success
249 am_Error_e CAmSocketHandler::addTimer(const timespec timeouts, IAmShTimerCallBack* callback, sh_timerHandle_t& handle, void * userData)
251 assert(!((timeouts.tv_sec==0) && (timeouts.tv_nsec==0)));
252 assert(callback!=NULL);
254 sh_timer_s timerItem;
256 //create a new handle for the timer
257 handle = ++mLastInsertedHandle; //todo: overflow ruling !o
258 timerItem.handle = handle;
259 timerItem.countdown = timeouts;
260 timerItem.callback = callback;
261 timerItem.userData = userData;
263 mListTimer.push_back(timerItem);
265 //we add here the time difference between startTime and currenttime, because this time will be substracted later on in timecorrection
266 timespec currentTime;
267 clock_gettime(CLOCK_MONOTONIC, ¤tTime);
268 if (!mDispatchDone) //the mainloop is started
269 timerItem.countdown = timespecAdd(timeouts, timespecSub(currentTime, mStartTime));
271 mListActiveTimer.push_back(timerItem);
272 mListActiveTimer.sort(compareCountdown);
277 * removes a timer from the list of timers
278 * @param handle the handle to the timer
279 * @return E_OK in case of success, E_UNKNOWN if timer was not found.
281 am_Error_e CAmSocketHandler::removeTimer(const sh_timerHandle_t handle)
285 //stop the current timer
288 std::list<sh_timer_s>::iterator it(mListTimer.begin());
289 for (; it != mListTimer.end(); ++it)
291 if (it->handle == handle)
293 it = mListTimer.erase(it);
301 * restarts a timer and updates with a new interva
302 * @param handle handle to the timer
303 * @param timeouts new timout time
304 * @return E_OK on success, E_NON_EXISTENT if the handle was not found
306 am_Error_e CAmSocketHandler::updateTimer(const sh_timerHandle_t handle, const timespec timeouts)
308 //update the mList ....
309 sh_timer_s timerItem;
310 std::list<sh_timer_s>::iterator it(mListTimer.begin()), activeIt(mListActiveTimer.begin());
312 for (; it != mListTimer.end(); ++it)
314 if (it->handle == handle)
316 it->countdown = timeouts;
323 return (E_NON_EXISTENT);
327 //we add here the time difference between startTime and currenttime, because this time will be substracted later on in timecorrection
328 timespec currentTime, timeoutsCorrected;
329 currentTime.tv_nsec=timeoutsCorrected.tv_nsec=0;
330 currentTime.tv_sec=timeoutsCorrected.tv_sec=0;
331 clock_gettime(CLOCK_MONOTONIC, ¤tTime);
332 if (!mDispatchDone) //the mainloop is started
333 timeoutsCorrected = timespecAdd(timeouts, timespecSub(currentTime, mStartTime));
335 for (; activeIt != mListActiveTimer.end(); ++activeIt)
337 if (activeIt->handle == handle)
339 activeIt->countdown = timeoutsCorrected;
346 timerItem.countdown = timeoutsCorrected;
347 mListActiveTimer.push_back(timerItem);
349 mListActiveTimer.sort(compareCountdown);
354 * restarts a timer with the original value
356 * @return E_OK on success, E_NON_EXISTENT if the handle was not found
358 am_Error_e CAmSocketHandler::restartTimer(const sh_timerHandle_t handle)
360 sh_timer_s timerItem; //!<the original timer value
361 //find the original value
362 std::list<sh_timer_s>::iterator it(mListTimer.begin()), activeIt(mListActiveTimer.begin());
364 for (; it != mListTimer.end(); ++it)
366 if (it->handle == handle)
374 return (E_NON_EXISTENT);
378 //we add here the time difference between startTime and currenttime, because this time will be substracted later on in timecorrection
379 timespec currentTime, timeoutsCorrected;
380 clock_gettime(CLOCK_MONOTONIC, ¤tTime);
381 if (!mDispatchDone) //the mainloop is started
383 timeoutsCorrected = timespecAdd(timerItem.countdown, timespecSub(currentTime, mStartTime));
384 timerItem.countdown = timeoutsCorrected;
387 for (; activeIt != mListActiveTimer.end(); ++activeIt)
389 if (activeIt->handle == handle)
391 activeIt->countdown = timerItem.countdown;
398 mListActiveTimer.push_back(timerItem);
400 mListActiveTimer.sort(compareCountdown);
408 * @return E_OK on success, E_NON_EXISTENT if the handle was not found
410 am_Error_e CAmSocketHandler::stopTimer(const sh_timerHandle_t handle)
412 //go through the list and remove the timer with the handle
413 std::list<sh_timer_s>::iterator it(mListActiveTimer.begin());
414 for (; it != mListActiveTimer.end(); ++it)
416 if (it->handle == handle)
418 it = mListActiveTimer.erase(it);
422 return (E_NON_EXISTENT);
426 * updates the eventFlags of a poll
429 * @return @return E_OK on succsess, E_NON_EXISTENT if fd was not found
431 am_Error_e CAmSocketHandler::updateEventFlags(const sh_pollHandle_t handle, const short events)
433 mListPoll_t::iterator iterator = mListPoll.begin();
435 for (; iterator != mListPoll.end(); ++iterator)
437 if (iterator->handle == handle)
439 iterator->pollfdValue.events = events;
440 mRecreatePollfds = true;
448 * checks if a filedescriptor is validCAmShSubstractTime
449 * @param fd the filedescriptor
450 * @return true if the fd is valid
452 bool CAmSocketHandler::fdIsValid(const int fd) const
454 return (fcntl(fd, F_GETFL) != -1 || errno != EBADF);
460 void CAmSocketHandler::timerUp()
462 //find out the timedifference to starttime
463 timespec currentTime, diffTime;
464 clock_gettime(CLOCK_MONOTONIC, ¤tTime);
465 diffTime = timespecSub(currentTime, mStartTime);
467 //now we need to substract the diffTime from all timers and see if they are up
468 std::list<sh_timer_s>::reverse_iterator overflowIter = std::find_if(mListActiveTimer.rbegin(), mListActiveTimer.rend(), CAmShCountdownUp(diffTime));
470 //copy all fired timers into a list
471 std::vector<sh_timer_s> tempList(overflowIter, mListActiveTimer.rend());
473 //erase all fired timers
474 std::list<sh_timer_s>::iterator it(overflowIter.base());
475 mListActiveTimer.erase(mListActiveTimer.begin(), it);
477 //call the callbacks for the timers
478 std::for_each(tempList.begin(), tempList.end(), CAmShCallTimer());
482 * correct timers and fire the ones who are up
484 void CAmSocketHandler::timerCorrection()
486 //get the current time and calculate the correction value
487 timespec currentTime, correctionTime;
488 clock_gettime(CLOCK_MONOTONIC, ¤tTime);
489 correctionTime = timespecSub(currentTime, mStartTime);
490 mStartTime = currentTime;
492 if (!mListActiveTimer.empty())
495 //subtract the correction value from all items in the list
496 std::for_each(mListActiveTimer.begin(), mListActiveTimer.end(), CAmShSubstractTime(correctionTime));
498 //find the last occurrence of zero -> timer overflowed
499 std::list<sh_timer_s>::reverse_iterator overflowIter = std::find_if(mListActiveTimer.rbegin(), mListActiveTimer.rend(), CAmShCountdownZero());
501 //only if a timer overflowed
502 if (overflowIter != mListActiveTimer.rend())
504 //copy all timers that need to be called to a new list
505 std::vector<sh_timer_s> tempList(overflowIter, mListActiveTimer.rend());
507 //erase all fired timers
508 std::list<sh_timer_s>::iterator it(overflowIter.base());
509 mListActiveTimer.erase(mListActiveTimer.begin(), it);
511 //call the callbacks for the timers
512 std::for_each(tempList.begin(), tempList.end(), CAmShCallTimer());
517 void CAmSocketHandler::exit_mainloop()
522 //fire the ending filedescriptor
524 write(mPipe[1], &p, sizeof(p));
528 * is used to set the pointer for the ppoll command
532 inline timespec* CAmSocketHandler::insertTime(timespec& buffertime)
534 if (!mListActiveTimer.empty())
536 buffertime = mListActiveTimer.front().countdown;
537 return (&buffertime);