base: fix a very theoretical performance problem
[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         nextActivation += (msek<0 ? 0 : msek);
49 //      eDebug("this = %p\nnow sec = %d, usec = %d\nadd %d msec", this, nextActivation.tv_sec, nextActivation.tv_usec, 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         notifiers.insert(std::pair<int,eSocketNotifier*> (sn->getFD(), sn));
120 }
121
122 void eMainloop::removeSocketNotifier(eSocketNotifier *sn)
123 {
124         notifiers.erase(sn->getFD());
125 }
126
127 void eMainloop::processOneEvent()
128 {
129                 /* notes:
130                   - we should use epoll(4)
131                   - timer are checked twice. there was a strong reason for it, but i can't remember. (FIXME)
132                   - for each time, we gettimeofday() and check wether the timer should fire.
133                     we should do this all better - we know how long the poll last, so we know which
134                     timers should fire. Problem is that a timer handler could have required so
135                     much time that another timer fired.
136
137                     A probably structure could look
138
139                     while (1)
140                     {
141                             time = gettimeofday()
142                             timeout = calculate_pending_timers(time);
143
144                       doPoll(timeout or infinite);
145
146                         if (poll_had_results)
147                                 handle_poll_handler();
148                         else
149                                     fire_timers(time + timeout)
150                           }
151
152                           the gettimeofday() call is required because fire_timers could last more
153                           than nothing.
154
155                           when poll did no timeout, we don't handle timers, as this will be done
156                           in the next iteration (without adding overhead - we had to get the new
157                           time anyway
158                 */
159
160                 /* get current time */
161         timeval now;
162         gettimeofday(&now, 0);
163         m_now_is_invalid = 0;
164         
165         int poll_timeout = -1; /* infinite in case of empty timer list */
166         
167         if (m_timer_list)
168         {
169                 singleLock s(recalcLock);
170                 poll_timeout = timeval_to_usec(m_timer_list.begin()->getNextActivation() - now);
171                         /* if current timer already passed, don't delay infinite. */
172                 if (poll_timeout < 0)
173                         poll_timeout = 0;
174                         
175                         /* convert us to ms */
176                 poll_timeout /= 1000;
177         }
178         
179         int ret = 0;
180
181         if (poll_timeout)
182         {
183                         // build the poll aray
184                 int fdcount = notifiers.size();
185                 pollfd* pfd = new pollfd[fdcount];  // make new pollfd array
186
187                 std::map<int,eSocketNotifier*>::iterator it(notifiers.begin());
188                 for (int i=0; i < fdcount; i++, it++)
189                 {
190                         pfd[i].fd = it->first;
191                         pfd[i].events = it->second->getRequested();
192                 }
193
194                 ret = poll(pfd, fdcount, poll_timeout);
195
196                         /* ret > 0 means that there are some active poll entries. */
197                 if (ret > 0)
198                 {
199                         for (int i=0; i < fdcount ; i++)
200                         {
201                                 if (notifiers.find(pfd[i].fd) == notifiers.end())
202                                         continue;
203                                 
204                                 int req = notifiers[pfd[i].fd]->getRequested();
205                                 
206                                 if (pfd[i].revents & req)
207                                 {
208                                         notifiers[pfd[i].fd]->activate(pfd[i].revents);
209                                 
210                                         if (!--ret)
211                                                 break;
212                                 } else if (pfd[i].revents & (POLLERR|POLLHUP|POLLNVAL))
213                                         eFatal("poll: unhandled POLLERR/HUP/NVAL for fd %d(%d) -> FIX YOUR CODE", pfd[i].fd,pfd[i].revents);
214                         }
215                 } else if (ret < 0)
216                 {
217                                 /* when we got a signal, we get EINTR. we do not care, 
218                                    because we check current time in timers anyway. */
219                         if (errno != EINTR)
220                                 eDebug("poll made error (%m)");
221                         else
222                                 ret = 0;
223                 }
224                 delete [] pfd;
225         }
226         
227                 /* when we not processed anything, check timers. */
228         if (!ret)
229         {
230                         /* we know that this time has passed. */
231                 now += poll_timeout;
232                 
233                 singleLock s(recalcLock);
234
235                         /* this will never change while we have the recalcLock */
236                         /* we can savely return here, the timer will be re-checked soon. */
237                 if (m_now_is_invalid)
238                         return;
239
240                         /* process all timers which are ready. first remove them out of the list. */
241                 while ((!m_timer_list.empty()) && (m_timer_list.begin()->getNextActivation() <= now))
242                         m_timer_list.begin()->activate();
243         }
244 }
245
246 void eMainloop::addTimer(eTimer* e)
247 {
248         m_timer_list.insert_in_order(e);
249 }
250
251 void eMainloop::removeTimer(eTimer* e)
252 {
253         m_timer_list.remove(e);
254 }
255
256 int eMainloop::exec()
257 {
258         if (!loop_level)
259         {
260                 app_quit_now = false;
261                 app_exit_loop = false;
262                 enter_loop();
263         }
264         return retval;
265 }
266
267 void eMainloop::enter_loop()
268 {
269         loop_level++;
270         // Status der vorhandenen Loop merken
271         bool old_exit_loop = app_exit_loop;
272
273         app_exit_loop = false;
274
275         while (!app_exit_loop && !app_quit_now)
276                 processOneEvent();
277
278         // wiederherstellen der vorherigen app_exit_loop
279         app_exit_loop = old_exit_loop;
280
281         --loop_level;
282
283         if (!loop_level)
284         {
285                 // do something here on exit the last loop
286         }
287 }
288
289 void eMainloop::exit_loop()  // call this to leave the current loop
290 {
291         app_exit_loop = true;
292 }
293
294 void eMainloop::quit( int ret )   // call this to leave all loops
295 {
296         retval=ret;
297         app_quit_now = true;
298 }
299
300 void eMainloop::addTimeOffset(int offset)
301 {
302         for (ePtrList<eMainloop>::iterator it(eMainloop::existing_loops)
303                 ;it != eMainloop::existing_loops.end(); ++it)
304         {
305                 singleLock s(it->recalcLock);
306                 it->m_now_is_invalid = 1;
307                 for (ePtrList<eTimer>::iterator tit = it->m_timer_list.begin(); tit != it->m_timer_list.end(); ++tit )
308                         tit->addTimeOffset(offset);
309         }
310 }
311
312 eApplication* eApp = 0;