X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=ucw%2Fmainloop.c;h=b2b07c5082476bedbdc023e094f49f6feb02a28b;hb=6efdc514c193f18c9ef840096750c37e78a01bf6;hp=57fd4ff967d6bd526ae6713355c6c46c0d20476f;hpb=01952d21dc7195336fe03d6b12a73d563924cfe9;p=libucw.git diff --git a/ucw/mainloop.c b/ucw/mainloop.c index 57fd4ff9..b2b07c50 100644 --- a/ucw/mainloop.c +++ b/ucw/mainloop.c @@ -1,7 +1,7 @@ /* * UCW Library -- Main Loop * - * (c) 2004--2011 Martin Mares + * (c) 2004--2012 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. @@ -9,11 +9,13 @@ #undef LOCAL_DEBUG -#include "ucw/lib.h" -#include "ucw/heap.h" -#include "ucw/mainloop.h" -#include "ucw/threads.h" -#include "ucw/gary.h" +#include +#include +#include +#include +#include +#include +#include #include #include @@ -43,13 +45,42 @@ #define EPOLL_BUF_SIZE 256 +static void file_del_ctx(struct main_context *m, struct main_file *fi); +static void signal_del_ctx(struct main_context *m, struct main_signal *ms); + static void -do_main_get_time(struct main_context *m) +main_get_time_ctx(struct main_context *m) +{ + m->now = get_timestamp(); +} + +static struct main_context * +main_current_nocheck(void) +{ + return ucwlib_thread_context()->main_context; +} + +struct main_context * +main_current(void) +{ + struct main_context *m = main_current_nocheck(); + ASSERT(m); + return m; +} + +static int +main_is_current(struct main_context *m) +{ + return (m == main_current_nocheck()); +} + +static inline uns +count_timers(struct main_context *m) { - struct timeval tv; - gettimeofday(&tv, NULL); - m->now_seconds = tv.tv_sec; - m->now = (timestamp_t)tv.tv_sec * 1000 + tv.tv_usec / 1000; + if (m->timer_table) + return GARY_SIZE(m->timer_table) - 1; + else + return 0; } struct main_context * @@ -68,31 +99,46 @@ main_new(void) m->epoll_fd = epoll_create(64); if (m->epoll_fd < 0) die("epoll_create() failed: %m"); - m->epoll_events = xmalloc(EPOLL_BUF_SIZE * sizeof(struct epoll_event *)); + m->epoll_events = xmalloc(EPOLL_BUF_SIZE * sizeof(struct epoll_event)); clist_init(&m->file_recalc_list); #else m->poll_table_obsolete = 1; #endif - do_main_get_time(m); + main_get_time_ctx(m); sigemptyset(&m->want_signals); m->sig_pipe_recv = m->sig_pipe_send = -1; return m; } -void -main_delete(struct main_context *m) +static void +main_prepare_delete(struct main_context *m) { - struct main_context *prev = main_switch_context(m); + /* + * If the context is current, deactivate it first. But beware, + * we must not call functions that depend on the current context. + */ + if (main_is_current(m)) + main_switch_context(NULL); + + // Close epoll descriptor early enough, it might be shared after fork! +#ifdef CONFIG_UCW_EPOLL + xfree(m->epoll_events); + close(m->epoll_fd); + m->epoll_fd = -1; +#else + GARY_FREE(m->poll_table); + GARY_FREE(m->poll_file_table); +#endif if (m->sigchld_handler) { - signal_del(m->sigchld_handler); + signal_del_ctx(m, m->sigchld_handler); xfree(m->sigchld_handler); } if (m->sig_pipe_file) { - file_del(m->sig_pipe_file); + file_del_ctx(m, m->sig_pipe_file); xfree(m->sig_pipe_file); } if (m->sig_pipe_recv >= 0) @@ -100,25 +146,51 @@ main_delete(struct main_context *m) close(m->sig_pipe_recv); close(m->sig_pipe_send); } +} + +static void +main_do_delete(struct main_context *m) +{ + GARY_FREE(m->timer_table); + xfree(m); +} + +void +main_delete(struct main_context *m) +{ + if (!m) + return; + + main_prepare_delete(m); ASSERT(clist_empty(&m->file_list)); ASSERT(clist_empty(&m->file_active_list)); +#ifdef CONFIG_UCW_EPOLL + ASSERT(clist_empty(&m->file_recalc_list)); +#endif ASSERT(clist_empty(&m->hook_list)); ASSERT(clist_empty(&m->hook_done_list)); ASSERT(clist_empty(&m->process_list)); ASSERT(clist_empty(&m->signal_list)); - GARY_FREE(m->timer_table); + ASSERT(!count_timers(m)); + main_do_delete(m); +} + +void +main_destroy(struct main_context *m) +{ + if (!m) + return; + main_prepare_delete(m); + + // Close all files + clist_insert_list_after(&m->file_active_list, m->file_list.head.prev); #ifdef CONFIG_UCW_EPOLL - ASSERT(clist_empty(&m->file_recalc_list)); - xfree(m->epoll_events); - close(m->epoll_fd); -#else - GARY_FREE(m->poll_table); - GARY_FREE(m->poll_file_table); + clist_insert_list_after(&m->file_recalc_list, m->file_list.head.prev); #endif - xfree(m); - // FIXME: Some mechanism for cleaning up after fork() + CLIST_FOR_EACH(struct main_file *, f, m->file_list) + close(f->fd); - main_switch_context((prev == m) ? NULL : prev); + main_do_delete(m); } struct main_context * @@ -141,15 +213,6 @@ main_switch_context(struct main_context *m) return m0; } -struct main_context * -main_current(void) -{ - struct ucwlib_context *c = ucwlib_thread_context(); - struct main_context *m = c->main_context; - ASSERT(m); - return m; -} - void main_init(void) { @@ -160,22 +223,19 @@ main_init(void) void main_cleanup(void) { - main_delete(main_current()); + main_delete(main_current_nocheck()); } void -main_get_time(void) +main_teardown(void) { - do_main_get_time(main_current()); + main_destroy(main_current_nocheck()); } -static inline uns -count_timers(struct main_context *m) +void +main_get_time(void) { - if (m->timer_table) - return GARY_SIZE(m->timer_table) - 1; - else - return 0; + main_get_time_ctx(main_current()); } void @@ -245,9 +305,9 @@ file_want_events(struct main_file *fi) { uns events = 0; if (fi->read_handler) - events |= POLLIN | POLLHUP | POLLERR; + events |= POLLIN; if (fi->write_handler) - events |= POLLOUT | POLLERR; + events |= POLLOUT; return events; } @@ -257,7 +317,7 @@ file_add(struct main_file *fi) struct main_context *m = main_current(); DBG("MAIN: Adding file %p (fd=%d)", fi, fi->fd); - ASSERT(!clist_is_linked(&fi->n)); + ASSERT(!file_is_active(fi)); clist_add_tail(&m->file_list, &fi->n); m->file_cnt++; #ifdef CONFIG_UCW_EPOLL @@ -288,17 +348,18 @@ file_chg(struct main_file *fi) #endif } -void -file_del(struct main_file *fi) +static void +file_del_ctx(struct main_context *m, struct main_file *fi) { - struct main_context *m = main_current(); - + // XXX: Can be called on a non-current context DBG("MAIN: Deleting file %p (fd=%d)", fi, fi->fd); - ASSERT(clist_is_linked(&fi->n)); + + if (!file_is_active(fi)) + return; clist_unlink(&fi->n); m->file_cnt--; #ifdef CONFIG_UCW_EPOLL - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, fi->fd, NULL) < 0) + if (m->epoll_fd >= 0 && epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, fi->fd, NULL) < 0) die("epoll_ctl() failed: %m"); #else m->poll_table_obsolete = 1; @@ -306,12 +367,9 @@ file_del(struct main_file *fi) } void -file_close_all(void) +file_del(struct main_file *fi) { - struct main_context *m = main_current(); - - CLIST_FOR_EACH(struct main_file *, f, m->file_list) - close(f->fd); + file_del_ctx(main_current(), fi); } void @@ -320,7 +378,8 @@ hook_add(struct main_hook *ho) struct main_context *m = main_current(); DBG("MAIN: Adding hook %p", ho); - ASSERT(!clist_is_linked(&ho->n)); + if (hook_is_active(ho)) + clist_unlink(&ho->n); clist_add_tail(&m->hook_list, &ho->n); } @@ -328,8 +387,8 @@ void hook_del(struct main_hook *ho) { DBG("MAIN: Deleting hook %p", ho); - ASSERT(clist_is_linked(&ho->n)); - clist_unlink(&ho->n); + if (hook_is_active(ho)) + clist_unlink(&ho->n); } static void @@ -361,7 +420,7 @@ process_add(struct main_process *mp) struct main_context *m = main_current(); DBG("MAIN: Adding process %p (pid=%d)", mp, mp->pid); - ASSERT(!clist_is_linked(&mp->n)); + ASSERT(!process_is_active(mp)); ASSERT(mp->handler); clist_add_tail(&m->process_list, &mp->n); if (!m->sigchld_handler) @@ -378,8 +437,8 @@ void process_del(struct main_process *mp) { DBG("MAIN: Deleting process %p (pid=%d)", mp, mp->pid); - ASSERT(clist_is_linked(&mp->n)); - clist_unlink(&mp->n); + if (process_is_active(mp)) + clist_unlink(&mp->n); } int @@ -485,7 +544,7 @@ signal_add(struct main_signal *ms) DBG("MAIN: Adding signal %p (sig=%d)", ms, ms->signum); - ASSERT(!clist_is_linked(&ms->n)); + ASSERT(!signal_is_active(ms)); // Adding at the head of the list is better if we are in the middle of walking the list. clist_add_head(&m->signal_list, &ms->n); if (m->sig_pipe_recv < 0) @@ -504,14 +563,14 @@ signal_add(struct main_signal *ms) sigaddset(&m->want_signals, ms->signum); } -void -signal_del(struct main_signal *ms) +static void +signal_del_ctx(struct main_context *m, struct main_signal *ms) { - struct main_context *m = main_current(); - + // XXX: Can be called on a non-current context DBG("MAIN: Deleting signal %p (sig=%d)", ms, ms->signum); - ASSERT(clist_is_linked(&ms->n)); + if (!signal_is_active(ms)) + return; clist_unlink(&ms->n); int another = 0; @@ -520,56 +579,107 @@ signal_del(struct main_signal *ms) another++; if (!another) { - sigset_t ss; - sigemptyset(&ss); - sigaddset(&ss, ms->signum); - THREAD_SIGMASK(SIG_BLOCK, &ss, NULL); + if (main_is_current(m)) + { + sigset_t ss; + sigemptyset(&ss); + sigaddset(&ss, ms->signum); + THREAD_SIGMASK(SIG_BLOCK, &ss, NULL); + } sigdelset(&m->want_signals, ms->signum); } } +void +signal_del(struct main_signal *ms) +{ + signal_del_ctx(main_current(), ms); +} + +#ifdef CONFIG_UCW_DEBUG + +void +file_debug(struct main_file *fi) +{ + msg(L_DEBUG, "\t\t%p (fd %d, rh %p, wh %p, data %p)", + fi, fi->fd, fi->read_handler, fi->write_handler, fi->data); +} + +void +hook_debug(struct main_hook *ho) +{ + msg(L_DEBUG, "\t\t%p (func %p, data %p)", ho, ho->handler, ho->data); +} + +void +signal_debug(struct main_signal *sg) +{ + if (sg->signum < 0) + msg(L_DEBUG, "\t\t(placeholder)"); + else + msg(L_DEBUG, "\t\t%p (sig %d, func %p, data %p)", sg, sg->signum, sg->handler, sg->data); +} + +static void +timer_debug_ctx(struct main_context *m, struct main_timer *tm) +{ + msg(L_DEBUG, "\t\t%p (expires %lld, data %p)", tm, (long long)(tm->expires - m->now), tm->data); +} + +void +timer_debug(struct main_timer *tm) +{ + timer_debug_ctx(main_current(), tm); +} + +void +process_debug(struct main_process *pr) +{ + msg(L_DEBUG, "\t\t%p (pid %d, func %p, data %p)", pr, pr->pid, pr->handler, pr->data); +} + void main_debug_context(struct main_context *m UNUSED) { -#ifdef CONFIG_DEBUG msg(L_DEBUG, "### Main loop status on %lld", (long long) m->now); msg(L_DEBUG, "\tActive timers:"); uns num_timers = count_timers(m); for (uns i = 1; i <= num_timers; i++) - { - struct main_timer *tm = m->timer_table[i]; - msg(L_DEBUG, "\t\t%p (expires %lld, data %p)", tm, (long long)(tm->expires ? tm->expires - m->now : 999999), tm->data); - } + timer_debug(m->timer_table[i]); msg(L_DEBUG, "\tActive files:"); CLIST_FOR_EACH(struct main_file *, fi, m->file_list) - msg(L_DEBUG, "\t\t%p (fd %d, rh %p, wh %p, data %p)", - fi, fi->fd, fi->read_handler, fi->write_handler, fi->data); + file_debug(fi); CLIST_FOR_EACH(struct main_file *, fi, m->file_active_list) - msg(L_DEBUG, "\t\t%p (fd %d, rh %p, wh %p, data %p) [pending events: %x]", - fi, fi->fd, fi->read_handler, fi->write_handler, fi->data, fi->events); - // FIXME: Can we display status of block_io requests somehow? + file_debug(fi); #ifdef CONFIG_UCW_EPOLL CLIST_FOR_EACH(struct main_file *, fi, m->file_recalc_list) - msg(L_DEBUG, "\t\t%p (fd %d, rh %p, wh %p, data %p) [pending recalculation]", - fi, fi->fd, fi->read_handler, fi->write_handler, fi->data); + file_debug(fi); #endif msg(L_DEBUG, "\tActive hooks:"); CLIST_FOR_EACH(struct main_hook *, ho, m->hook_done_list) - msg(L_DEBUG, "\t\t%p (func %p, data %p)", ho, ho->handler, ho->data); + hook_debug(ho); CLIST_FOR_EACH(struct main_hook *, ho, m->hook_list) - msg(L_DEBUG, "\t\t%p (func %p, data %p)", ho, ho->handler, ho->data); + hook_debug(ho); msg(L_DEBUG, "\tActive processes:"); CLIST_FOR_EACH(struct main_process *, pr, m->process_list) - msg(L_DEBUG, "\t\t%p (pid %d, func %p, data %p)", pr, pr->pid, pr->handler, pr->data); + process_debug(pr); msg(L_DEBUG, "\tActive signal catchers:"); CLIST_FOR_EACH(struct main_signal *, sg, m->signal_list) - if (sg->signum < 0) - msg(L_DEBUG, "\t\t(placeholder)"); - else - msg(L_DEBUG, "\t\t%p (sig %d, func %p, data %p)", sg, sg->signum, sg->handler, sg->data); -#endif + signal_debug(sg); } +#else + +// Stubs +void file_debug(struct main_file *fi UNUSED) { } +void hook_debug(struct main_hook *ho UNUSED) { } +void signal_debug(struct main_signal *sg UNUSED) { } +void timer_debug(struct main_timer *tm UNUSED) { } +void process_debug(struct main_process *pr UNUSED) { } +void main_debug_context(struct main_context *m UNUSED) { } + +#endif + static void process_timers(struct main_context *m) { @@ -601,7 +711,7 @@ process_hooks(struct main_context *m) hook_min == HOOK_DONE && hook_max == HOOK_DONE || m->shutdown) { - DBG("MAIN: Shut down by %s", m->shutdown ? "main_shutdown" : "a hook"); + DBG("MAIN: Shut down by %s", m->shutdown ? "main_shut_down" : "a hook"); return HOOK_SHUTDOWN; } if (hook_max == HOOK_RETRY) @@ -663,7 +773,9 @@ main_loop(void) DBG("MAIN: Entering main_loop"); struct main_context *m = main_current(); - do_main_get_time(m); + main_get_time_ctx(m); + m->shutdown = 0; + for (;;) { timestamp_t wake = m->now + 1000000000; @@ -677,10 +789,15 @@ main_loop(void) break; default: ; } - if (count_timers(m)) - wake = MIN(wake, m->timer_table[1]->expires); - do_main_get_time(m); - int timeout = ((wake > m->now) ? wake - m->now : 0); + + 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); @@ -697,11 +814,16 @@ main_loop(void) if (n < 0 && errno != EAGAIN && errno != EINTR) die("(e)poll failed: %m"); timestamp_t old_now = m->now; - do_main_get_time(m); + main_get_time_ctx(m); m->idle_time += m->now - old_now; if (n <= 0) - continue; + { + if (m->single_step) + return; + else + continue; + } // Relink all files with a pending event to file_active_list #ifdef CONFIG_UCW_EPOLL @@ -736,17 +858,17 @@ main_loop(void) struct main_file *fi; while (fi = clist_head(&m->file_active_list)) { - if (fi->events & (POLLIN | POLLHUP | POLLERR)) + if (fi->read_handler && (fi->events & (POLLIN | POLLHUP))) { - fi->events &= ~(POLLIN | POLLHUP | POLLERR); + 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->events & (POLLOUT | POLLERR)) + if (fi->write_handler && (fi->events & (POLLOUT | POLLHUP | POLLERR))) { - fi->events &= ~(POLLOUT | POLLERR); + fi->events &= ~(POLLOUT | POLLHUP | POLLERR); do DBG("MAIN: Write event on fd %d", fi->fd); while (fi->write_handler && fi->write_handler(fi)); @@ -757,3 +879,12 @@ main_loop(void) } } } + +void +main_step(void) +{ + struct main_context *m = main_current(); + m->single_step = 1; + main_loop(); + m->single_step = 0; +}