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>
32 #include "shared/CAmDltWrapper.h"
37 CAmSocketHandler::CAmSocketHandler() :
40 mListActiveTimer(), //
41 mLastInsertedHandle(0), //
42 mLastInsertedPollHandle(0), //
43 mRecreatePollfds(true), //
49 CAmSocketHandler::~CAmSocketHandler()
53 //todo: maybe have some: give me more time returned?
55 * start the block listening for filedescriptors. This is the mainloop.
57 void CAmSocketHandler::start_listenting()
62 //prepare the signalmask
64 sigemptyset(&sigmask);
65 sigaddset(&sigmask, SIGINT);
66 sigaddset(&sigmask, SIGQUIT);
67 sigaddset(&sigmask, SIGTERM);
68 sigaddset(&sigmask, SIGHUP);
69 sigaddset(&sigmask, SIGQUIT);
71 clock_gettime(CLOCK_MONOTONIC, &mStartTime);
72 while (!gDispatchDone)
74 //first we go through the registered filedescriptors and check if someone needs preparation:
75 std::for_each(mListPoll.begin(), mListPoll.end(), CAmShCallPrep());
79 mfdPollingArray.clear();
80 //there was a change in the setup, so we need to recreate the fdarray from the list
81 std::for_each(mListPoll.begin(), mListPoll.end(), CAmShCopyPollfd(mfdPollingArray));
82 mRecreatePollfds = false;
87 //block until something is on a filedescriptor
90 if ((pollStatus = ppoll(&mfdPollingArray[0], mfdPollingArray.size(), insertTime(buffertime), &sigmask)) < 0)
94 //a signal was received, that means it's time to go...
99 logError("SocketHandler::start_listenting ppoll returned with error", errno);
104 if (pollStatus != 0) //only check filedescriptors if there was a change
106 //todo: here could be a timer that makes sure naughty plugins return!
108 //freeze mListPoll by copying it - otherwise we get problems when we want to manipulate it during the next lines
109 std::list<sh_poll_s> listPoll;
110 mListPoll_t::iterator listmPollIt;
112 //remove all filedescriptors who did not fire
113 std::vector<pollfd>::iterator it = mfdPollingArray.begin();
116 it = std::find_if(it, mfdPollingArray.end(), eventFired);
117 if (it != mfdPollingArray.end())
119 listmPollIt = mListPoll.begin();
120 std::advance(listmPollIt, std::distance(mfdPollingArray.begin(), it));
121 listPoll.push_back(*listmPollIt);
122 listPoll.back().pollfdValue = *it;
125 } while (it != mfdPollingArray.end());
127 //stage 1, call firedCB
128 std::for_each(listPoll.begin(), listPoll.end(), CAmShCallFire());
130 //stage 2, lets ask around if some dispatching is necessary, the ones who need stay on the list
131 listPoll.remove_if(noDispatching);
133 //stage 3, the ones left need to dispatch, we do this as long as there is something to dispatch..
136 listPoll.remove_if(dispatchingFinished);
137 } while (!listPoll.empty());
142 //this was a timer event, we need to take care about the timers
151 void CAmSocketHandler::stop_listening()
155 //this is for all running timers only - we need to handle the additional offset here
156 if (!mListActiveTimer.empty())
158 timespec currentTime, correctionTime;
159 clock_gettime(CLOCK_MONOTONIC, ¤tTime);
160 correctionTime = timespecSub(currentTime, mStartTime);
161 std::for_each(mListActiveTimer.begin(), mListActiveTimer.end(), CAmShSubstractTime(correctionTime));
167 * Adds a filedescriptor to the polling loop
168 * @param fd the filedescriptor
169 * @param event the event flags
170 * @param prepare a callback that is called before the loop is entered
171 * @param fired a callback that is called when the filedescriptor needs to be read
172 * @param check a callback that is called to check if further actions are neccessary
173 * @param dispatch a callback that is called to dispatch the received data
174 * @param userData a pointer to userdata that is always passed around
175 * @param handle the handle of this poll
176 * @return E_OK if the descriptor was added, E_NON_EXISTENT if the fd is not valid
178 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)
181 return (E_NON_EXISTENT);
184 pollData.pollfdValue.fd = fd;
185 pollData.handle = ++mLastInsertedPollHandle;
186 pollData.pollfdValue.events = event;
187 pollData.pollfdValue.revents = 0;
188 pollData.userData = userData;
189 pollData.prepareCB = prepare;
190 pollData.firedCB = fired;
191 pollData.checkCB = check;
192 pollData.dispatchCB = dispatch;
194 //add new data to the list
195 mListPoll.push_back(pollData);
197 mRecreatePollfds = true;
199 handle = pollData.handle;
204 * removes a filedescriptor from the poll loop
208 am_Error_e CAmSocketHandler::removeFDPoll(const sh_pollHandle_t handle)
210 mListPoll_t::iterator iterator = mListPoll.begin();
212 for (; iterator != mListPoll.end(); ++iterator)
214 if (iterator->handle == handle)
216 iterator = mListPoll.erase(iterator);
217 mRecreatePollfds = true;
225 * adds a timer to the list of timers. The callback will be fired when the timer is up.
226 * This is not a high precise timer, it is very coarse. It is meant to be used for timeouts when waiting
227 * for an answer via a filedescriptor.
228 * One time timer. If you need again a timer, you need to add a new timer in the callback of the old one.
229 * @param timeouts timeouts time until the callback is fired
230 * @param callback callback the callback
231 * @param handle handle the handle that is created for the timer is returned. Can be used to remove the timer
232 * @param userData pointer always passed with the call
233 * @return E_OK in case of success
235 am_Error_e CAmSocketHandler::addTimer(const timespec timeouts, IAmShTimerCallBack* callback, sh_timerHandle_t& handle, void * userData)
237 assert(!((timeouts.tv_sec==0) && (timeouts.tv_nsec==0)));
238 assert(callback!=NULL);
240 sh_timer_s timerItem;
242 //create a new handle for the timer
243 handle = ++mLastInsertedHandle; //todo: overflow ruling !o
244 timerItem.handle = handle;
245 timerItem.countdown = timeouts;
246 timerItem.callback = callback;
247 timerItem.userData = userData;
249 mListTimer.push_back(timerItem);
251 //we add here the time difference between startTime and currenttime, because this time will be substracted later on in timecorrection
252 timespec currentTime;
253 clock_gettime(CLOCK_MONOTONIC, ¤tTime);
254 if (!gDispatchDone) //the mainloop is started
255 timerItem.countdown = timespecAdd(timeouts, timespecSub(currentTime, mStartTime));
257 mListActiveTimer.push_back(timerItem);
258 mListActiveTimer.sort(compareCountdown);
263 * removes a timer from the list of timers
264 * @param handle the handle to the timer
265 * @return E_OK in case of success, E_UNKNOWN if timer was not found.
267 am_Error_e CAmSocketHandler::removeTimer(const sh_timerHandle_t handle)
271 //stop the current timer
274 std::list<sh_timer_s>::iterator it(mListTimer.begin());
275 for (; it != mListTimer.end(); ++it)
277 if (it->handle == handle)
279 it = mListTimer.erase(it);
287 * restarts a timer and updates with a new interva
288 * @param handle handle to the timer
289 * @param timeouts new timout time
290 * @return E_OK on success, E_NON_EXISTENT if the handle was not found
292 am_Error_e CAmSocketHandler::updateTimer(const sh_timerHandle_t handle, const timespec timeouts)
294 //update the mList ....
295 sh_timer_s timerItem;
296 std::list<sh_timer_s>::iterator it(mListTimer.begin()), activeIt(mListActiveTimer.begin());
298 for (; it != mListTimer.end(); ++it)
300 if (it->handle == handle)
302 it->countdown = timeouts;
309 return (E_NON_EXISTENT);
313 //we add here the time difference between startTime and currenttime, because this time will be substracted later on in timecorrection
314 timespec currentTime, timeoutsCorrected;
315 currentTime.tv_nsec=timeoutsCorrected.tv_nsec=0;
316 currentTime.tv_sec=timeoutsCorrected.tv_sec=0;
317 clock_gettime(CLOCK_MONOTONIC, ¤tTime);
318 if (!gDispatchDone) //the mainloop is started
319 timeoutsCorrected = timespecAdd(timeouts, timespecSub(currentTime, mStartTime));
321 for (; activeIt != mListActiveTimer.end(); ++activeIt)
323 if (activeIt->handle == handle)
325 activeIt->countdown = timeoutsCorrected;
332 timerItem.countdown = timeoutsCorrected;
333 mListActiveTimer.push_back(timerItem);
335 mListActiveTimer.sort(compareCountdown);
340 * restarts a timer with the original value
342 * @return E_OK on success, E_NON_EXISTENT if the handle was not found
344 am_Error_e CAmSocketHandler::restartTimer(const sh_timerHandle_t handle)
346 sh_timer_s timerItem; //!<the original timer value
347 //find the original value
348 std::list<sh_timer_s>::iterator it(mListTimer.begin()), activeIt(mListActiveTimer.begin());
350 for (; it != mListTimer.end(); ++it)
352 if (it->handle == handle)
360 return (E_NON_EXISTENT);
364 //we add here the time difference between startTime and currenttime, because this time will be substracted later on in timecorrection
365 timespec currentTime, timeoutsCorrected;
366 clock_gettime(CLOCK_MONOTONIC, ¤tTime);
367 if (!gDispatchDone) //the mainloop is started
369 timeoutsCorrected = timespecAdd(timerItem.countdown, timespecSub(currentTime, mStartTime));
370 timerItem.countdown = timeoutsCorrected;
373 for (; activeIt != mListActiveTimer.end(); ++activeIt)
375 if (activeIt->handle == handle)
377 activeIt->countdown = timerItem.countdown;
384 mListActiveTimer.push_back(timerItem);
386 mListActiveTimer.sort(compareCountdown);
394 * @return E_OK on success, E_NON_EXISTENT if the handle was not found
396 am_Error_e CAmSocketHandler::stopTimer(const sh_timerHandle_t handle)
398 //go through the list and remove the timer with the handle
399 std::list<sh_timer_s>::iterator it(mListActiveTimer.begin());
400 for (; it != mListActiveTimer.end(); ++it)
402 if (it->handle == handle)
404 it = mListActiveTimer.erase(it);
408 return (E_NON_EXISTENT);
412 * updates the eventFlags of a poll
415 * @return @return E_OK on succsess, E_NON_EXISTENT if fd was not found
417 am_Error_e CAmSocketHandler::updateEventFlags(const sh_pollHandle_t handle, const short events)
419 mListPoll_t::iterator iterator = mListPoll.begin();
421 for (; iterator != mListPoll.end(); ++iterator)
423 if (iterator->handle == handle)
425 iterator->pollfdValue.events = events;
426 mRecreatePollfds = true;
434 * checks if a filedescriptor is validCAmShSubstractTime
435 * @param fd the filedescriptor
436 * @return true if the fd is valid
438 bool CAmSocketHandler::fdIsValid(const int fd) const
440 return (fcntl(fd, F_GETFL) != -1 || errno != EBADF);
446 void CAmSocketHandler::timerUp()
448 //find out the timedifference to starttime
449 timespec currentTime, diffTime;
450 clock_gettime(CLOCK_MONOTONIC, ¤tTime);
451 diffTime = timespecSub(currentTime, mStartTime);
453 //now we need to substract the diffTime from all timers and see if they are up
454 std::list<sh_timer_s>::reverse_iterator overflowIter = std::find_if(mListActiveTimer.rbegin(), mListActiveTimer.rend(), CAmShCountdownUp(diffTime));
456 //copy all fired timers into a list
457 std::vector<sh_timer_s> tempList(overflowIter, mListActiveTimer.rend());
459 //erase all fired timers
460 std::list<sh_timer_s>::iterator it(overflowIter.base());
461 mListActiveTimer.erase(mListActiveTimer.begin(), it);
463 //call the callbacks for the timers
464 std::for_each(tempList.begin(), tempList.end(), CAmShCallTimer());
468 * correct timers and fire the ones who are up
470 void CAmSocketHandler::timerCorrection()
472 //get the current time and calculate the correction value
473 timespec currentTime, correctionTime;
474 clock_gettime(CLOCK_MONOTONIC, ¤tTime);
475 correctionTime = timespecSub(currentTime, mStartTime);
476 mStartTime = currentTime;
478 if (!mListActiveTimer.empty())
481 //subtract the correction value from all items in the list
482 std::for_each(mListActiveTimer.begin(), mListActiveTimer.end(), CAmShSubstractTime(correctionTime));
484 //find the last occurrence of zero -> timer overflowed
485 std::list<sh_timer_s>::reverse_iterator overflowIter = std::find_if(mListActiveTimer.rbegin(), mListActiveTimer.rend(), CAmShCountdownZero());
487 //only if a timer overflowed
488 if (overflowIter != mListActiveTimer.rend())
490 //copy all timers that need to be called to a new list
491 std::vector<sh_timer_s> tempList(overflowIter, mListActiveTimer.rend());
493 //erase all fired timers
494 std::list<sh_timer_s>::iterator it(overflowIter.base());
495 mListActiveTimer.erase(mListActiveTimer.begin(), it);
497 //call the callbacks for the timers
498 std::for_each(tempList.begin(), tempList.end(), CAmShCallTimer());
504 * is used to set the pointer for the ppoll command
508 inline timespec* CAmSocketHandler::insertTime(timespec& buffertime)
510 if (!mListActiveTimer.empty())
512 buffertime = mListActiveTimer.front().countdown;
513 return (&buffertime);