]> mj.ucw.cz Git - libucw.git/blob - ucw/mainloop.c
6575210b2665baf7fc5e2cc2cfd267e8d5218fe2
[libucw.git] / ucw / mainloop.c
1 /*
2  *      UCW Library -- Main Loop
3  *
4  *      (c) 2004--2011 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #undef LOCAL_DEBUG
11
12 #include "ucw/lib.h"
13 #include "ucw/heap.h"
14 #include "ucw/mainloop.h"
15 #include "ucw/threads.h"
16 #include "ucw/gary.h"
17
18 #include <stdio.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <signal.h>
23 #include <fcntl.h>
24 #include <errno.h>
25 #include <time.h>
26 #include <sys/poll.h>
27 #include <sys/wait.h>
28 #include <sys/time.h>
29
30 #ifdef CONFIG_UCW_THREADS
31 #include <pthread.h>
32 #define THREAD_SIGMASK pthread_sigmask
33 #else
34 #define THREAD_SIGMASK sigprocmask
35 #endif
36
37 #ifdef CONFIG_UCW_EPOLL
38 #include <sys/epoll.h>
39 #endif
40
41 #define MAIN_TIMER_LESS(x,y) ((x)->expires < (y)->expires)
42 #define MAIN_TIMER_SWAP(heap,a,b,t) (t=heap[a], heap[a]=heap[b], heap[b]=t, heap[a]->index=(a), heap[b]->index=(b))
43
44 #define EPOLL_BUF_SIZE 256
45
46 static void
47 do_main_get_time(struct main_context *m)
48 {
49   struct timeval tv;
50   gettimeofday(&tv, NULL);
51   m->now_seconds = tv.tv_sec;
52   m->now = (timestamp_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
53 }
54
55 struct main_context *
56 main_new(void)
57 {
58   struct main_context *m = xmalloc_zero(sizeof(*m));
59
60   DBG("MAIN: New context");
61   clist_init(&m->file_list);
62   clist_init(&m->file_active_list);
63   clist_init(&m->hook_list);
64   clist_init(&m->hook_done_list);
65   clist_init(&m->process_list);
66   clist_init(&m->signal_list);
67 #ifdef CONFIG_UCW_EPOLL
68   m->epoll_fd = epoll_create(64);
69   if (m->epoll_fd < 0)
70     die("epoll_create() failed: %m");
71   m->epoll_events = xmalloc(EPOLL_BUF_SIZE * sizeof(struct epoll_event *));
72   clist_init(&m->file_recalc_list);
73 #else
74   m->poll_table_obsolete = 1;
75 #endif
76   do_main_get_time(m);
77   sigemptyset(&m->want_signals);
78   m->sig_pipe_recv = m->sig_pipe_send = -1;
79
80   return m;
81 }
82
83 void
84 main_delete(struct main_context *m)
85 {
86   struct main_context *prev = main_switch_context(m);
87
88   if (m->sigchld_handler)
89     signal_del(m->sigchld_handler);
90   if (m->sig_pipe_file)
91     file_del(m->sig_pipe_file);
92   if (m->sig_pipe_recv >= 0)
93     {
94       close(m->sig_pipe_recv);
95       close(m->sig_pipe_send);
96     }
97   ASSERT(clist_empty(&m->file_list));
98   ASSERT(clist_empty(&m->file_active_list));
99   ASSERT(clist_empty(&m->hook_list));
100   ASSERT(clist_empty(&m->hook_done_list));
101   ASSERT(clist_empty(&m->process_list));
102   ASSERT(clist_empty(&m->signal_list));
103   GARY_FREE(m->timer_table);
104 #ifdef CONFIG_UCW_EPOLL
105   ASSERT(clist_empty(&m->file_recalc_list));
106   xfree(m->epoll_events);
107   close(m->epoll_fd);
108 #else
109   GARY_FREE(m->poll_table);
110   GARY_FREE(m->poll_file_table);
111 #endif
112   xfree(m);
113   // FIXME: Some mechanism for cleaning up after fork()
114
115   main_switch_context((prev == m) ? NULL : prev);
116 }
117
118 struct main_context *
119 main_switch_context(struct main_context *m)
120 {
121   struct ucwlib_context *c = ucwlib_thread_context();
122   struct main_context *m0 = c->main_context;
123
124   /*
125    *  Not only we need to switch the signal sets of the two contexts,
126    *  but it is also necessary to avoid invoking a signal handler
127    *  in the middle of changing c->main_context.
128    */
129   if (m0 && !clist_empty(&m0->signal_list))
130     THREAD_SIGMASK(SIG_BLOCK, &m0->want_signals, NULL);
131   c->main_context = m;
132   if (m && !clist_empty(&m->signal_list))
133     THREAD_SIGMASK(SIG_UNBLOCK, &m->want_signals, NULL);
134
135   return m0;
136 }
137
138 struct main_context *
139 main_current(void)
140 {
141   struct ucwlib_context *c = ucwlib_thread_context();
142   struct main_context *m = c->main_context;
143   ASSERT(m);
144   return m;
145 }
146
147 void
148 main_init(void)
149 {
150   struct main_context *m = main_switch_context(main_new());
151   ASSERT(!m);
152 }
153
154 void
155 main_cleanup(void)
156 {
157   main_delete(main_current());
158 }
159
160 void
161 main_get_time(void)
162 {
163   do_main_get_time(main_current());
164 }
165
166 static inline uns
167 count_timers(struct main_context *m)
168 {
169   if (m->timer_table)
170     return GARY_SIZE(m->timer_table) - 1;
171   else
172     return 0;
173 }
174
175 void
176 timer_add(struct main_timer *tm, timestamp_t expires)
177 {
178   struct main_context *m = main_current();
179
180   if (!m->timer_table)
181     {
182       GARY_INIT(m->timer_table, 1);
183       m->timer_table[0] = NULL;
184     }
185
186   if (expires)
187     DBG("MAIN: Setting timer %p (expire at now+%lld)", tm, (long long)(expires - m->now));
188   else
189     DBG("MAIN: Clearing timer %p", tm);
190   uns num_timers = count_timers(m);
191   if (tm->expires < expires)
192     {
193       if (!tm->expires)
194         {
195           tm->expires = expires;
196           tm->index = num_timers + 1;
197           *GARY_PUSH(m->timer_table, 1) = tm;
198           HEAP_INSERT(struct main_timer *, m->timer_table, tm->index, MAIN_TIMER_LESS, MAIN_TIMER_SWAP);
199         }
200       else
201         {
202           tm->expires = expires;
203           HEAP_INCREASE(struct main_timer *, m->timer_table, num_timers, MAIN_TIMER_LESS, MAIN_TIMER_SWAP, tm->index);
204         }
205     }
206   else if (tm->expires > expires)
207     {
208       if (!expires)
209         {
210           ASSERT(tm->index && tm->index <= num_timers);
211           HEAP_DELETE(struct main_timer *, m->timer_table, num_timers, MAIN_TIMER_LESS, MAIN_TIMER_SWAP, tm->index);
212           tm->index = 0;
213           tm->expires = 0;
214           GARY_POP(m->timer_table, 1);
215         }
216       else
217         {
218           tm->expires = expires;
219           HEAP_DECREASE(struct main_timer *, m->timer_table, num_timers, MAIN_TIMER_LESS, MAIN_TIMER_SWAP, tm->index);
220         }
221     }
222 }
223
224 void
225 timer_add_rel(struct main_timer *tm, timestamp_t expires_delta)
226 {
227   struct main_context *m = main_current();
228   return timer_add(tm, m->now + expires_delta);
229 }
230
231 void
232 timer_del(struct main_timer *tm)
233 {
234   timer_add(tm, 0);
235 }
236
237 static uns
238 file_want_events(struct main_file *fi)
239 {
240   uns events = 0;
241   if (fi->read_handler)
242     events |= POLLIN | POLLHUP | POLLERR;
243   if (fi->write_handler)
244     events |= POLLOUT | POLLERR;
245   return events;
246 }
247
248 void
249 file_add(struct main_file *fi)
250 {
251   struct main_context *m = main_current();
252
253   DBG("MAIN: Adding file %p (fd=%d)", fi, fi->fd);
254   ASSERT(!clist_is_linked(&fi->n));
255   clist_add_tail(&m->file_list, &fi->n);
256   m->file_cnt++;
257 #ifdef CONFIG_UCW_EPOLL
258   struct epoll_event evt = {
259     .events = file_want_events(fi),
260     .data.ptr = fi,
261   };
262   if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, fi->fd, &evt) < 0)
263     die("epoll_ctl() failed: %m");
264   fi->last_want_events = evt.events;
265 #else
266   m->poll_table_obsolete = 1;
267 #endif
268   if (fcntl(fi->fd, F_SETFL, O_NONBLOCK) < 0)
269     msg(L_ERROR, "Error setting fd %d to non-blocking mode: %m. Keep fingers crossed.", fi->fd);
270 }
271
272 void
273 file_chg(struct main_file *fi)
274 {
275 #ifdef CONFIG_UCW_EPOLL
276   clist_remove(&fi->n);
277   clist_add_tail(&main_current()->file_recalc_list, &fi->n);
278 #else
279   struct pollfd *p = fi->pollfd;
280   if (p)
281     p->events = file_want_events(fi);
282 #endif
283 }
284
285 void
286 file_del(struct main_file *fi)
287 {
288   struct main_context *m = main_current();
289
290   DBG("MAIN: Deleting file %p (fd=%d)", fi, fi->fd);
291   ASSERT(clist_is_linked(&fi->n));
292   clist_unlink(&fi->n);
293   m->file_cnt--;
294 #ifdef CONFIG_UCW_EPOLL
295   if (epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, fi->fd, NULL) < 0)
296     die("epoll_ctl() failed: %m");
297 #else
298   m->poll_table_obsolete = 1;
299 #endif
300 }
301
302 void
303 file_close_all(void)
304 {
305   struct main_context *m = main_current();
306
307   CLIST_FOR_EACH(struct main_file *, f, m->file_list)
308     close(f->fd);
309 }
310
311 void
312 hook_add(struct main_hook *ho)
313 {
314   struct main_context *m = main_current();
315
316   DBG("MAIN: Adding hook %p", ho);
317   ASSERT(!clist_is_linked(&ho->n));
318   clist_add_tail(&m->hook_list, &ho->n);
319 }
320
321 void
322 hook_del(struct main_hook *ho)
323 {
324   DBG("MAIN: Deleting hook %p", ho);
325   ASSERT(clist_is_linked(&ho->n));
326   clist_unlink(&ho->n);
327 }
328
329 static void
330 sigchld_received(struct main_signal *sg UNUSED)
331 {
332   struct main_context *m = main_current();
333   int stat;
334   pid_t pid;
335
336   while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
337     {
338       DBG("MAIN: Child %d exited with status %x", pid, stat);
339       CLIST_FOR_EACH(struct main_process *, pr, m->process_list)
340         if (pr->pid == pid)
341           {
342             pr->status = stat;
343             process_del(pr);
344             format_exit_status(pr->status_msg, pr->status);
345             DBG("MAIN: Calling process exit handler");
346             pr->handler(pr);
347             break;
348           }
349     }
350 }
351
352 void
353 process_add(struct main_process *mp)
354 {
355   struct main_context *m = main_current();
356
357   DBG("MAIN: Adding process %p (pid=%d)", mp, mp->pid);
358   ASSERT(!clist_is_linked(&mp->n));
359   ASSERT(mp->handler);
360   clist_add_tail(&m->process_list, &mp->n);
361   if (!m->sigchld_handler)
362     {
363       struct main_signal *sg = xmalloc_zero(sizeof(*sg));
364       m->sigchld_handler = sg;
365       sg->signum = SIGCHLD;
366       sg->handler = sigchld_received;
367       signal_add(sg);
368     }
369 }
370
371 void
372 process_del(struct main_process *mp)
373 {
374   DBG("MAIN: Deleting process %p (pid=%d)", mp, mp->pid);
375   ASSERT(clist_is_linked(&mp->n));
376   clist_unlink(&mp->n);
377 }
378
379 int
380 process_fork(struct main_process *mp)
381 {
382   pid_t pid = fork();
383   if (pid < 0)
384     {
385       DBG("MAIN: Fork failed");
386       mp->status = -1;
387       format_exit_status(mp->status_msg, -1);
388       mp->handler(mp);
389       return 1;
390     }
391   else if (!pid)
392     return 0;
393   else
394     {
395       DBG("MAIN: Forked process %d", (int) pid);
396       mp->pid = pid;
397       process_add(mp);
398       return 1;
399     }
400 }
401
402 static int
403 pipe_read_handler(struct main_file *mf UNUSED)
404 {
405   struct main_context *m = main_current();
406   int signum;
407   int n = read(m->sig_pipe_recv, &signum, sizeof(signum));
408
409   if (n < 0)
410     {
411       if (errno != EAGAIN)
412         msg(L_ERROR, "Error reading signal pipe: %m");
413       return 0;
414     }
415   ASSERT(n == sizeof(signum));
416
417   DBG("MAIN: Sigpipe: received signal %d", signum);
418   struct main_signal iter = { .signum = -1 };
419   struct main_signal *sg = clist_head(&m->signal_list);
420   while (sg)
421     {
422       if (sg->signum == signum)
423         {
424           DBG("MAIN: Sigpipe: invoking handler %p", sg);
425           clist_insert_after(&iter.n, &sg->n);
426           sg->handler(sg);
427           sg = clist_next(&m->signal_list, &iter.n);
428           clist_remove(&iter.n);
429         }
430       else
431         sg = clist_next(&m->signal_list, &sg->n);
432     }
433
434   return 1;
435 }
436
437 static void
438 pipe_configure(int fd)
439 {
440   int flags;
441   if ((flags = fcntl(fd, F_GETFL)) < 0 || fcntl(fd, F_SETFL, flags|O_NONBLOCK) < 0)
442     die("Could not set file descriptor %d to non-blocking: %m", fd);
443 }
444
445 static void
446 pipe_setup(struct main_context *m)
447 {
448   DBG("MAIN: Sigpipe: Setting up the pipe");
449
450   int pipe_result[2];
451   if (pipe(pipe_result) == -1)
452     die("Could not create signal pipe: %m");
453   pipe_configure(pipe_result[0]);
454   pipe_configure(pipe_result[1]);
455   m->sig_pipe_recv = pipe_result[0];
456   m->sig_pipe_send = pipe_result[1];
457
458   struct main_file *f = xmalloc_zero(sizeof(*f));
459   m->sig_pipe_file = f;
460   f->fd = m->sig_pipe_recv;
461   f->read_handler = pipe_read_handler;
462   file_add(f);
463 }
464
465 static void
466 signal_handler_pipe(int signum)
467 {
468   struct main_context *m = main_current();
469 #ifdef LOCAL_DEBUG
470   msg(L_DEBUG | L_SIGHANDLER, "MAIN: Sigpipe: sending signal %d down the drain", signum);
471 #endif
472   write(m->sig_pipe_send, &signum, sizeof(signum));
473 }
474
475 void
476 signal_add(struct main_signal *ms)
477 {
478   struct main_context *m = main_current();
479
480   DBG("MAIN: Adding signal %p (sig=%d)", ms, ms->signum);
481
482   ASSERT(!clist_is_linked(&ms->n));
483   // Adding at the head of the list is better if we are in the middle of walking the list.
484   clist_add_head(&m->signal_list, &ms->n);
485   if (m->sig_pipe_recv < 0)
486     pipe_setup(m);
487
488   struct sigaction sa = {
489     .sa_handler = signal_handler_pipe,
490     .sa_flags = SA_NOCLDSTOP | SA_RESTART,
491   };
492   sigaction(ms->signum, &sa, NULL);
493
494   sigset_t ss;
495   sigemptyset(&ss);
496   sigaddset(&ss, ms->signum);
497   THREAD_SIGMASK(SIG_UNBLOCK, &ss, NULL);
498   sigaddset(&m->want_signals, ms->signum);
499 }
500
501 void
502 signal_del(struct main_signal *ms)
503 {
504   struct main_context *m = main_current();
505
506   DBG("MAIN: Deleting signal %p (sig=%d)", ms, ms->signum);
507
508   ASSERT(clist_is_linked(&ms->n));
509   clist_unlink(&ms->n);
510
511   int another = 0;
512   CLIST_FOR_EACH(struct main_signal *, s, m->signal_list)
513     if (s->signum == ms->signum)
514       another++;
515   if (!another)
516     {
517       sigset_t ss;
518       sigemptyset(&ss);
519       sigaddset(&ss, ms->signum);
520       THREAD_SIGMASK(SIG_BLOCK, &ss, NULL);
521       sigdelset(&m->want_signals, ms->signum);
522     }
523 }
524
525 void
526 main_debug_context(struct main_context *m UNUSED)
527 {
528 #ifdef CONFIG_DEBUG
529   msg(L_DEBUG, "### Main loop status on %lld", (long long) m->now);
530   msg(L_DEBUG, "\tActive timers:");
531   uns num_timers = count_timers(m);
532   for (uns i = 1; i <= num_timers; i++)
533     {
534       struct main_timer *tm = m->timer_table[i];
535       msg(L_DEBUG, "\t\t%p (expires %lld, data %p)", tm, (long long)(tm->expires ? tm->expires - m->now : 999999), tm->data);
536     }
537   msg(L_DEBUG, "\tActive files:");
538   CLIST_FOR_EACH(struct main_file *, fi, m->file_list)
539     msg(L_DEBUG, "\t\t%p (fd %d, rh %p, wh %p, data %p)",
540         fi, fi->fd, fi->read_handler, fi->write_handler, fi->data);
541   CLIST_FOR_EACH(struct main_file *, fi, m->file_active_list)
542     msg(L_DEBUG, "\t\t%p (fd %d, rh %p, wh %p, data %p) [pending events: %x]",
543         fi, fi->fd, fi->read_handler, fi->write_handler, fi->data, fi->events);
544     // FIXME: Can we display status of block_io requests somehow?
545 #ifdef CONFIG_UCW_EPOLL
546   CLIST_FOR_EACH(struct main_file *, fi, m->file_recalc_list)
547     msg(L_DEBUG, "\t\t%p (fd %d, rh %p, wh %p, data %p) [pending recalculation]",
548         fi, fi->fd, fi->read_handler, fi->write_handler, fi->data);
549 #endif
550   msg(L_DEBUG, "\tActive hooks:");
551   CLIST_FOR_EACH(struct main_hook *, ho, m->hook_done_list)
552     msg(L_DEBUG, "\t\t%p (func %p, data %p)", ho, ho->handler, ho->data);
553   CLIST_FOR_EACH(struct main_hook *, ho, m->hook_list)
554     msg(L_DEBUG, "\t\t%p (func %p, data %p)", ho, ho->handler, ho->data);
555   msg(L_DEBUG, "\tActive processes:");
556   CLIST_FOR_EACH(struct main_process *, pr, m->process_list)
557     msg(L_DEBUG, "\t\t%p (pid %d, func %p, data %p)", pr, pr->pid, pr->handler, pr->data);
558   msg(L_DEBUG, "\tActive signal catchers:");
559   CLIST_FOR_EACH(struct main_signal *, sg, m->signal_list)
560     if (sg->signum < 0)
561       msg(L_DEBUG, "\t\t(placeholder)");
562     else
563       msg(L_DEBUG, "\t\t%p (sig %d, func %p, data %p)", sg, sg->signum, sg->handler, sg->data);
564 #endif
565 }
566
567 static void
568 process_timers(struct main_context *m)
569 {
570   struct main_timer *tm;
571   while (count_timers(m) && (tm = m->timer_table[1])->expires <= m->now)
572     {
573       DBG("MAIN: Timer %p expired at now-%lld", tm, (long long)(m->now - tm->expires));
574       tm->handler(tm);
575     }
576 }
577
578 static enum main_hook_return
579 process_hooks(struct main_context *m)
580 {
581   int hook_min = HOOK_RETRY;
582   int hook_max = HOOK_SHUTDOWN;
583   struct main_hook *ho;
584
585   while (ho = clist_remove_head(&m->hook_list))
586     {
587       clist_add_tail(&m->hook_done_list, &ho->n);
588       DBG("MAIN: Hook %p", ho);
589       int ret = ho->handler(ho);
590       hook_min = MIN(hook_min, ret);
591       hook_max = MAX(hook_max, ret);
592     }
593   clist_move(&m->hook_list, &m->hook_done_list);
594   if (hook_min == HOOK_SHUTDOWN ||
595     hook_min == HOOK_DONE && hook_max == HOOK_DONE ||
596     m->shutdown)
597     {
598       DBG("MAIN: Shut down by %s", m->shutdown ? "main_shutdown" : "a hook");
599       return HOOK_SHUTDOWN;
600     }
601   if (hook_max == HOOK_RETRY)
602     return HOOK_RETRY;
603   else
604     return HOOK_IDLE;
605 }
606
607 #ifdef CONFIG_UCW_EPOLL
608
609 static void
610 recalc_files(struct main_context *m)
611 {
612   struct main_file *fi;
613
614   while (fi = clist_remove_head(&m->file_recalc_list))
615     {
616       struct epoll_event evt = {
617         .events = file_want_events(fi),
618         .data.ptr = fi,
619       };
620       if (evt.events != fi->last_want_events)
621         {
622           DBG("MAIN: Changing requested events for fd %d to %x", fi->fd, evt.events);
623           fi->last_want_events = evt.events;
624           if (epoll_ctl(main_current()->epoll_fd, EPOLL_CTL_MOD, fi->fd, &evt) < 0)
625             die("epoll_ctl() failed: %m");
626         }
627       clist_add_tail(&m->file_list, &fi->n);
628     }
629 }
630
631 #else
632
633 static void
634 rebuild_poll_table(struct main_context *m)
635 {
636   GARY_INIT_OR_RESIZE(m->poll_table, m->file_cnt);
637   GARY_INIT_OR_RESIZE(m->poll_file_table, m->file_cnt);
638   DBG("MAIN: Rebuilding poll table: %d entries", m->file_cnt);
639
640   struct pollfd *p = m->poll_table;
641   struct main_file **pf = m->poll_file_table;
642   CLIST_FOR_EACH(struct main_file *, fi, m->file_list)
643     {
644       p->fd = fi->fd;
645       p->events = file_want_events(fi);
646       fi->pollfd = p++;
647       *pf++ = fi;
648     }
649   m->poll_table_obsolete = 0;
650 }
651
652 #endif
653
654 void
655 main_loop(void)
656 {
657   DBG("MAIN: Entering main_loop");
658   struct main_context *m = main_current();
659
660   do_main_get_time(m);
661   for (;;)
662     {
663       timestamp_t wake = m->now + 1000000000;
664       process_timers(m);
665       switch (process_hooks(m))
666         {
667         case HOOK_SHUTDOWN:
668           return;
669         case HOOK_RETRY:
670           wake = 0;
671           break;
672         default: ;
673         }
674       if (count_timers(m))
675         wake = MIN(wake, m->timer_table[1]->expires);
676       do_main_get_time(m);
677       int timeout = ((wake > m->now) ? wake - m->now : 0);
678
679 #ifdef CONFIG_UCW_EPOLL
680       recalc_files(m);
681       DBG("MAIN: Epoll for %d fds and timeout %d ms", m->file_cnt, timeout);
682       int n = epoll_wait(m->epoll_fd, m->epoll_events, EPOLL_BUF_SIZE, timeout);
683 #else
684       if (m->poll_table_obsolete)
685         rebuild_poll_table(m);
686       DBG("MAIN: Poll for %d fds and timeout %d ms", m->file_cnt, timeout);
687       int n = poll(m->poll_table, m->file_cnt, timeout);
688 #endif
689
690       DBG("\t-> %d events", n);
691       if (n < 0 && errno != EAGAIN && errno != EINTR)
692         die("(e)poll failed: %m");
693       timestamp_t old_now = m->now;
694       do_main_get_time(m);
695       m->idle_time += m->now - old_now;
696
697       if (n <= 0)
698         continue;
699
700       // Relink all files with a pending event to file_active_list
701 #ifdef CONFIG_UCW_EPOLL
702       for (int i=0; i<n; i++)
703         {
704           struct epoll_event *e = &m->epoll_events[i];
705           struct main_file *fi = e->data.ptr;
706           clist_remove(&fi->n);
707           clist_add_tail(&m->file_active_list, &fi->n);
708           fi->events = e->events;
709         }
710 #else
711       struct pollfd *p = m->poll_table;
712       struct main_file **pf = m->poll_file_table;
713       for (uns i=0; i < m->file_cnt; i++)
714         if (p[i].revents)
715           {
716             struct main_file *fi = pf[i];
717             clist_remove(&fi->n);
718             clist_add_tail(&m->file_active_list, &fi->n);
719             fi->events = p[i].revents;
720           }
721 #endif
722
723       /*
724        *  Process the buffered file events. This is pretty tricky, since
725        *  user callbacks can modify the file structure or even destroy it.
726        *  In such cases, we detect that the structure was relinked and stop
727        *  processing its events, leaving them for the next iteration of the
728        *  main loop.
729        */
730       struct main_file *fi;
731       while (fi = clist_head(&m->file_active_list))
732         {
733           if (fi->events & (POLLIN | POLLHUP | POLLERR))
734             {
735               fi->events &= ~(POLLIN | POLLHUP | POLLERR);
736               do
737                 DBG("MAIN: Read event on fd %d", fi->fd);
738               while (fi->read_handler && fi->read_handler(fi));
739               continue;
740             }
741           if (fi->events & (POLLOUT | POLLERR))
742             {
743               fi->events &= ~(POLLOUT | POLLERR);
744               do
745                 DBG("MAIN: Write event on fd %d", fi->fd);
746               while (fi->write_handler && fi->write_handler(fi));
747               continue;
748             }
749           clist_remove(&fi->n);
750           clist_add_tail(&m->file_list, &fi->n);
751         }
752     }
753 }