fix bug in mainloop (this fixes sometimes no more responding e2)
[vuplus_dvbapp] / lib / base / ebase.cpp
1 #include <lib/base/ebase.h>
2
3 #include <fcntl.h>
4 #include <unistd.h>
5 #include <errno.h>
6
7 #include <lib/base/eerror.h>
8 #include <lib/base/elock.h>
9
10 eSocketNotifier::eSocketNotifier(eMainloop *context, int fd, int requested, bool startnow): context(*context), fd(fd), state(0), requested(requested)
11 {
12         if (startnow)   
13                 start();
14 }
15
16 eSocketNotifier::~eSocketNotifier()
17 {
18         stop();
19 }
20
21 void eSocketNotifier::start()
22 {
23         if (state)
24                 stop();
25
26         context.addSocketNotifier(this);
27         state=1;
28 }
29
30 void eSocketNotifier::stop()
31 {
32         if (state)
33                 context.removeSocketNotifier(this);
34
35         state=0;
36 }
37
38                                         // timer
39 void eTimer::start(long msek, bool singleShot)
40 {
41         if (bActive)
42                 stop();
43
44         bActive = true;
45         bSingleShot = singleShot;
46         interval = msek;
47         gettimeofday(&nextActivation, 0);
48 //      eDebug("this = %p\nnow sec = %d, usec = %d\nadd %d msec", this, nextActivation.tv_sec, nextActivation.tv_usec, msek);
49         nextActivation += (msek<0 ? 0 : msek);
50 //      eDebug("next Activation sec = %d, usec = %d", nextActivation.tv_sec, nextActivation.tv_usec );
51         context.addTimer(this);
52 }
53
54 void eTimer::startLongTimer( int seconds )
55 {
56         if (bActive)
57                 stop();
58
59         bActive = bSingleShot = true;
60         interval = 0;
61         gettimeofday(&nextActivation, 0);
62 //      eDebug("this = %p\nnow sec = %d, usec = %d\nadd %d sec", this, nextActivation.tv_sec, nextActivation.tv_usec, seconds);
63         if ( seconds > 0 )
64                 nextActivation.tv_sec += seconds;
65 //      eDebug("next Activation sec = %d, usec = %d", nextActivation.tv_sec, nextActivation.tv_usec );
66         context.addTimer(this);
67 }
68
69 void eTimer::stop()
70 {
71         if (bActive)
72         {
73                 bActive=false;
74                 context.removeTimer(this);
75         }
76 }
77
78 void eTimer::changeInterval(long msek)
79 {
80         if (bActive)  // Timer is running?
81         {
82                 context.removeTimer(this);       // then stop
83                 nextActivation -= interval;  // sub old interval
84         }
85         else
86                 bActive=true; // then activate Timer
87
88         interval = msek;                                                // set new Interval
89         nextActivation += interval;             // calc nextActivation
90
91         context.addTimer(this);                         // add Timer to context TimerList
92 }
93
94 void eTimer::activate()   // Internal Funktion... called from eApplication
95 {
96         context.removeTimer(this);
97
98         if (!bSingleShot)
99         {
100                 nextActivation += interval;
101                 context.addTimer(this);
102         }
103         else
104                 bActive=false;
105
106         /*emit*/ timeout();
107 }
108
109 void eTimer::addTimeOffset( int offset )
110 {
111         nextActivation.tv_sec += offset;
112 }
113
114 // mainloop
115 ePtrList<eMainloop> eMainloop::existing_loops;
116
117 void eMainloop::addSocketNotifier(eSocketNotifier *sn)
118 {
119         int fd = sn->getFD();
120         ASSERT(notifiers.find(fd) == notifiers.end());
121         ASSERT(new_notifiers.find(fd) == new_notifiers.end());
122         new_notifiers[fd]=sn;
123 }
124
125 void eMainloop::removeSocketNotifier(eSocketNotifier *sn)
126 {
127         for (std::map<int,eSocketNotifier*>::iterator i = notifiers.find(sn->getFD());
128                         i != notifiers.end();
129                         ++i)
130                 if (i->second == sn)
131                         return notifiers.erase(i);
132         for (std::map<int,eSocketNotifier*>::iterator i = new_notifiers.find(sn->getFD());
133                         i != new_notifiers.end();
134                         ++i)
135                 if (i->second == sn)
136                         return new_notifiers.erase(i);
137         eFatal("removed socket notifier which is not present");
138 }
139
140 int eMainloop::processOneEvent(unsigned int user_timeout, PyObject **res, ePyObject additional)
141 {
142         int return_reason = 0;
143                 /* get current time */
144         timeval now;
145         gettimeofday(&now, 0);
146         m_now_is_invalid = 0;
147                 
148         if (additional && !PyDict_Check(additional))
149                 eFatal("additional, but it's not dict");
150                 
151         if (additional && !res)
152                 eFatal("additional, but no res");
153                 
154         int poll_timeout = -1; /* infinite in case of empty timer list */
155                 
156         if (m_timer_list)
157         {
158                 singleLock s(recalcLock);
159                 poll_timeout = timeval_to_usec(m_timer_list.begin()->getNextActivation() - now);
160                         /* if current timer already passed, don't delay infinite. */
161                 if (poll_timeout < 0)
162                         poll_timeout = 0;
163                         
164                         /* convert us to ms */
165                 poll_timeout /= 1000;
166         }
167         
168         if ((user_timeout > 0) && (poll_timeout > 0) && ((unsigned int)poll_timeout > user_timeout))
169         {
170                 poll_timeout = user_timeout;
171                 return_reason = 1;
172         }
173
174         for (std::map<int, eSocketNotifier*>::iterator it(new_notifiers.begin()); it != new_notifiers.end();)
175         {
176                 notifiers[it->first]=it->second;
177                 new_notifiers.erase(it++);
178         }
179
180         int nativecount=notifiers.size(),
181                 fdcount=nativecount,
182                 ret=0;
183
184         if (additional)
185                 fdcount += PyDict_Size(additional);
186                 
187                 // build the poll aray
188         pollfd pfd[fdcount];  // make new pollfd array
189         std::map<int,eSocketNotifier*>::iterator it = notifiers.begin();
190         int i=0;
191         for (; i < nativecount; ++i, ++it)
192         {
193                 pfd[i].fd = it->first;
194                 pfd[i].events = it->second->getRequested();
195         }
196         
197         if (additional)
198         {
199                 PyObject *key, *val;
200                 int pos=0;
201                 while (PyDict_Next(additional, &pos, &key, &val)) {
202                         pfd[i].fd = PyObject_AsFileDescriptor(key);
203                         pfd[i++].events = PyInt_AsLong(val);
204                 }
205         }
206         
207         ret = ::poll(pfd, fdcount, poll_timeout);
208                         /* ret > 0 means that there are some active poll entries. */
209         if (ret > 0)
210         {
211                 int i=0;
212                 return_reason = 0;
213                 for (; i < nativecount ; ++i)
214                 {
215                         if (pfd[i].revents)
216                         {
217                                 int handled = 0;
218                                 it = notifiers.find(pfd[i].fd);
219                                 if (it != notifiers.end())
220                                 {
221                                         int req = it->second->getRequested();
222                                         handled |= req;
223                                         if (pfd[i].revents & req)
224                                                 it->second->activate(pfd[i].revents);
225                                 }
226                                 pfd[i].revents &= ~handled;
227                                 if (pfd[i].revents & (POLLERR|POLLHUP|POLLNVAL))
228                                         eDebug("poll: unhandled POLLERR/HUP/NVAL for fd %d(%d)", pfd[i].fd, pfd[i].revents);
229                         }
230                 }
231                 for (; i < fdcount; ++i)
232                 {
233                         if (pfd[i].revents)
234                         {
235                                 if (!*res)
236                                         *res = PyList_New(0);
237                                 ePyObject it = PyTuple_New(2);
238                                 PyTuple_SET_ITEM(it, 0, PyInt_FromLong(pfd[i].fd));
239                                 PyTuple_SET_ITEM(it, 1, PyInt_FromLong(pfd[i].revents));
240                                 PyList_Append(*res, it);
241                                 Py_DECREF(it);
242                         }
243                 }
244                         
245                 ret = 1; /* poll did not timeout. */
246         } else if (ret < 0)
247         {
248                         /* when we got a signal, we get EINTR. */
249                 if (errno != EINTR)
250                         eDebug("poll made error (%m)");
251                 else
252                 {
253                         return_reason = 2;
254                         ret = -1; /* don't assume the timeout has passed when we got a signal */
255                 }
256         }
257         
258                 /* when we not processed anything, check timers. */
259         if (!m_timer_list.empty())
260         {
261                         /* we know that this time has passed. */
262                 singleLock s(recalcLock);
263
264                 if (ret || m_now_is_invalid)
265                         gettimeofday(&now, 0);
266                 else
267                         now += poll_timeout;
268
269                         /* process all timers which are ready. first remove them out of the list. */
270                 while ((!m_timer_list.empty()) && (m_timer_list.begin()->getNextActivation() <= now))
271                         m_timer_list.begin()->activate();
272         }
273         
274         return return_reason;
275 }
276
277 void eMainloop::addTimer(eTimer* e)
278 {
279         m_timer_list.insert_in_order(e);
280 }
281
282 void eMainloop::removeTimer(eTimer* e)
283 {
284         m_timer_list.remove(e);
285 }
286
287 int eMainloop::iterate(unsigned int user_timeout, PyObject **res, ePyObject dict)
288 {
289         int ret = 0;
290         
291         timeval user_timer;
292         gettimeofday(&user_timer, 0);
293         user_timer += user_timeout;
294
295                 /* TODO: this code just became ugly. fix that. */
296         do
297         {
298                 if (m_interrupt_requested)
299                 {
300                         m_interrupt_requested = 0;
301                         return 0;
302                 }
303                 if (app_quit_now) return -1;
304                 timeval now, timeout;
305                 gettimeofday(&now, 0);
306                 timeout = user_timer - now;
307                 
308                 if (user_timeout && (user_timer <= now))
309                         return 0;
310                 
311                 int to = 0;
312                 if (user_timeout)
313                         to = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
314                 
315                 ret = processOneEvent(to, res, dict);
316                 if (res && *res)
317                         return ret;
318         } while (ret == 0);
319         
320         return ret;
321 }
322
323 int eMainloop::runLoop()
324 {
325         while (!app_quit_now)
326                 iterate();
327         return retval;
328 }
329
330 void eMainloop::reset()
331 {
332         app_quit_now=false;
333 }
334
335 PyObject *eMainloop::poll(ePyObject timeout, ePyObject dict)
336 {
337         PyObject *res=0;
338         
339         if (app_quit_now)
340         {
341                 Py_INCREF(Py_None);
342                 return Py_None;
343         }
344         
345         int user_timeout = (timeout == Py_None) ? 0 : PyInt_AsLong(timeout);
346
347         iterate(user_timeout, &res, dict);
348         ePyObject ret(res);
349         
350         if (!ret) /* return empty list on timeout */
351                 return PyList_New(0);
352         
353         return ret;
354 }
355
356 void eMainloop::interruptPoll()
357 {
358         m_interrupt_requested = 1;
359 }
360
361 void eMainloop::quit(int ret)
362 {
363         retval = ret;
364         app_quit_now = true;
365 }
366
367 void eMainloop::addTimeOffset(int offset)
368 {
369         for (ePtrList<eMainloop>::iterator it(eMainloop::existing_loops)
370                 ;it != eMainloop::existing_loops.end(); ++it)
371         {
372                 singleLock s(it->recalcLock);
373                 it->m_now_is_invalid = 1;
374                 for (ePtrList<eTimer>::iterator tit = it->m_timer_list.begin(); tit != it->m_timer_list.end(); ++tit )
375                         tit->addTimeOffset(offset);
376         }
377 }
378
379 eApplication* eApp = 0;