fix lengthfield parsing
[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 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                 singleLock s(recalcLock);
231
232                         /* this will never change while we have the recalcLock */
233                         /* we can savely return here, the timer will be re-checked soon. */
234                 if (m_now_is_invalid)
235                         return;
236
237                         /* process all timers which are ready. first remove them out of the list. */
238                 while ((!m_timer_list.empty()) && (m_timer_list.begin()->getNextActivation() < now))
239                         m_timer_list.begin()->activate();
240         }
241 }
242
243 void eMainloop::addTimer(eTimer* e)
244 {
245         m_timer_list.insert_in_order(e);
246 }
247
248 void eMainloop::removeTimer(eTimer* e)
249 {
250         m_timer_list.remove(e);
251 }
252
253 int eMainloop::exec()
254 {
255         if (!loop_level)
256         {
257                 app_quit_now = false;
258                 app_exit_loop = false;
259                 enter_loop();
260         }
261         return retval;
262 }
263
264 void eMainloop::enter_loop()
265 {
266         loop_level++;
267         // Status der vorhandenen Loop merken
268         bool old_exit_loop = app_exit_loop;
269
270         app_exit_loop = false;
271
272         while (!app_exit_loop && !app_quit_now)
273                 processOneEvent();
274
275         // wiederherstellen der vorherigen app_exit_loop
276         app_exit_loop = old_exit_loop;
277
278         --loop_level;
279
280         if (!loop_level)
281         {
282                 // do something here on exit the last loop
283         }
284 }
285
286 void eMainloop::exit_loop()  // call this to leave the current loop
287 {
288         app_exit_loop = true;
289 }
290
291 void eMainloop::quit( int ret )   // call this to leave all loops
292 {
293         retval=ret;
294         app_quit_now = true;
295 }
296
297 void eMainloop::addTimeOffset(int offset)
298 {
299         for (ePtrList<eMainloop>::iterator it(eMainloop::existing_loops)
300                 ;it != eMainloop::existing_loops.end(); ++it)
301         {
302                 singleLock s(it->recalcLock);
303                 it->m_now_is_invalid = 1;
304                 for (ePtrList<eTimer>::iterator tit = it->m_timer_list.begin(); tit != it->m_timer_list.end(); ++tit )
305                         tit->addTimeOffset(offset);
306         }
307 }
308
309 eApplication* eApp = 0;