+
+void
+main_loop(void)
+{
+ DBG("MAIN: Entering main_loop");
+ struct main_context *m = main_current();
+
+ main_get_time_ctx(m);
+ m->shutdown = 0;
+
+ for (;;)
+ {
+ timestamp_t wake = m->now + 1000000000;
+ process_timers(m);
+ switch (process_hooks(m))
+ {
+ case HOOK_SHUTDOWN:
+ return;
+ case HOOK_RETRY:
+ wake = 0;
+ break;
+ default: ;
+ }
+
+ int timeout = 0;
+ if (!m->single_step)
+ {
+ if (count_timers(m))
+ wake = MIN(wake, m->timer_table[1]->expires);
+ main_get_time_ctx(m);
+ timeout = ((wake > m->now) ? wake - m->now : 0);
+ }
+
+#ifdef CONFIG_UCW_EPOLL
+ recalc_files(m);
+ DBG("MAIN: Epoll for %d fds and timeout %d ms", m->file_cnt, timeout);
+ int n = epoll_wait(m->epoll_fd, m->epoll_events, EPOLL_BUF_SIZE, timeout);
+#else
+ if (m->poll_table_obsolete)
+ rebuild_poll_table(m);
+ DBG("MAIN: Poll for %d fds and timeout %d ms", m->file_cnt, timeout);
+ int n = poll(m->poll_table, m->file_cnt, timeout);
+#endif
+
+ DBG("\t-> %d events", n);
+ if (n < 0 && errno != EAGAIN && errno != EINTR)
+ die("(e)poll failed: %m");
+ timestamp_t old_now = m->now;
+ main_get_time_ctx(m);
+ m->idle_time += m->now - old_now;
+
+ if (n <= 0)
+ {
+ if (m->single_step)
+ return;
+ else
+ continue;
+ }
+
+ // Relink all files with a pending event to file_active_list
+#ifdef CONFIG_UCW_EPOLL
+ for (int i=0; i<n; i++)
+ {
+ struct epoll_event *e = &m->epoll_events[i];
+ struct main_file *fi = e->data.ptr;
+ clist_remove(&fi->n);
+ clist_add_tail(&m->file_active_list, &fi->n);
+ fi->events = e->events;
+ }
+#else
+ struct pollfd *p = m->poll_table;
+ struct main_file **pf = m->poll_file_table;
+ for (uns i=0; i < m->file_cnt; i++)
+ if (p[i].revents)
+ {
+ struct main_file *fi = pf[i];
+ clist_remove(&fi->n);
+ clist_add_tail(&m->file_active_list, &fi->n);
+ fi->events = p[i].revents;
+ }
+#endif
+
+ /*
+ * Process the buffered file events. This is pretty tricky, since
+ * user callbacks can modify the file structure or even destroy it.
+ * In such cases, we detect that the structure was relinked and stop
+ * processing its events, leaving them for the next iteration of the
+ * main loop.
+ */
+ struct main_file *fi;
+ while (fi = clist_head(&m->file_active_list))
+ {
+ if (fi->read_handler && (fi->events & (POLLIN | POLLHUP)))
+ {
+ fi->events &= ~(POLLIN | POLLHUP);
+ do
+ DBG("MAIN: Read event on fd %d", fi->fd);
+ while (fi->read_handler && fi->read_handler(fi));
+ continue;
+ }
+ if (fi->write_handler && (fi->events & (POLLOUT | POLLHUP | POLLERR)))
+ {
+ fi->events &= ~(POLLOUT | POLLHUP | POLLERR);
+ do
+ DBG("MAIN: Write event on fd %d", fi->fd);
+ while (fi->write_handler && fi->write_handler(fi));
+ continue;
+ }
+ clist_remove(&fi->n);
+ clist_add_tail(&m->file_list, &fi->n);
+ }
+ }
+}
+
+void
+main_step(void)
+{
+ struct main_context *m = main_current();
+ m->single_step = 1;
+ main_loop();
+ m->single_step = 0;
+}