add basic support for repeated timers (doesn't work yet)
[enigma2.git] / 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 msec", this, nextActivation.tv_sec, nextActivation.tv_usec, msek);
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         
164         int poll_timeout = -1; /* infinite in case of empty timer list */
165         
166         if (m_timer_list)
167         {
168                 singleLock s(recalcLock);
169                 poll_timeout = timeval_to_usec(m_timer_list.begin()->getNextActivation() - now);
170                         /* if current timer already passed, don't delay infinite. */
171                 if (poll_timeout < 0)
172                         poll_timeout = 0;
173                         
174                         /* convert us to ms */
175                 poll_timeout /= 1000;
176         }
177         
178         int ret = 0;
179
180         if (poll_timeout)
181         {
182                         // build the poll aray
183                 int fdcount = notifiers.size();
184                 pollfd* pfd = new pollfd[fdcount];  // make new pollfd array
185
186                 std::map<int,eSocketNotifier*>::iterator it(notifiers.begin());
187                 for (int i=0; i < fdcount; i++, it++)
188                 {
189                         pfd[i].fd = it->first;
190                         pfd[i].events = it->second->getRequested();
191                 }
192
193                 ret = poll(pfd, fdcount, poll_timeout);
194
195                         /* ret > 0 means that there are some active poll entries. */
196                 if (ret > 0)
197                 {
198                         for (int i=0; i < fdcount ; i++)
199                         {
200                                 if (notifiers.find(pfd[i].fd) == notifiers.end())
201                                         continue;
202                                 
203                                 int req = notifiers[pfd[i].fd]->getRequested();
204                                 
205                                 if (pfd[i].revents & req)
206                                 {
207                                         notifiers[pfd[i].fd]->activate(pfd[i].revents);
208                                 
209                                         if (!--ret)
210                                                 break;
211                                 } else if (pfd[i].revents & (POLLERR|POLLHUP|POLLNVAL))
212                                         eFatal("poll: unhandled POLLERR/HUP/NVAL for fd %d(%d) -> FIX YOUR CODE", pfd[i].fd,pfd[i].revents);
213                         }
214                 } else if (ret < 0)
215                 {
216                                 /* when we got a signal, we get EINTR. we do not care, 
217                                    because we check current time in timers anyway. */
218                         if (errno != EINTR)
219                                 eDebug("poll made error (%m)");
220                         else
221                                 ret = 0;
222                 }
223                 delete [] pfd;
224         }
225         
226                 /* when we not processed anything, check timers. */
227         if (!ret)
228         {
229                 singleLock s(recalcLock);
230
231                         /* process all timers which are ready. first remove them out of the list. */
232                 while ((!m_timer_list.empty()) && (m_timer_list.begin()->getNextActivation() < now))
233                         m_timer_list.begin()->activate();
234         }
235 }
236
237 void eMainloop::addTimer(eTimer* e)
238 {
239         m_timer_list.insert_in_order(e);
240 }
241
242 void eMainloop::removeTimer(eTimer* e)
243 {
244         m_timer_list.remove(e);
245 }
246
247 int eMainloop::exec()
248 {
249         if (!loop_level)
250         {
251                 app_quit_now = false;
252                 app_exit_loop = false;
253                 enter_loop();
254         }
255         return retval;
256 }
257
258 void eMainloop::enter_loop()
259 {
260         loop_level++;
261         // Status der vorhandenen Loop merken
262         bool old_exit_loop = app_exit_loop;
263
264         app_exit_loop = false;
265
266         while (!app_exit_loop && !app_quit_now)
267                 processOneEvent();
268
269         // wiederherstellen der vorherigen app_exit_loop
270         app_exit_loop = old_exit_loop;
271
272         --loop_level;
273
274         if (!loop_level)
275         {
276                 // do something here on exit the last loop
277         }
278 }
279
280 void eMainloop::exit_loop()  // call this to leave the current loop
281 {
282         app_exit_loop = true;
283 }
284
285 void eMainloop::quit( int ret )   // call this to leave all loops
286 {
287         retval=ret;
288         app_quit_now = true;
289 }
290
291 void eMainloop::addTimeOffset(int offset)
292 {
293         for (ePtrList<eMainloop>::iterator it(eMainloop::existing_loops)
294                 ;it != eMainloop::existing_loops.end(); ++it)
295         {
296                 singleLock s(it->recalcLock);
297                 for (ePtrList<eTimer>::iterator tit = it->m_timer_list.begin(); tit != it->m_timer_list.end(); ++tit )
298                         tit->addTimeOffset(offset);
299         }
300 }
301
302 eApplication* eApp = 0;