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"
34 //todo: implement time correction if timer was interrupted by call
35 //todo: change hitlist to a list that holds all information, because entering and removing items will be cheaper than with std::vector
40 CAmSocketHandler::CAmSocketHandler() :
43 mListActiveTimer(), //
45 mLastInsertedHandle(0), //
46 mLastInsertedPollHandle(0), //
47 mRecreatePollfds(true), //
50 mTimeout.tv_nsec = -1;
55 CAmSocketHandler::~CAmSocketHandler()
59 //todo: maybe have some: give me more time returned?
61 * start the block listening for filedescriptors. This is the mainloop.
63 void CAmSocketHandler::start_listenting()
67 std::list<int16_t> hitList;
72 //prepare the signalmask
74 sigemptyset(&sigmask);
75 sigaddset(&sigmask, SIGINT);
76 sigaddset(&sigmask, SIGQUIT);
77 sigaddset(&sigmask, SIGTERM);
78 sigaddset(&sigmask, SIGHUP);
79 sigaddset(&sigmask, SIGQUIT);
81 while (!gDispatchDone)
83 //first we go through the registered filedescriptors and check if someone needs preparation:
84 mListPoll_t::iterator prepIter = mListPoll.begin();
85 IAmShPollPrepare *prep = NULL;
86 for (; prepIter != mListPoll.end(); ++prepIter)
88 if ((prep = prepIter->prepareCB) != NULL)
89 prep->Call(prepIter->handle, prepIter->userData);
94 mfdPollingArray.clear();
95 //there was a change in the setup, so we need to recreate the fdarray from the list
96 std::for_each(mListPoll.begin(), mListPoll.end(), CAmShCopyPollfd(mfdPollingArray));
97 mRecreatePollfds = false;
100 //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);
119 //sigprocmask (SIG_SETMASK, &mask, &oldmask);
120 if((pollStatus=poll(&mfdPollingArray[0],mfdPollingArray.size(),timespec2ms(mTimeout)))<0)
125 //a signal was received, that means it's time to go...
126 //todo: add things to do here before going to sleep
129 logError("SocketHandler::start_listenting poll returned with error",errno);
132 //sigprocmask (SIG_SETMASK, &oldmask, NULL);
135 if (pollStatus != 0) //only check filedescriptors if there was a change
137 //todo: here could be a timer that makes sure naughty plugins return!
139 //freeze mListPoll by copying it - otherwise we get problems when we want to manipulate it during the next lines
140 mListPoll_t listPoll(mListPoll);
142 //get all indexes of the fired events and save them int hitList
144 std::vector<pollfd>::iterator it = mfdPollingArray.begin();
147 it = std::find_if(it, mfdPollingArray.end(), onlyFiredEvents);
148 if (it != mfdPollingArray.end())
149 hitList.push_back(std::distance(mfdPollingArray.begin(), it++));
151 } while (it != mfdPollingArray.end());
153 //stage 1, call firedCB for all matched events, but only if callback is not zero!
154 std::list<int16_t>::iterator hListIt = hitList.begin();
155 for (; hListIt != hitList.end(); ++hListIt)
157 IAmShPollFired* fire = NULL;
158 if ((fire = listPoll.at(*hListIt).firedCB) != NULL)
159 fire->Call(mfdPollingArray.at(*hListIt), listPoll.at(*hListIt).handle, listPoll.at(*hListIt).userData);
162 //stage 2, lets ask around if some dispatching is necessary, if not, they are taken from the hitlist
163 hListIt = hitList.begin();
164 for (; hListIt != hitList.end(); ++hListIt)
166 IAmShPollCheck* check = NULL;
167 if ((check = listPoll.at(*hListIt).checkCB) != NULL)
169 if (!check->Call(listPoll.at(*hListIt).handle, listPoll.at(*hListIt).userData))
171 hListIt = hitList.erase(hListIt);
176 //stage 3, the ones left need to dispatch, we do this as long as there is something to dispatch..
179 hListIt = hitList.begin();
180 for (; hListIt != hitList.end(); ++hListIt)
182 IAmShPollDispatch *dispatch = NULL;
183 if ((dispatch = listPoll.at(*hListIt).dispatchCB) != NULL)
185 if (!dispatch->Call(listPoll.at(*hListIt).handle, listPoll.at(*hListIt).userData))
187 hListIt = hitList.erase(hListIt);
190 else //there is no dispatch function, so we just remove the file from the list...
192 hListIt = hitList.erase(hListIt);
195 } while (!hitList.empty());
200 //this was a timer event, we need to take care about the timers
209 void CAmSocketHandler::stop_listening()
215 * Adds a filedescriptor to the polling loop
216 * @param fd the filedescriptor
217 * @param event the event flags
218 * @param prepare a callback that is called before the loop is entered
219 * @param fired a callback that is called when the filedescriptor needs to be read
220 * @param check a callback that is called to check if further actions are neccessary
221 * @param dispatch a callback that is called to dispatch the received data
222 * @param userData a pointer to userdata that is always passed around
223 * @param handle the handle of this poll
224 * @return E_OK if the descriptor was added, E_NON_EXISTENT if the fd is not valid
226 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)
229 return (E_NON_EXISTENT);
232 pollData.pollfdValue.fd = fd;
233 pollData.handle = ++mLastInsertedPollHandle;
234 pollData.pollfdValue.events = event;
235 pollData.pollfdValue.revents = 0;
236 pollData.userData = userData;
237 pollData.prepareCB = prepare;
238 pollData.firedCB = fired;
239 pollData.checkCB = check;
240 pollData.dispatchCB = dispatch;
242 //add new data to the list
243 mListPoll.push_back(pollData);
245 mRecreatePollfds = true;
247 handle = pollData.handle;
252 * removes a filedescriptor from the poll loop
256 am_Error_e CAmSocketHandler::removeFDPoll(const sh_pollHandle_t handle)
258 mListPoll_t::iterator iterator = mListPoll.begin();
260 for (; iterator != mListPoll.end(); ++iterator)
262 if (iterator->handle == handle)
264 iterator = mListPoll.erase(iterator);
265 mRecreatePollfds = true;
273 * adds a timer to the list of timers. The callback will be fired when the timer is up.
274 * This is not a high precise timer, it is very coarse. It is meant to be used for timeouts when waiting
275 * for an answer via a filedescriptor.
276 * One time timer. If you need again a timer, you need to add a new timer in the callback of the old one.
277 * @param timeouts timeouts time until the callback is fired
278 * @param callback callback the callback
279 * @param handle handle the handle that is created for the timer is returned. Can be used to remove the timer
280 * @param userData pointer always passed with the call
281 * @return E_OK in case of success
283 am_Error_e CAmSocketHandler::addTimer(const timespec timeouts, IAmShTimerCallBack*& callback, sh_timerHandle_t& handle, void * userData)
285 assert(!((timeouts.tv_sec==0) && (timeouts.tv_nsec==0)));
286 assert(callback!=NULL);
288 sh_timer_s timerItem;
290 //create a new handle for the timer
291 handle = ++mLastInsertedHandle; //todo: overflow ruling !
292 timerItem.handle = handle;
293 timerItem.countdown = timeouts;
294 timerItem.timeout = timeouts;
295 timerItem.callback = callback;
296 timerItem.userData = userData;
298 //add timer to the list
299 mListActiveTimer.push_back(timerItem);
300 mListTimer.push_back(timerItem);
302 //very important: sort the list so that the smallest value is front
303 mListActiveTimer.sort(compareCountdown);
304 mTimeout = mListActiveTimer.front().countdown;
309 * removes a timer from the list of timers
310 * @param handle the handle to the timer
311 * @return E_OK in case of success, E_UNKNOWN if timer was not found.
313 am_Error_e CAmSocketHandler::removeTimer(const sh_timerHandle_t handle)
317 //stop the current timer
320 std::list<sh_timer_s>::iterator it = mListTimer.begin();
321 for (; it != mListTimer.end(); ++it)
323 if (it->handle == handle)
325 it = mListTimer.erase(it);
333 * restarts a timer and updates with a new interval
334 * @param handle handle to the timer
335 * @param timeouts new timout time
336 * @return E_OK on success, E_NON_EXISTENT if the handle was not found
338 am_Error_e CAmSocketHandler::restartTimer(const sh_timerHandle_t handle, const timespec timeouts)
340 sh_timer_s timerItem;
341 std::list<sh_timer_s>::iterator it = mListTimer.begin();
342 for (; it != mListTimer.end(); ++it)
344 if (it->handle == handle)
351 if (timeouts.tv_nsec != -1 && timeouts.tv_sec != -1)
353 timerItem.timeout = timeouts;
356 mListActiveTimer.push_back(timerItem);
358 //very important: sort the list so that the smallest value is front
359 mListActiveTimer.sort(compareCountdown);
360 mTimeout = mListActiveTimer.front().countdown;
364 am_Error_e CAmSocketHandler::stopTimer(const sh_timerHandle_t handle)
366 //go through the list and remove the timer with the handle
367 std::list<sh_timer_s>::iterator it = mListActiveTimer.begin();
368 for (; it != mListActiveTimer.end(); ++it)
370 if (it->handle == handle)
372 it = mListActiveTimer.erase(it);
373 if (!mListActiveTimer.empty())
375 mTimeout = mListActiveTimer.front().countdown;
379 mTimeout.tv_nsec = -1;
380 mTimeout.tv_sec = -1;
385 return (E_NON_EXISTENT);
389 * updates the eventFlags of a poll
392 * @return @return E_OK on succsess, E_NON_EXISTENT if fd was not found
394 am_Error_e CAmSocketHandler::updateEventFlags(const sh_pollHandle_t handle, const short events)
396 mListPoll_t::iterator iterator = mListPoll.begin();
398 for (; iterator != mListPoll.end(); ++iterator)
400 if (iterator->handle == handle)
402 iterator->pollfdValue.events = events;
403 mRecreatePollfds = true;
411 * checks if a filedescriptor is valid
412 * @param fd the filedescriptor
413 * @return true if the fd is valid
415 bool CAmSocketHandler::fdIsValid(const int fd) const
417 return (fcntl(fd, F_GETFL) != -1 || errno != EBADF);
421 * whenever a timer is up, this function needs to be called.
422 * Removes the fired timer, calls the callback and resets mTimeout
424 void CAmSocketHandler::timerUp()
426 //first fire the event
427 mListActiveTimer.front().callback->Call(mListActiveTimer.front().handle, mListActiveTimer.front().userData);
429 //then remove the first timer, the one who fired
430 mListActiveTimer.pop_front();
431 if (!mListActiveTimer.empty())
433 //substract the old value from all timers in the list
434 std::for_each(mListActiveTimer.begin(), mListActiveTimer.end(), CAmShSubstractTime(mTimeout));
435 mTimeout = mListActiveTimer.front().countdown;
439 mTimeout.tv_nsec = -1;
440 mTimeout.tv_sec = -1;
447 void CAmSocketHandler::initTimer()
449 if (!mListActiveTimer.empty())
451 mTimeout = mListActiveTimer.front().countdown;
455 mTimeout.tv_nsec = -1;
456 mTimeout.tv_sec = -1;
461 * convert timespec to milliseconds
462 * @param time time in timespec
463 * @return time in milliseconds
465 inline int CAmSocketHandler::timespec2ms(const timespec & time)
467 return ((time.tv_nsec == -1 && time.tv_sec == -1) ? -1 : time.tv_sec * 1000 + time.tv_nsec / 1000000);
470 inline timespec* CAmSocketHandler::insertTime(timespec& buffertime)
472 buffertime.tv_nsec = mTimeout.tv_nsec;
473 buffertime.tv_sec = mTimeout.tv_sec;
474 return ((mTimeout.tv_nsec == -1 && mTimeout.tv_sec == -1) ? NULL : &buffertime);
478 * functor to easy substract from each countdown
479 * @param t value to substract from
481 void CAmSocketHandler::CAmShSubstractTime::operator ()(sh_timer_s & t) const
484 if ((val = t.countdown.tv_nsec - param.tv_nsec) < 0)
486 t.countdown.tv_nsec = 1000000000 + val;
487 t.countdown.tv_sec--;
491 t.countdown.tv_nsec = val;
493 (t.countdown.tv_sec - param.tv_sec) < 0 ? 0 : (t.countdown.tv_sec -= param.tv_sec);
496 void CAmSocketHandler::CAmShCopyPollfd::operator ()(const sh_poll_s & row)
498 pollfd temp = row.pollfdValue;
500 mArray.push_back(temp);