#ifdef HAVE_CONFIG_H # include #endif #define EINA_SLSTR_INTERNAL // as per: // // https://forums.freebsd.org/threads/solved-enlightenment-application-menu-icons-missing-due-to-efreetd-crashing.80743/ // // from ethorns - can't find more user info... // // normal fd set size (maximum fd num allowed) is limited to 1024. raise this // to something more useful than the default 1024 ... this doesn't affect // linux as it will use epoll. beware that this also will be a performance // hit - the higher this number the more it costs to zero and re-fill the fd // set every time we go into select. #define FD_SETSIZE EFL_MAX_FD_SIZE #ifdef _WIN32 # ifndef USER_TIMER_MINIMUM # define USER_TIMER_MINIMUM 0x0a # endif #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_IEEEFP_H # include // for Solaris #endif #ifdef HAVE_ISFINITE # define ECORE_FINITE(t) isfinite(t) #else # define ECORE_FINITE(t) finite(t) #endif //#define FIX_HZ 1 #ifdef FIX_HZ # include # ifndef HZ # define HZ 100 # endif #endif #ifdef _WIN32 # include /* evil_last_error_get */ #endif #include "Ecore.h" #include "ecore_private.h" #include "ecore_main_common.h" #include "eina_internal.h" #ifdef USE_G_MAIN_LOOP # include #endif #ifdef HAVE_LIBUV # ifdef HAVE_NODE_UV_H # include # elif defined(HAVE_NODEJS_DEPS_UV_UV_H) # include # elif defined(HAVE_NODEJS_DEPS_UV_INCLUDE_UV_H) # include # elif defined(HAVE_NODEJS_SRC_UV_H) # include # elif defined(HAVE_UV_H) # include # else # error No uv.h header found? # endif // XXX: FIXME: use eina_module # if defined(HAVE_DLOPEN) && !defined(_WIN32) # include # endif static uv_prepare_t _ecore_main_uv_prepare; static uv_check_t _ecore_main_uv_check; static uv_timer_t _ecore_main_uv_handle_timers; static Eina_Bool _ecore_main_uv_idling; static int (*_dl_uv_loop_alive)(uv_loop_t*) = 0; static int (*_dl_uv_run)(uv_loop_t*, uv_run_mode mode) = 0; static int (*_dl_uv_stop)(uv_loop_t*) = 0; static uv_loop_t* (*_dl_uv_default_loop)() = 0; static int (*_dl_uv_poll_init_socket)(uv_loop_t* loop, uv_poll_t* handle, uv_os_sock_t fd) = 0; static int (*_dl_uv_poll_init)(uv_loop_t* loop, uv_poll_t* handle, int fd) = 0; static int (*_dl_uv_poll_start)(uv_poll_t* handle, int events, uv_poll_cb cb) = 0; static int (*_dl_uv_poll_stop)(uv_poll_t* handle) = 0; static int (*_dl_uv_timer_init)(uv_loop_t*, uv_timer_t* handle); static int (*_dl_uv_timer_start)(uv_timer_t* handle, uv_timer_cb cb, uint64_t timeout, uint64_t repeat); static int (*_dl_uv_timer_stop)(uv_timer_t* handle); static int (*_dl_uv_prepare_init)(uv_loop_t*, uv_prepare_t* prepare); static int (*_dl_uv_prepare_start)(uv_prepare_t* prepare, uv_prepare_cb cb); static int (*_dl_uv_prepare_stop)(uv_prepare_t* prepare); static int (*_dl_uv_check_init)(uv_loop_t*, uv_check_t* prepare); static int (*_dl_uv_check_start)(uv_check_t* prepare, uv_check_cb cb); static int (*_dl_uv_check_stop)(uv_check_t* prepare); static int (*_dl_uv_close)(uv_handle_t* handle, uv_close_cb close_cb); #endif #define NS_PER_SEC (1000000000.0) struct _Ecore_Fd_Handler { EINA_INLIST; ECORE_MAGIC; Ecore_Fd_Handler *next_ready; int fd; Ecore_Fd_Handler_Flags flags; Eo *handler; Eo *loop; Efl_Loop_Data *loop_data; Ecore_Fd_Cb func; void *data; Ecore_Fd_Cb buf_func; void *buf_data; Ecore_Fd_Prep_Cb prep_func; void *prep_data; int references; #if defined(USE_G_MAIN_LOOP) GPollFD gfd; #endif #ifdef HAVE_LIBUV uv_poll_t uv_handle; #endif Eina_Bool read_active : 1; Eina_Bool write_active : 1; Eina_Bool error_active : 1; Eina_Bool delete_me : 1; Eina_Bool file : 1; Eina_Bool legacy : 1; }; GENERIC_ALLOC_SIZE_DECLARE(Ecore_Fd_Handler); #ifdef _WIN32 struct _Ecore_Win32_Handler { EINA_INLIST; ECORE_MAGIC; HANDLE h; Eo *handler; Eo *loop; Efl_Loop_Data *loop_data; Ecore_Win32_Handle_Cb func; void *data; int references; Eina_Bool delete_me : 1; }; GENERIC_ALLOC_SIZE_DECLARE(Ecore_Win32_Handler); #endif #if !defined(USE_G_MAIN_LOOP) && !defined(HAVE_LIBUV) static int _ecore_main_select(Eo *obj, Efl_Loop_Data *pd, double timeout); #endif static void _ecore_main_prepare_handlers(Eo *obj, Efl_Loop_Data *pd); static void _ecore_main_fd_handlers_cleanup(Eo *obj, Efl_Loop_Data *pd); #ifndef _WIN32 # ifndef USE_G_MAIN_LOOP static void _ecore_main_fd_handlers_bads_rem(Eo *obj, Efl_Loop_Data *pd); # endif #endif static void _ecore_main_fd_handlers_call(Eo *obj, Efl_Loop_Data *pd); static int _ecore_main_fd_handlers_buf_call(Eo *obj, Efl_Loop_Data *pd); #ifndef USE_G_MAIN_LOOP static void _ecore_main_loop_iterate_internal(Eo *obj, Efl_Loop_Data *pd, int once_only); #endif #ifdef _WIN32 static int _ecore_main_win32_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); static void _ecore_main_win32_handlers_cleanup(Eo *obj, Efl_Loop_Data *pd); #endif static void _ecore_main_loop_setup(Eo *obj, Efl_Loop_Data *pd); static void _ecore_main_loop_clear(Eo *obj, Efl_Loop_Data *pd); // for legacy mainloop only and not other loops int in_main_loop = 0; #ifndef USE_G_MAIN_LOOP static double t1 = 0.0; static double t2 = 0.0; #endif #ifdef _WIN32 Ecore_Select_Function main_loop_select = _ecore_main_win32_select; static Ecore_Select_Function general_loop_select = _ecore_main_win32_select; #else # include Ecore_Select_Function main_loop_select = select; static Ecore_Select_Function general_loop_select = select; #endif #ifdef USE_G_MAIN_LOOP static GPollFD ecore_epoll_fd; static GPollFD ecore_timer_fd; static GSource *ecore_glib_source; static guint ecore_glib_source_id; static GMainLoop *ecore_main_loop; static gboolean ecore_idling; static gboolean _ecore_glib_idle_enterer_called; static gboolean ecore_fds_ready; #endif static double _ecore_main_loop_wakeup_time = 0.0; EAPI double _ecore_main_loop_wakeup_time_get(void) { return _ecore_main_loop_wakeup_time; } static inline void _update_loop_time(Efl_Loop_Data *pd) { double loop_time = ecore_time_get(); if (loop_time > pd->loop_time) pd->loop_time = loop_time; _ecore_main_loop_wakeup_time = loop_time; } #ifdef EFL_EXTRA_SANITY_CHECKS static inline void _ecore_fd_valid(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED) { # ifdef HAVE_SYS_EPOLL_H if ((pd->epoll_fd >= 0) && (fcntl(pd->epoll_fd, F_GETFD) < 0)) { ERR("arghhh you caught me! report a backtrace to edevel!"); # ifdef HAVE_PAUSE pause(); # else sleep(60); # endif } # endif } #endif static inline void _ecore_try_add_to_call_list(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd, Ecore_Fd_Handler *fdh) { // check if this fdh is already in the list if (fdh->next_ready) { DBG("next_ready"); return; } if (fdh->read_active || fdh->write_active || fdh->error_active || (fdh->flags & ECORE_FD_ALWAYS)) { DBG("added"); // make sure next_ready is non-null by pointing to ourselves // use that to indicate this fdh is in the ready list // insert at the head of the list to avoid trouble fdh->next_ready = pd->fd_handlers_to_call ? pd->fd_handlers_to_call : fdh; pd->fd_handlers_to_call = fdh; } } static inline void _throttle_do(Efl_Loop_Data *pd) { if (pd->throttle == 0) return; eina_evlog("+throttle", NULL, 0.0, NULL); usleep(pd->throttle); eina_evlog("-throttle", NULL, 0.0, NULL); } #ifdef HAVE_SYS_EPOLL_H static inline int _ecore_get_epoll_fd(Eo *obj, Efl_Loop_Data *pd) { if (pd->epoll_pid && (pd->epoll_pid != getpid())) // forked! _ecore_main_loop_clear(obj, pd); if ((pd->epoll_pid == 0) && (pd->epoll_fd < 0)) _ecore_main_loop_setup(obj, pd); return pd->epoll_fd; } static inline int _ecore_epoll_add(int efd, int fd, int events, void *ptr) { struct epoll_event ev; memset(&ev, 0, sizeof (ev)); ev.events = events; ev.data.ptr = ptr; DBG("adding poll on %d %08x", fd, events); return epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev); } static inline int _ecore_poll_events_from_fdh(Ecore_Fd_Handler *fdh) { int events = 0; if (fdh->flags & ECORE_FD_READ) events |= EPOLLIN | EPOLLHUP; if (fdh->flags & ECORE_FD_WRITE) events |= EPOLLOUT | EPOLLHUP; if (fdh->flags & ECORE_FD_ERROR) events |= EPOLLERR | EPOLLPRI | EPOLLHUP; return events; } #endif #ifdef USE_G_MAIN_LOOP static inline int _gfd_events_from_fdh(Ecore_Fd_Handler *fdh) { int events = 0; if (fdh->flags & ECORE_FD_READ) events |= G_IO_IN | G_IO_HUP; if (fdh->flags & ECORE_FD_WRITE) events |= G_IO_OUT | G_IO_HUP; if (fdh->flags & ECORE_FD_ERROR) events |= G_IO_ERR | G_IO_HUP; return events; } #endif static void _ecore_main_pre_idle_exit(void) { // even if we never go idle, keep up flushing some of our freeq away // on every idle exit which may happen if we even never called idlers // for now an idea but dont enforce eina_freeq_reduce(eina_freeq_main_get(), 128); } #ifdef HAVE_LIBUV static void _ecore_main_uv_poll_cb(uv_poll_t *handle, int status, int events) { Eo *obj = ML_OBJ; Efl_Loop_Data *pd = ML_DAT; DBG("_ecore_main_uv_poll_cb %p status %d events %d", (void *)handle->data, status, events); Ecore_Fd_Handler *fdh = handle->data; if (_ecore_main_uv_idling) { DBG("not IDLE anymore"); _ecore_main_uv_idling = EINA_FALSE; eina_file_statgen_next(); _ecore_main_pre_idle_exit(); efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_EXIT, NULL); _ecore_animator_run_reset(); } if (status) fdh->error_active = EINA_TRUE; if (events & UV_READABLE) fdh->read_active = EINA_TRUE; if (events & UV_WRITABLE) fdh->write_active = EINA_TRUE; if (events & UV_DISCONNECT) { fdh->read_active = EINA_TRUE; fdh->write_active = EINA_TRUE; fdh->error_active = EINA_TRUE; } _ecore_try_add_to_call_list(obj, pd, fdh); _ecore_main_fd_handlers_call(obj, pd); if (pd->fd_handlers_with_buffer) _ecore_main_fd_handlers_buf_call(obj, pd); _ecore_signal_received_process(obj, pd); efl_loop_message_process(obj); _ecore_main_fd_handlers_cleanup(obj, pd); _efl_loop_timer_expired_timers_call(obj, pd, pd->loop_time); } static int _ecore_main_uv_events_from_fdh(Ecore_Fd_Handler *fdh) { int events = 0; if (fdh->flags & ECORE_FD_READ) events |= UV_READABLE; if (fdh->flags & ECORE_FD_WRITE) events |= UV_WRITABLE; DBG("events is %d", (int)events); return events; } #endif static inline int _ecore_main_fdh_poll_add(Efl_Loop_Data *pd EINA_UNUSED, Ecore_Fd_Handler *fdh) { DBG("_ecore_main_fdh_poll_add"); int r = 0; #ifdef HAVE_SYS_EPOLL_H # ifdef HAVE_LIBUV if (!_dl_uv_run) # endif { if ((!fdh->file) && (pd->epoll_fd >= 0)) r = _ecore_epoll_add(_ecore_get_epoll_fd(fdh->loop, pd), fdh->fd, _ecore_poll_events_from_fdh(fdh), fdh); } # ifdef HAVE_LIBUV else # endif #endif { #ifdef HAVE_LIBUV if (!fdh->file) { DBG("_ecore_main_fdh_poll_add libuv socket %p", fdh); fdh->uv_handle.data = fdh; DBG("_ecore_main_fdh_poll_add2 %p", fdh); _dl_uv_poll_init_socket(_dl_uv_default_loop(), &fdh->uv_handle, fdh->fd); DBG("_ecore_main_fdh_poll_add3 %p", fdh->uv_handle.data); _dl_uv_poll_start(&fdh->uv_handle, _ecore_main_uv_events_from_fdh(fdh) , _ecore_main_uv_poll_cb); DBG("_ecore_main_fdh_poll_add libuv DONE"); } else { DBG("_ecore_main_fdh_poll_add libuv file"); fdh->uv_handle.data = fdh; DBG("_ecore_main_fdh_poll_add2 %p", fdh); _dl_uv_poll_init(_dl_uv_default_loop(), &fdh->uv_handle, fdh->fd); DBG("_ecore_main_fdh_poll_add3 %p", fdh->uv_handle.data); _dl_uv_poll_start(&fdh->uv_handle, _ecore_main_uv_events_from_fdh(fdh) , _ecore_main_uv_poll_cb); DBG("_ecore_main_fdh_poll_add libuv DONE"); } #elif defined(USE_G_MAIN_LOOP) fdh->gfd.fd = fdh->fd; fdh->gfd.events = _gfd_events_from_fdh(fdh); fdh->gfd.revents = 0; DBG("adding gpoll on %d %08x", fdh->fd, fdh->gfd.events); g_source_add_poll(ecore_glib_source, &fdh->gfd); #endif } return r; } static inline void _ecore_main_fdh_poll_del(Efl_Loop_Data *pd, Ecore_Fd_Handler *fdh) { #ifdef HAVE_SYS_EPOLL_H # ifdef HAVE_LIBUV if (!_dl_uv_run) # endif { if (!pd) { WRN("Efl_Loop_Data is NULL!"); return; } if ((!fdh->file) && (pd->epoll_fd >= 0)) { struct epoll_event ev; int efd = _ecore_get_epoll_fd(fdh->loop, pd); memset(&ev, 0, sizeof (ev)); DBG("removing poll on %d", fdh->fd); // could get an EBADF if somebody closed the FD before removing if ((epoll_ctl(efd, EPOLL_CTL_DEL, fdh->fd, &ev) < 0)) { if (errno == EBADF) { WRN("fd %d closed, can't remove from epoll - reinit!", fdh->fd); _ecore_main_loop_clear(fdh->loop, pd); _ecore_main_loop_setup(fdh->loop, pd); } else ERR("Failed to delete epoll fd %d! (errno=%d)", fdh->fd, errno); } } } # ifdef HAVE_LIBUV else # endif #endif { #ifdef HAVE_LIBUV DBG("_ecore_main_fdh_poll_del libuv %p", fdh); uv_handle_t* h = (uv_handle_t*)&fdh->uv_handle; _dl_uv_close(h, 0); DBG("_ecore_main_fdh_poll_del libuv DONE"); #elif USE_G_MAIN_LOOP fdh->gfd.fd = fdh->fd; fdh->gfd.events = _gfd_events_from_fdh(fdh); fdh->gfd.revents = 0; DBG("removing gpoll on %d %08x", fdh->fd, fdh->gfd.events); g_source_remove_poll(ecore_glib_source, &fdh->gfd); #endif } } static inline int _ecore_main_fdh_poll_modify(Efl_Loop_Data *pd EINA_UNUSED, Ecore_Fd_Handler *fdh) { DBG("_ecore_main_fdh_poll_modify %p", fdh); int r = 0; #ifdef HAVE_SYS_EPOLL_H # ifdef HAVE_LIBUV if (!_dl_uv_run) # endif { if ((!fdh->file) && (pd->epoll_fd >= 0)) { struct epoll_event ev; int efd = _ecore_get_epoll_fd(fdh->loop, pd); memset(&ev, 0, sizeof (ev)); ev.events = _ecore_poll_events_from_fdh(fdh); ev.data.ptr = fdh; DBG("modifing epoll on %d to %08x", fdh->fd, ev.events); r = epoll_ctl(efd, EPOLL_CTL_MOD, fdh->fd, &ev); } } # ifdef HAVE_LIBUV else # endif #endif { #ifdef HAVE_LIBUV _dl_uv_poll_start(&fdh->uv_handle, _ecore_main_uv_events_from_fdh(fdh) , _ecore_main_uv_poll_cb); #elif defined(USE_G_MAIN_LOOP) fdh->gfd.fd = fdh->fd; fdh->gfd.events = _gfd_events_from_fdh(fdh); fdh->gfd.revents = 0; DBG("modifing gpoll on %d to %08x", fdh->fd, fdh->gfd.events); #endif } return r; } static Eina_Bool _ecore_main_idlers_exist(Efl_Loop_Data *pd) { return pd->idlers || eina_freeq_ptr_pending(eina_freeq_main_get()); } static void _ecore_main_idler_all_call(Eo *loop, Efl_Loop_Data *pd) { if (pd->idlers) efl_event_callback_call(loop, EFL_LOOP_EVENT_IDLE, NULL); eina_freeq_reduce(eina_freeq_main_get(), 256); } #ifdef HAVE_SYS_EPOLL_H static inline int _ecore_main_fdh_epoll_mark_active(Eo *obj, Efl_Loop_Data *pd) { DBG("_ecore_main_fdh_epoll_mark_active"); struct epoll_event ev[32]; int i, ret; int efd = _ecore_get_epoll_fd(obj, pd); memset(&ev, 0, sizeof (ev)); ret = epoll_wait(efd, ev, sizeof(ev) / sizeof(struct epoll_event), 0); if (ret < 0) { if (errno == EINTR) return -1; ERR("epoll_wait failed on fd: %d %s", efd, strerror(errno)); return -1; } for (i = 0; i < ret; i++) { Ecore_Fd_Handler *fdh; fdh = ev[i].data.ptr; if (!ECORE_MAGIC_CHECK(fdh, ECORE_MAGIC_FD_HANDLER)) { ECORE_MAGIC_FAIL(fdh, ECORE_MAGIC_FD_HANDLER, "_ecore_main_fdh_epoll_mark_active"); continue; } if (fdh->delete_me) { ERR("deleted fd in epoll"); continue; } if (ev[i].events & EPOLLIN) fdh->read_active = EINA_TRUE; if (ev[i].events & EPOLLOUT) fdh->write_active = EINA_TRUE; if (ev[i].events & EPOLLERR) fdh->error_active = EINA_TRUE; if (ev[i].events & EPOLLHUP) { fdh->read_active = EINA_TRUE; fdh->write_active = EINA_TRUE; fdh->error_active = EINA_TRUE; } /* We'll add this one anyway outside this function, don't want it twice */ if (fdh->flags & ECORE_FD_ALWAYS) continue; _ecore_try_add_to_call_list(obj, pd, fdh); } return ret; } #endif #ifdef USE_G_MAIN_LOOP static inline int _ecore_main_fdh_glib_mark_active(Eo *obj, Efl_Loop_Data *pd) { Ecore_Fd_Handler *fdh; int ret = 0; // call the prepare callback for all handlers EINA_INLIST_FOREACH(pd->fd_handlers, fdh) { if (fdh->delete_me) continue; if (fdh->gfd.revents & G_IO_IN) fdh->read_active = EINA_TRUE; if (fdh->gfd.revents & G_IO_OUT) fdh->write_active = EINA_TRUE; if (fdh->gfd.revents & G_IO_ERR) fdh->error_active = EINA_TRUE; if (fdh->gfd.revents & G_IO_HUP) { fdh->read_active = EINA_TRUE; fdh->write_active = EINA_TRUE; fdh->error_active = EINA_TRUE; } _ecore_try_add_to_call_list(obj, pd, fdh); if (fdh->gfd.revents & (G_IO_IN | G_IO_OUT | G_IO_ERR)) ret++; } return ret; } // like we are about to enter main_loop_select in _ecore_main_select static gboolean _ecore_main_gsource_prepare(GSource *source EINA_UNUSED, gint *next_time) { Eo *obj = ML_OBJ; Efl_Loop_Data *pd = ML_DAT; gboolean ready = FALSE; in_main_loop++; pd->in_loop = in_main_loop; if ((!ecore_idling) && (!_ecore_glib_idle_enterer_called)) { _update_loop_time(pd); _efl_loop_timer_expired_timers_call(obj, pd, pd->loop_time); efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_ENTER, NULL); _ecore_throttle(); _throttle_do(pd); _ecore_glib_idle_enterer_called = FALSE; if (pd->fd_handlers_with_buffer) _ecore_main_fd_handlers_buf_call(obj, pd); } _ecore_signal_received_process(obj, pd); // don't check fds if somebody quit if (g_main_loop_is_running(ecore_main_loop)) { // only set idling state in dispatch if (ecore_idling && (!_ecore_main_idlers_exist(pd)) && (!pd->message_queue)) { if (_efl_loop_timers_exists(obj, pd)) { int r = -1; double t = _efl_loop_timer_next_get(obj, pd); if ((pd->timer_fd >= 0) && (t > 0.0)) { struct itimerspec ts; ts.it_interval.tv_sec = 0; ts.it_interval.tv_nsec = 0; ts.it_value.tv_sec = t; ts.it_value.tv_nsec = fmod(t * NS_PER_SEC, NS_PER_SEC); // timerfd cannot sleep for 0 time if (ts.it_value.tv_sec || ts.it_value.tv_nsec) { r = timerfd_settime(pd->timer_fd, 0, &ts, NULL); if (r < 0) { ERR("timer set returned %d (errno=%d)", r, errno); close(pd->timer_fd); pd->timer_fd = -1; } else INF("sleeping for %ld s %06ldus", ts.it_value.tv_sec, ts.it_value.tv_nsec / 1000); } } if (r == -1) { *next_time = ceil(t * 1000.0); if (t == 0.0) ready = TRUE; } } else *next_time = -1; } else { *next_time = 0; if (pd->message_queue) ready = TRUE; } if (pd->fd_handlers_with_prep) _ecore_main_prepare_handlers(obj, pd); } else ready = TRUE; in_main_loop--; pd->in_loop = in_main_loop; DBG("leave, timeout = %d", *next_time); // ready if we're not running (about to quit) return ready; } static gboolean _ecore_main_gsource_check(GSource *source EINA_UNUSED) { Eo *obj = ML_OBJ; Efl_Loop_Data *pd = ML_DAT; gboolean ret = FALSE; in_main_loop++; pd->in_loop = in_main_loop; // check if old timers expired if (ecore_idling && (!_ecore_main_idlers_exist(pd)) && (!pd->message_queue)) { if (pd->timer_fd >= 0) { uint64_t count = 0; int r = read(pd->timer_fd, &count, sizeof count); if ((r == -1) && (errno == EAGAIN)) { } else if (r == sizeof count) ret = TRUE; else { // unexpected things happened... fail back to old way ERR("timer read returned %d (errno=%d)", r, errno); close(pd->timer_fd); pd->timer_fd = -1; } } } else ret = TRUE; // check if fds are ready #ifdef HAVE_SYS_EPOLL_H if (pd->epoll_fd >= 0) ecore_fds_ready = (_ecore_main_fdh_epoll_mark_active(obj, pd) > 0); else #endif ecore_fds_ready = (_ecore_main_fdh_glib_mark_active(obj, pd) > 0); _ecore_main_fd_handlers_cleanup(obj, pd); if (ecore_fds_ready) ret = TRUE; // check timers after updating loop time if (!ret && _efl_loop_timers_exists(obj, pd)) ret = (0.0 == _efl_loop_timer_next_get(obj, pd)); in_main_loop--; pd->in_loop = in_main_loop; return ret; } // like we just came out of main_loop_select in _ecore_main_select static gboolean _ecore_main_gsource_dispatch(GSource *source EINA_UNUSED, GSourceFunc callback EINA_UNUSED, gpointer user_data EINA_UNUSED) { Eo *obj = ML_OBJ; Efl_Loop_Data *pd = ML_DAT; gboolean events_ready, timers_ready, idlers_ready; double next_time; _update_loop_time(pd); _efl_loop_timer_enable_new(obj, pd); next_time = _efl_loop_timer_next_get(obj, pd); events_ready = pd->message_queue ? 1 : 0; timers_ready = _efl_loop_timers_exists(obj, pd) && (0.0 == next_time); idlers_ready = _ecore_main_idlers_exist(pd); in_main_loop++; pd->in_loop = in_main_loop; DBG("enter idling=%d fds=%d events=%d timers=%d (next=%.2f) idlers=%d", ecore_idling, ecore_fds_ready, events_ready, timers_ready, next_time, idlers_ready); if (ecore_idling && events_ready) { _ecore_animator_run_reset(); eina_file_statgen_next(); _ecore_main_pre_idle_exit(); efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_EXIT, NULL); ecore_idling = 0; } else if (!ecore_idling && !events_ready) ecore_idling = 1; if (ecore_idling) { _ecore_main_idler_all_call(obj, pd); events_ready = pd->message_queue ? 1 : 0; if (ecore_fds_ready || events_ready || timers_ready) { _ecore_animator_run_reset(); eina_file_statgen_next(); _ecore_main_pre_idle_exit(); efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_EXIT, NULL); ecore_idling = 0; } } // process events if (!ecore_idling) { _ecore_main_fd_handlers_call(obj, pd); if (pd->fd_handlers_with_buffer) _ecore_main_fd_handlers_buf_call(obj, pd); _ecore_signal_received_process(obj, pd); efl_loop_message_process(obj); _ecore_main_fd_handlers_cleanup(obj, pd); _efl_loop_timer_expired_timers_call(obj, pd, pd->loop_time); efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_ENTER, NULL); _ecore_throttle(); _throttle_do(pd); _ecore_glib_idle_enterer_called = TRUE; if (pd->fd_handlers_with_buffer) _ecore_main_fd_handlers_buf_call(obj, pd); } in_main_loop--; pd->in_loop = in_main_loop; return TRUE; // what should be returned here? } static void _ecore_main_gsource_finalize(GSource *source EINA_UNUSED) { } static GSourceFuncs ecore_gsource_funcs = { .prepare = _ecore_main_gsource_prepare, .check = _ecore_main_gsource_check, .dispatch = _ecore_main_gsource_dispatch, .finalize = _ecore_main_gsource_finalize, }; #endif #ifdef HAVE_LIBUV static inline void _ecore_main_loop_uv_check(uv_check_t *handle); static void _ecore_main_loop_uv_prepare(uv_prepare_t *handle); static void _ecore_main_loop_timer_run(uv_timer_t *timer EINA_UNUSED) { Eo *obj = ML_OBJ; Efl_Loop_Data *pd = ML_DAT; if (_ecore_main_uv_idling) { _ecore_main_uv_idling = EINA_FALSE; eina_file_statgen_next(); _ecore_main_pre_idle_exit(); efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_EXIT, NULL); _ecore_animator_run_reset(); } _update_loop_time(pd); _ecore_main_loop_uv_check(NULL); _ecore_main_loop_uv_prepare(NULL); } static inline void _ecore_main_loop_uv_check(uv_check_t *handle EINA_UNUSED) { Eo *obj = ML_OBJ; Efl_Loop_Data *pd = ML_DAT; DBG("_ecore_main_loop_uv_check idling? %d", (int)_ecore_main_uv_idling); in_main_loop++; pd->in_loop = in_main_loop; if (pd->do_quit) goto quit; do { _ecore_main_fd_handlers_call(obj, pd); if (pd->fd_handlers_with_buffer) _ecore_main_fd_handlers_buf_call(obj, pd); _ecore_signal_received_process(obj, pd); efl_loop_message_process(obj); _ecore_main_fd_handlers_cleanup(obj, pd); _efl_loop_timer_expired_timers_call(obj, pd, pd->loop_time); } while (pd->fd_handlers_to_call); quit: in_main_loop--; pd->in_loop = in_main_loop; } #endif static void _ecore_main_loop_setup(Eo *obj, Efl_Loop_Data *pd) { // Please note that this function is being also called in case of a bad // fd to reset the main loop. #ifdef HAVE_SYS_EPOLL_H pd->epoll_fd = epoll_create(1); if (pd->epoll_fd < 0) WRN("Failed to create epoll fd!"); else { eina_file_close_on_exec(pd->epoll_fd, EINA_TRUE); pd->epoll_pid = getpid(); // add polls on all our file descriptors Ecore_Fd_Handler *fdh; EINA_INLIST_FOREACH(pd->fd_handlers, fdh) { if (fdh->delete_me) continue; _ecore_epoll_add(pd->epoll_fd, fdh->fd, _ecore_poll_events_from_fdh(fdh), fdh); _ecore_main_fdh_poll_add(pd, fdh); } } #endif if (obj == ML_OBJ) { #ifdef HAVE_LIBUV // XXX: FIXME: the below uv init should not assert but gracefully // fail with errors DBG("loading lib uv"); # ifdef HAVE_NODEJS void *lib = dlopen(NULL, RTLD_LAZY); # else void *lib = dlopen("libuv.so.1", RTLD_GLOBAL | RTLD_LAZY); # endif if (lib && dlsym(lib, "uv_run")) { DBG("loaded lib uv"); _dl_uv_run = dlsym(lib, "uv_run"); assert(!!_dl_uv_run); _dl_uv_stop = dlsym(lib, "uv_stop"); assert(!!_dl_uv_stop); _dl_uv_default_loop = dlsym(lib, "uv_default_loop"); assert(!!_dl_uv_default_loop); _dl_uv_poll_init_socket = dlsym(lib, "uv_poll_init_socket"); assert(!!_dl_uv_poll_init_socket); _dl_uv_poll_init = dlsym(lib, "uv_poll_init"); assert(!!_dl_uv_poll_init); _dl_uv_poll_start = dlsym(lib, "uv_poll_start"); assert(!!_dl_uv_poll_start); _dl_uv_poll_stop = dlsym(lib, "uv_poll_stop"); assert(!!_dl_uv_poll_stop); _dl_uv_timer_init = dlsym(lib, "uv_timer_init"); assert(!!_dl_uv_timer_init); _dl_uv_timer_start = dlsym(lib, "uv_timer_start"); assert(!!_dl_uv_timer_start); _dl_uv_timer_stop = dlsym(lib, "uv_timer_stop"); assert(!!_dl_uv_timer_stop); _dl_uv_prepare_init = dlsym(lib, "uv_prepare_init"); assert(!!_dl_uv_prepare_init); _dl_uv_prepare_start = dlsym(lib, "uv_prepare_start"); assert(!!_dl_uv_prepare_start); _dl_uv_prepare_stop = dlsym(lib, "uv_prepare_stop"); assert(!!_dl_uv_prepare_stop); _dl_uv_check_init = dlsym(lib, "uv_check_init"); assert(!!_dl_uv_check_init); _dl_uv_check_start = dlsym(lib, "uv_check_start"); assert(!!_dl_uv_check_start); _dl_uv_check_stop = dlsym(lib, "uv_check_stop"); assert(!!_dl_uv_check_stop); _dl_uv_close = dlsym(lib, "uv_close"); assert(!!_dl_uv_close); _dl_uv_loop_alive = dlsym(lib, "uv_loop_alive"); assert(!!_dl_uv_loop_alive); //dlclose(lib); DBG("_dl_uv_prepare_init"); _dl_uv_prepare_init(_dl_uv_default_loop(), &_ecore_main_uv_prepare); DBG("_dl_uv_prepare_start"); _dl_uv_prepare_start(&_ecore_main_uv_prepare, &_ecore_main_loop_uv_prepare); DBG("_dl_uv_prepare_started"); DBG("_dl_uv_check_init"); _dl_uv_check_init(_dl_uv_default_loop(), &_ecore_main_uv_check); DBG("_dl_uv_check_start"); _dl_uv_check_start(&_ecore_main_uv_check, &_ecore_main_loop_uv_check); DBG("_dl_uv_check_started"); _dl_uv_timer_init(_dl_uv_default_loop(), &_ecore_main_uv_handle_timers); } // else // DBG("did not load uv"); DBG("loaded dlsyms uv"); #endif // setup for the g_main_loop only integration #ifdef USE_G_MAIN_LOOP ecore_glib_source = g_source_new(&ecore_gsource_funcs, sizeof(GSource)); if (!ecore_glib_source) CRI("Failed to create glib source for epoll!"); else { g_source_set_priority(ecore_glib_source, G_PRIORITY_HIGH_IDLE + 20); # ifdef HAVE_SYS_EPOLL_H if (pd->epoll_fd >= 0) { // epoll multiplexes fds into the g_main_loop ecore_epoll_fd.fd = pd->epoll_fd; ecore_epoll_fd.events = G_IO_IN; ecore_epoll_fd.revents = 0; g_source_add_poll(ecore_glib_source, &ecore_epoll_fd); } # endif // timerfd gives us better than millisecond accuracy pd->timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); if (pd->timer_fd < 0) WRN("failed to create timer fd!"); else { eina_file_close_on_exec(pd->timer_fd, EINA_TRUE); ecore_timer_fd.fd = pd->timer_fd; ecore_timer_fd.events = G_IO_IN; ecore_timer_fd.revents = 0; g_source_add_poll(ecore_glib_source, &ecore_timer_fd); } ecore_glib_source_id = g_source_attach(ecore_glib_source, NULL); if (ecore_glib_source_id <= 0) CRI("Failed to attach glib source to default context"); } #endif } _ecore_main_timechanges_start(obj); } static void _ecore_main_loop_clear(Eo *obj, Efl_Loop_Data *pd) { if (!pd) return; // Please note that _ecore_main_loop_shutdown is called in cycle to // restart the main loop in case of a bad fd if (obj == ML_OBJ) { _ecore_main_timechanges_stop(obj); #ifdef USE_G_MAIN_LOOP if (ecore_glib_source) { g_source_destroy(ecore_glib_source); ecore_glib_source = NULL; } #endif #ifdef HAVE_LIBUV if (_dl_uv_run) { DBG("_ecore_main_loop_shutdown"); _dl_uv_timer_stop(&_ecore_main_uv_handle_timers); _dl_uv_close((uv_handle_t*)&_ecore_main_uv_handle_timers, 0); } #endif } # ifdef HAVE_SYS_EPOLL_H if (pd->epoll_fd >= 0) { close(pd->epoll_fd); pd->epoll_fd = -1; } #endif if (pd->timer_fd >= 0) { close(pd->timer_fd); pd->timer_fd = -1; } } void _ecore_main_loop_init(void) { DBG("_ecore_main_loop_init"); if (!efl_main_loop_get()) ERR("Cannot create main loop object"); _ecore_main_loop_setup(ML_OBJ, ML_DAT); } void _ecore_main_loop_shutdown(void) { if (!ML_OBJ) return; _ecore_main_loop_clear(ML_OBJ, ML_DAT); // XXX: this seemingly closes fd's it shouldn't.... :( fd 0? efl_replace(&ML_OBJ, NULL); ML_DAT = NULL; } void _ecore_main_loop_iterate(Eo *obj, Efl_Loop_Data *pd) { if (obj == ML_OBJ) { #ifdef HAVE_LIBUV if (!_dl_uv_run) { #endif #ifndef USE_G_MAIN_LOOP _update_loop_time(pd); _ecore_main_loop_iterate_internal(obj, pd, 1); #else g_main_context_iteration(NULL, 0); #endif #ifdef HAVE_LIBUV } else _dl_uv_run(_dl_uv_default_loop(), UV_RUN_ONCE | UV_RUN_NOWAIT); #endif } else { #ifndef USE_G_MAIN_LOOP _update_loop_time(pd); _ecore_main_loop_iterate_internal(obj, pd, 1); #else g_main_context_iteration(NULL, 0); #endif } } int _ecore_main_loop_iterate_may_block(Eo *obj, Efl_Loop_Data *pd, int may_block) { if (obj == ML_OBJ) { #ifdef HAVE_LIBUV if (!_dl_uv_run) { #endif #ifndef USE_G_MAIN_LOOP in_main_loop++; pd->in_loop = in_main_loop; _update_loop_time(pd); _ecore_main_loop_iterate_internal(obj, pd, !may_block); in_main_loop--; pd->in_loop = in_main_loop; return pd->message_queue ? 1 : 0; #else return g_main_context_iteration(NULL, may_block); #endif #ifdef HAVE_LIBUV } else _dl_uv_run(_dl_uv_default_loop(), may_block ? (UV_RUN_ONCE | UV_RUN_NOWAIT) : UV_RUN_ONCE); #endif } else { #ifndef USE_G_MAIN_LOOP pd->in_loop++; _update_loop_time(pd); _ecore_main_loop_iterate_internal(obj, pd, !may_block); pd->in_loop--; return pd->message_queue ? 1 : 0; #else return g_main_context_iteration(NULL, may_block); #endif } return 0; } void _ecore_main_loop_begin(Eo *obj, Efl_Loop_Data *pd) { pd->loop_active++; if (obj == ML_OBJ) { #ifdef HAVE_SYSTEMD if (getenv("NOTIFY_SOCKET")) { _ecore_sd_init(); if (_ecore_sd_notify) _ecore_sd_notify(0, "READY=1"); } #endif #ifdef HAVE_LIBUV if (!_dl_uv_run) { #endif #ifndef USE_G_MAIN_LOOP in_main_loop++; pd->in_loop = in_main_loop; _update_loop_time(pd); while (!pd->do_quit) _ecore_main_loop_iterate_internal(obj, pd, 0); pd->do_quit = 0; in_main_loop--; pd->in_loop = in_main_loop; #else if (!pd->do_quit) { if (!ecore_main_loop) ecore_main_loop = g_main_loop_new(NULL, FALSE); g_main_loop_run(ecore_main_loop); } pd->do_quit = 0; #endif #ifdef HAVE_LIBUV } else { DBG("uv_run"); in_main_loop++; pd->in_loop = in_main_loop; _update_loop_time(pd); while (!pd->do_quit) _dl_uv_run(_dl_uv_default_loop(), UV_RUN_DEFAULT); in_main_loop--; pd->in_loop = in_main_loop; pd->do_quit = 0; DBG("quit"); } #endif } else { #ifndef USE_G_MAIN_LOOP pd->in_loop++; _update_loop_time(pd); while (!pd->do_quit) _ecore_main_loop_iterate_internal(obj, pd, 0); pd->do_quit = 0; pd->in_loop--; #else if (!pd->do_quit) { if (!ecore_main_loop) ecore_main_loop = g_main_loop_new(NULL, 1); g_main_loop_run(ecore_main_loop); } pd->do_quit = 0; #endif } pd->loop_active--; } void _ecore_main_loop_quit(Eo *obj, Efl_Loop_Data *pd) { if (!pd->loop_active) return; pd->do_quit = 1; if (obj != ML_OBJ) return; #ifdef USE_G_MAIN_LOOP if (ecore_main_loop) g_main_loop_quit(ecore_main_loop); #elif defined(HAVE_LIBUV) if (_dl_uv_run) _dl_uv_stop(_dl_uv_default_loop()); #endif } EAPI void ecore_main_loop_iterate(void) { EINA_MAIN_LOOP_CHECK_RETURN; efl_loop_iterate(ML_OBJ); } EAPI int ecore_main_loop_iterate_may_block(int may_block) { EINA_MAIN_LOOP_CHECK_RETURN_VAL(0); return efl_loop_iterate_may_block(ML_OBJ, may_block); } EAPI void ecore_main_loop_begin(void) { DBG("ecore_main_loop_begin"); EINA_MAIN_LOOP_CHECK_RETURN; eina_evlog("+mainloop", NULL, 0.0, NULL); efl_loop_begin(ML_OBJ); eina_evlog("-mainloop", NULL, 0.0, NULL); } EAPI void ecore_main_loop_quit(void) { Eina_Value v = EINA_VALUE_EMPTY; eina_value_setup(&v, EINA_VALUE_TYPE_INT); eina_value_set(&v, 0); EINA_MAIN_LOOP_CHECK_RETURN; efl_loop_quit(ML_OBJ, v); } EAPI int ecore_main_loop_nested_get(void) { return in_main_loop; } EAPI Eina_Bool ecore_main_loop_animator_ticked_get(void) { DBG("ecore_main_loop_animator_ticked_get"); return _ecore_animator_run_get(); } EAPI void ecore_main_loop_select_func_set(Ecore_Select_Function func) { EINA_MAIN_LOOP_CHECK_RETURN; main_loop_select = func; } EAPI Ecore_Select_Function ecore_main_loop_select_func_get(void) { EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL); return main_loop_select; } Ecore_Fd_Handler * _ecore_main_fd_handler_add(Eo *obj, Efl_Loop_Data *pd, Eo *handler, int fd, Ecore_Fd_Handler_Flags flags, Ecore_Fd_Cb func, const void *data, Ecore_Fd_Cb buf_func, const void *buf_data, Eina_Bool is_file) { DBG("_ecore_main_fd_handler_add"); Ecore_Fd_Handler *fdh = NULL; if ((fd < 0) || (flags == 0) || (!func)) return NULL; fdh = ecore_fd_handler_calloc(1); if (!fdh) return NULL; ECORE_MAGIC_SET(fdh, ECORE_MAGIC_FD_HANDLER); fdh->loop = obj; fdh->loop_data = pd; fdh->handler = handler; fdh->fd = fd; fdh->flags = flags; fdh->file = is_file; if (_ecore_main_fdh_poll_add(pd, fdh) < 0) { int err = errno; ERR("Failed to add poll on fd %d (errno = %d: %s)!", fd, err, strerror(err)); ecore_fd_handler_mp_free(fdh); return NULL; } fdh->func = func; fdh->data = (void *)data; fdh->buf_func = buf_func; if (buf_func) pd->fd_handlers_with_buffer = eina_list_append (pd->fd_handlers_with_buffer, fdh); fdh->buf_data = (void *)buf_data; if (is_file) pd->file_fd_handlers = eina_list_append (pd->file_fd_handlers, fdh); if (fdh->flags & ECORE_FD_ALWAYS) pd->always_fd_handlers = eina_list_append (pd->always_fd_handlers, fdh); pd->fd_handlers = (Ecore_Fd_Handler *) eina_inlist_append(EINA_INLIST_GET(pd->fd_handlers), EINA_INLIST_GET(fdh)); return fdh; } void * _ecore_main_fd_handler_del(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd, Ecore_Fd_Handler *fd_handler) { void *r = fd_handler->data; DBG("_ecore_main_fd_handler_del %p", fd_handler); if (fd_handler->delete_me) { ERR("fdh %p deleted twice", fd_handler); return NULL; } fd_handler->handler = NULL; fd_handler->delete_me = EINA_TRUE; if (pd) { _ecore_main_fdh_poll_del(pd, fd_handler); pd->fd_handlers_to_delete = eina_list_append (pd->fd_handlers_to_delete, fd_handler); if (fd_handler->prep_func && pd->fd_handlers_with_prep) pd->fd_handlers_with_prep = eina_list_remove (pd->fd_handlers_with_prep, fd_handler); if (fd_handler->buf_func && pd->fd_handlers_with_buffer) pd->fd_handlers_with_buffer = eina_list_remove (pd->fd_handlers_with_buffer, fd_handler); } else { // The main loop is dead by now, so cleanup is required. ECORE_MAGIC_SET(fd_handler, ECORE_MAGIC_NONE); ecore_fd_handler_mp_free(fd_handler); } return r; } EAPI Ecore_Fd_Handler * ecore_main_fd_handler_add(int fd, Ecore_Fd_Handler_Flags flags, Ecore_Fd_Cb func, const void *data, Ecore_Fd_Cb buf_func, const void *buf_data) { Ecore_Fd_Handler *fdh = NULL; EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL); fdh = _ecore_main_fd_handler_add(efl_main_loop_get(), ML_DAT, NULL, fd, flags, func, data, buf_func, buf_data, EINA_FALSE); if (fdh) fdh->legacy = EINA_TRUE; return fdh; } EAPI Ecore_Fd_Handler * ecore_main_fd_handler_file_add(int fd, Ecore_Fd_Handler_Flags flags, Ecore_Fd_Cb func, const void *data, Ecore_Fd_Cb buf_func, const void *buf_data) { EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL); return _ecore_main_fd_handler_add(efl_main_loop_get(), ML_DAT, NULL, fd, flags, func, data, buf_func, buf_data, EINA_TRUE); } EAPI void * ecore_main_fd_handler_del(Ecore_Fd_Handler *fd_handler) { if (!fd_handler) return NULL; EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL); if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER)) { ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER, "ecore_main_fd_handler_del"); return NULL; } return _ecore_main_fd_handler_del(ML_OBJ, ML_DAT, fd_handler); } #ifdef _WIN32 EAPI Ecore_Win32_Handler * _ecore_main_win32_handler_add(Eo *obj, Efl_Loop_Data *pd, Eo *handler, void *h, Ecore_Win32_Handle_Cb func, const void *data) { Ecore_Win32_Handler *wh; EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL); if (!h || !func) return NULL; wh = ecore_win32_handler_calloc(1); if (!wh) return NULL; ECORE_MAGIC_SET(wh, ECORE_MAGIC_WIN32_HANDLER); wh->loop = obj; wh->loop_data = pd; wh->handler = handler; wh->h = (HANDLE)h; wh->func = func; wh->data = (void *)data; pd->win32_handlers = (Ecore_Win32_Handler *) eina_inlist_append(EINA_INLIST_GET(pd->win32_handlers), EINA_INLIST_GET(wh)); return wh; } void * _ecore_main_win32_handler_del(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd, Ecore_Win32_Handler *win32_handler) { if (win32_handler->delete_me) { ERR("win32 handler %p deleted twice", win32_handler); return NULL; } win32_handler->delete_me = EINA_TRUE; win32_handler->handler = NULL; pd->win32_handlers_to_delete = eina_list_append (pd->win32_handlers_to_delete, win32_handler); return win32_handler->data; } EAPI Ecore_Win32_Handler * ecore_main_win32_handler_add(void *h, Ecore_Win32_Handle_Cb func, const void *data) { EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL); return _ecore_main_win32_handler_add(ML_OBJ, ML_DAT, NULL, h, func, data); } EAPI void * ecore_main_win32_handler_del(Ecore_Win32_Handler *win32_handler) { void *ret = NULL; if (!win32_handler) return NULL; EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL); if (!ECORE_MAGIC_CHECK(win32_handler, ECORE_MAGIC_WIN32_HANDLER)) { ECORE_MAGIC_FAIL(win32_handler, ECORE_MAGIC_WIN32_HANDLER, "ecore_main_win32_handler_del"); return NULL; } ret = _ecore_main_win32_handler_del(ML_OBJ, ML_DAT, win32_handler); return ret; } #else EAPI Ecore_Win32_Handler * _ecore_main_win32_handler_add(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED, Eo *handler EINA_UNUSED, void *h EINA_UNUSED, Ecore_Win32_Handle_Cb func EINA_UNUSED, const void *data EINA_UNUSED) { return NULL; } void * _ecore_main_win32_handler_del(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED, Ecore_Win32_Handler *win32_handler EINA_UNUSED) { return NULL; } EAPI Ecore_Win32_Handler * ecore_main_win32_handler_add(void *h EINA_UNUSED, Ecore_Win32_Handle_Cb func EINA_UNUSED, const void *data EINA_UNUSED) { return NULL; } EAPI void * ecore_main_win32_handler_del(Ecore_Win32_Handler *win32_handler EINA_UNUSED) { return NULL; } #endif EAPI void ecore_main_fd_handler_prepare_callback_set(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Prep_Cb func, const void *data) { if (!fd_handler) return; Efl_Loop_Data *pd = fd_handler->loop_data; EINA_MAIN_LOOP_CHECK_RETURN; if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER)) { ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER, "ecore_main_fd_handler_prepare_callback_set"); return; } fd_handler->prep_func = func; fd_handler->prep_data = (void *)data; if ((!pd->fd_handlers_with_prep) || (pd->fd_handlers_with_prep && (!eina_list_data_find(pd->fd_handlers_with_prep, fd_handler)))) // FIXME: THIS WILL NOT SCALE WITH LOTS OF PREP FUNCTIONS!!! pd->fd_handlers_with_prep = eina_list_append (pd->fd_handlers_with_prep, fd_handler); } EAPI int ecore_main_fd_handler_fd_get(Ecore_Fd_Handler *fd_handler) { if (!fd_handler) return -1; EINA_MAIN_LOOP_CHECK_RETURN_VAL(-1); if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER)) { ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER, "ecore_main_fd_handler_fd_get"); return -1; } return fd_handler->fd; } EAPI Eina_Bool ecore_main_fd_handler_active_get(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Handler_Flags flags) { int ret = EINA_FALSE; EINA_MAIN_LOOP_CHECK_RETURN_VAL(EINA_FALSE); if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER)) { ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER, "ecore_main_fd_handler_active_get"); return EINA_FALSE; } if ((flags & ECORE_FD_READ) && (fd_handler->read_active)) ret = EINA_TRUE; if ((flags & ECORE_FD_WRITE) && (fd_handler->write_active)) ret = EINA_TRUE; if ((flags & ECORE_FD_ERROR) && (fd_handler->error_active)) ret = EINA_TRUE; return ret; } EAPI void ecore_main_fd_handler_active_set(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Handler_Flags flags) { int ret = -1; if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER)) { ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER, "ecore_main_fd_handler_active_set"); return; } fd_handler->flags = flags; if (fd_handler->loop_data) ret = _ecore_main_fdh_poll_modify(fd_handler->loop_data, fd_handler); if (ret < 0) ERR("Failed to mod epoll fd %d, loop data=%p: %s!", fd_handler->fd, fd_handler->loop_data, strerror(errno)); } void _ecore_main_content_clear(Eo *obj, Efl_Loop_Data *pd) { __eina_promise_cancel_data(obj); while (pd->fd_handlers) { Ecore_Fd_Handler *fdh = pd->fd_handlers; pd->fd_handlers = (Ecore_Fd_Handler *) eina_inlist_remove(EINA_INLIST_GET(pd->fd_handlers), EINA_INLIST_GET(fdh)); if ((fdh->handler) && (fdh->legacy)) efl_del(fdh->handler); else { // XXX: can't do this because this fd handler is legacy and might // be cleaned up later in object destructors // ECORE_MAGIC_SET(fdh, ECORE_MAGIC_NONE); // ecore_fd_handler_mp_free(fdh); } } if (pd->fd_handlers_with_buffer) pd->fd_handlers_with_buffer = eina_list_free(pd->fd_handlers_with_buffer); if (pd->fd_handlers_with_prep) pd->fd_handlers_with_prep = eina_list_free(pd->fd_handlers_with_prep); if (pd->file_fd_handlers) pd->file_fd_handlers = eina_list_free(pd->file_fd_handlers); if (pd->always_fd_handlers) pd->always_fd_handlers = eina_list_free(pd->always_fd_handlers); if (pd->fd_handlers_to_delete) pd->fd_handlers_to_delete = eina_list_free(pd->fd_handlers_to_delete); pd->fd_handlers_to_call = NULL; pd->fd_handlers_to_call_current = NULL; pd->do_quit = 0; #ifdef _WIN32 while (pd->win32_handlers) { Ecore_Win32_Handler *wh = pd->win32_handlers; pd->win32_handlers = (Ecore_Win32_Handler *) eina_inlist_remove(EINA_INLIST_GET(pd->win32_handlers), EINA_INLIST_GET(wh)); if (wh->handler) efl_del(wh->handler); else { ECORE_MAGIC_SET(wh, ECORE_MAGIC_NONE); ecore_win32_handler_mp_free(wh); } } if (pd->win32_handlers_to_delete) pd->win32_handlers_to_delete = eina_list_free(pd->win32_handlers_to_delete); pd->win32_handler_current = NULL; #endif } void _ecore_main_shutdown(void) { Efl_Loop_Data *pd = ML_DAT; if (pd->in_loop) { ERR("Calling ecore_shutdown() while still in the main loop!!!"); return; } } static void _ecore_main_prepare_handlers(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd) { Ecore_Fd_Handler *fdh; Eina_List *l, *l2; // call the prepare callback for all handlers with prep functions EINA_LIST_FOREACH_SAFE(pd->fd_handlers_with_prep, l, l2, fdh) { if (!fdh) { pd->fd_handlers_with_prep = eina_list_remove_list (l, pd->fd_handlers_with_prep); continue; } if (!fdh->delete_me && fdh->prep_func) { fdh->references++; _ecore_call_prep_cb(fdh->prep_func, fdh->prep_data, fdh); fdh->references--; } else pd->fd_handlers_with_prep = eina_list_remove_list (pd->fd_handlers_with_prep, l); } } static void _ecore_main_select_fd_too_big_check(int fd) { if (fd < FD_SETSIZE) return; int newsize = fd + 1 - 1; newsize |= newsize >> 1; newsize |= newsize >> 2; newsize |= newsize >> 4; newsize |= newsize >> 8; newsize |= newsize >> 16; newsize++; fprintf(stderr, "ERROR: ecore main loop, fd %i >= max possible fd %i. " "If this continues to be an issue with lots of high value fds " "being needed, perhaps consider recompiling EFL with the option: " "-Dmax-fd-size=%i\n" , fd, FD_SETSIZE, newsize); } #if !defined(USE_G_MAIN_LOOP) static int _ecore_main_select(Eo *obj, Efl_Loop_Data *pd, double timeout) { struct timeval tv, *t; fd_set rfds, wfds, exfds; Ecore_Fd_Handler *fdh; Eina_List *l; int max_fd, ret, outval; #ifndef _WIN32 int err_no; #endif t = NULL; if ((!ECORE_FINITE(timeout)) || (EINA_DBL_EQ(timeout, 0.0))) { // finite() tests for NaN, too big, too small, and infinity. tv.tv_sec = 0; tv.tv_usec = 0; t = &tv; } else if (timeout > 0.0) { int sec, usec; #ifdef FIX_HZ timeout += (0.5 / HZ); sec = (int)timeout; usec = (int)((timeout - (double)sec) * 1000000); #else sec = (int)timeout; usec = (int)((timeout - (double)sec) * 1000000); #endif tv.tv_sec = sec; tv.tv_usec = usec; t = &tv; } max_fd = 0; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&exfds); // call the prepare callback for all handlers if (pd->fd_handlers_with_prep) _ecore_main_prepare_handlers(obj, pd); #ifdef HAVE_SYS_EPOLL_H if (pd->epoll_fd < 0) { #endif EINA_INLIST_FOREACH(pd->fd_handlers, fdh) { if (!fdh->delete_me) { _ecore_main_select_fd_too_big_check(fdh->fd); if ((fdh->flags & ECORE_FD_READ) || (fdh->flags & ECORE_FD_ALWAYS)) { FD_SET(fdh->fd, &rfds); if (fdh->fd > max_fd) max_fd = fdh->fd; } if ((fdh->flags & ECORE_FD_WRITE) || (fdh->flags & ECORE_FD_ALWAYS)) { FD_SET(fdh->fd, &wfds); if (fdh->fd > max_fd) max_fd = fdh->fd; } if ((fdh->flags & ECORE_FD_ERROR) || (fdh->flags & ECORE_FD_ALWAYS)) { FD_SET(fdh->fd, &exfds); if (fdh->fd > max_fd) max_fd = fdh->fd; } } } #ifdef HAVE_SYS_EPOLL_H } else { // polling on the epoll fd will wake when fd in the epoll set is active max_fd = _ecore_get_epoll_fd(obj, pd); if (max_fd > -1) FD_SET(max_fd, &rfds); } #endif EINA_LIST_FOREACH(pd->file_fd_handlers, l, fdh) { if (!fdh->delete_me) { _ecore_main_select_fd_too_big_check(fdh->fd); if ((fdh->flags & ECORE_FD_READ) || (fdh->flags & ECORE_FD_ALWAYS)) { FD_SET(fdh->fd, &rfds); if (fdh->fd > max_fd) max_fd = fdh->fd; } if ((fdh->flags & ECORE_FD_WRITE) || (fdh->flags & ECORE_FD_ALWAYS)) { FD_SET(fdh->fd, &wfds); if (fdh->fd > max_fd) max_fd = fdh->fd; } if ((fdh->flags & ECORE_FD_ERROR) || (fdh->flags & ECORE_FD_ALWAYS)) { FD_SET(fdh->fd, &exfds); if (fdh->fd > max_fd) max_fd = fdh->fd; } if (fdh->fd > max_fd) max_fd = fdh->fd; } } if (_ecore_signal_count_get(obj, pd)) return -1; eina_evlog("RUN", NULL, 0.0, NULL); _update_loop_time(pd); if (ret < 0) { #ifndef _WIN32 if (err_no == EINTR) { outval = -1; goto BAIL; } else if (err_no == EBADF) _ecore_main_fd_handlers_bads_rem(obj, pd); #endif } if (ret > 0) { #ifdef HAVE_SYS_EPOLL_H if (pd->epoll_fd >= 0) _ecore_main_fdh_epoll_mark_active(obj, pd); else #endif { EINA_INLIST_FOREACH(pd->fd_handlers, fdh) { if (!fdh->delete_me) { if (FD_ISSET(fdh->fd, &rfds)) fdh->read_active = EINA_TRUE; if (FD_ISSET(fdh->fd, &wfds)) fdh->write_active = EINA_TRUE; if (FD_ISSET(fdh->fd, &exfds)) fdh->error_active = EINA_TRUE; _ecore_try_add_to_call_list(obj, pd, fdh); } } } EINA_LIST_FOREACH(pd->file_fd_handlers, l, fdh) { if (!fdh->delete_me) { if (FD_ISSET(fdh->fd, &rfds)) fdh->read_active = EINA_TRUE; if (FD_ISSET(fdh->fd, &wfds)) fdh->write_active = EINA_TRUE; if (FD_ISSET(fdh->fd, &exfds)) fdh->error_active = EINA_TRUE; _ecore_try_add_to_call_list(obj, pd, fdh); } } outval = 1; goto BAIL; } outval = 0; BAIL: EINA_LIST_FOREACH(pd->always_fd_handlers, l, fdh) _ecore_try_add_to_call_list(obj, pd, fdh); if (ret > 0) { _ecore_main_fd_handlers_cleanup(obj, pd); #ifdef _WIN32 _ecore_main_win32_handlers_cleanup(obj, pd); #endif } return outval || pd->always_fd_handlers; } #endif #ifndef _WIN32 # ifndef USE_G_MAIN_LOOP static void _ecore_main_fd_handlers_bads_rem(Eo *obj, Efl_Loop_Data *pd) { Ecore_Fd_Handler *fdh; Eina_Inlist *l; int found = 0; ERR("Removing bad fds"); for (l = EINA_INLIST_GET(pd->fd_handlers); l; ) { fdh = (Ecore_Fd_Handler *)l; l = l->next; errno = 0; if ((fcntl(fdh->fd, F_GETFD) < 0) && (errno == EBADF)) { ERR("Found bad fd at index %d", fdh->fd); if (fdh->flags & ECORE_FD_ERROR) { ERR("Fd set for error! calling user"); fdh->references++; if (!_ecore_call_fd_cb(fdh->func, fdh->data, fdh)) { ERR("Fd function err returned 0, remove it"); if (!fdh->delete_me) { fdh->delete_me = EINA_TRUE; _ecore_main_fdh_poll_del(pd, fdh); pd->fd_handlers_to_delete = eina_list_append(pd->fd_handlers_to_delete, fdh); } found++; } fdh->references--; } else { ERR("Problematic fd found at %d! setting it for delete", fdh->fd); if (!fdh->delete_me) { fdh->delete_me = EINA_TRUE; _ecore_main_fdh_poll_del(pd, fdh); pd->fd_handlers_to_delete = eina_list_append(pd->fd_handlers_to_delete, fdh); } found++; } } } if (found == 0) { # ifdef HAVE_GLIB ERR("No bad fd found. Maybe a foreign fd from glib?"); # else ERR("No bad fd found. EEEK!"); # endif } _ecore_main_fd_handlers_cleanup(obj, pd); } # endif #endif static void _ecore_main_fd_handlers_cleanup(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd) { Ecore_Fd_Handler *fdh, *last; Eina_List *l, *l2; // Cleanup deleted caller from the list last = NULL; fdh = pd->fd_handlers_to_call; while (fdh) { if (fdh->delete_me) { if (!last) pd->fd_handlers_to_call = fdh == fdh->next_ready ? NULL : fdh->next_ready; else last->next_ready = fdh == fdh->next_ready ? last : fdh->next_ready; } else last = fdh; if (fdh == fdh->next_ready) break; fdh = fdh->next_ready; } if (!pd->fd_handlers_to_delete) return; EINA_LIST_FOREACH_SAFE(pd->fd_handlers_to_delete, l, l2, fdh) { if (!fdh) { pd->fd_handlers_to_delete = eina_list_remove_list (l, pd->fd_handlers_to_delete); continue; } if (fdh->references) continue; if (pd->fd_handlers_to_call_current == fdh) pd->fd_handlers_to_call_current = NULL; if (fdh->buf_func && pd->fd_handlers_with_buffer) pd->fd_handlers_with_buffer = eina_list_remove (pd->fd_handlers_with_buffer, fdh); if (fdh->prep_func && pd->fd_handlers_with_prep) pd->fd_handlers_with_prep = eina_list_remove (pd->fd_handlers_with_prep, fdh); pd->fd_handlers = (Ecore_Fd_Handler *) eina_inlist_remove(EINA_INLIST_GET(pd->fd_handlers), EINA_INLIST_GET(fdh)); if (fdh->file) pd->file_fd_handlers = eina_list_remove(pd->file_fd_handlers, fdh); if (fdh->flags & ECORE_FD_ALWAYS) pd->always_fd_handlers = eina_list_remove(pd->always_fd_handlers, fdh); ECORE_MAGIC_SET(fdh, ECORE_MAGIC_NONE); ecore_fd_handler_mp_free(fdh); pd->fd_handlers_to_delete = eina_list_remove_list (pd->fd_handlers_to_delete, l); } } #ifdef _WIN32 static void _ecore_main_win32_handlers_cleanup(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd) { Ecore_Win32_Handler *wh; Eina_List *l, *l2; if (!pd->win32_handlers_to_delete) return; EINA_LIST_FOREACH_SAFE(pd->win32_handlers_to_delete, l, l2, wh) { if (!wh) { pd->win32_handlers_to_delete = eina_list_remove_list (l, pd->win32_handlers_to_delete); continue; } // wh->delete_me should be set for all whs at the start of the list if (wh->references) continue; pd->win32_handlers = (Ecore_Win32_Handler *) eina_inlist_remove(EINA_INLIST_GET(pd->win32_handlers), EINA_INLIST_GET(wh)); ECORE_MAGIC_SET(wh, ECORE_MAGIC_NONE); ecore_win32_handler_mp_free(wh); pd->win32_handlers_to_delete = eina_list_remove_list (pd->win32_handlers_to_delete, l); } } #endif static void _ecore_main_fd_handlers_call(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd) { // grab a new list if (!pd->fd_handlers_to_call_current) { pd->fd_handlers_to_call_current = pd->fd_handlers_to_call; pd->fd_handlers_to_call = NULL; } if (!pd->fd_handlers_to_call_current) return; eina_evlog("+fd_handlers", NULL, 0.0, NULL); while (pd->fd_handlers_to_call_current) { Ecore_Fd_Handler *fdh = pd->fd_handlers_to_call_current; if (!fdh->delete_me) { if ((fdh->read_active) || (fdh->write_active) || (fdh->error_active) || (fdh->flags & ECORE_FD_ALWAYS)) { fdh->references++; if (!_ecore_call_fd_cb(fdh->func, fdh->data, fdh)) { if (!fdh->delete_me) { fdh->delete_me = EINA_TRUE; _ecore_main_fdh_poll_del(pd, fdh); pd->fd_handlers_to_delete = eina_list_append (pd->fd_handlers_to_delete, fdh); } } fdh->references--; #ifdef EFL_EXTRA_SANITY_CHECKS _ecore_fd_valid(obj, pd); #endif fdh->read_active = EINA_FALSE; fdh->write_active = EINA_FALSE; fdh->error_active = EINA_FALSE; } } // stop when we point to ourselves if (fdh->next_ready == fdh) { fdh->next_ready = NULL; pd->fd_handlers_to_call_current = NULL; break; } pd->fd_handlers_to_call_current = fdh->next_ready; fdh->next_ready = NULL; } eina_evlog("-fd_handlers", NULL, 0.0, NULL); } static int _ecore_main_fd_handlers_buf_call(Eo *obj, Efl_Loop_Data *pd) { Ecore_Fd_Handler *fdh; Eina_List *l, *l2; int ret; if (!pd->fd_handlers_with_buffer) return 0; eina_evlog("+fd_handlers_buf", NULL, 0.0, NULL); ret = 0; EINA_LIST_FOREACH_SAFE(pd->fd_handlers_with_buffer, l, l2, fdh) { if (!fdh) { pd->fd_handlers_with_buffer = eina_list_remove_list (l, pd->fd_handlers_with_buffer); continue; } if ((!fdh->delete_me) && fdh->buf_func) { fdh->references++; if (_ecore_call_fd_cb(fdh->buf_func, fdh->buf_data, fdh)) { ret |= _ecore_call_fd_cb(fdh->func, fdh->data, fdh); fdh->read_active = EINA_TRUE; _ecore_try_add_to_call_list(obj, pd, fdh); } fdh->references--; } else pd->fd_handlers_with_buffer = eina_list_remove_list (pd->fd_handlers_with_buffer, l); } eina_evlog("-fd_handlers_buf", NULL, 0.0, NULL); return ret; } #ifdef HAVE_LIBUV static void _ecore_main_loop_uv_prepare(uv_prepare_t *handle EINA_UNUSED) { Eo *obj = ML_OBJ; Efl_Loop_Data *pd = ML_DAT; double t = -1; _dl_uv_timer_stop(&_ecore_main_uv_handle_timers); if ((pd->in_loop == 0) && (pd->do_quit)) { _ecore_main_fd_handlers_cleanup(obj, pd); while (pd->fd_handlers) { Ecore_Fd_Handler *fdh = pd->fd_handlers; pd->fd_handlers = (Ecore_Fd_Handler *) eina_inlist_remove(EINA_INLIST_GET(pd->fd_handlers), EINA_INLIST_GET(fdh)); fdh->delete_me = 1; _ecore_main_fdh_poll_del(pd, fdh); ECORE_MAGIC_SET(fdh, ECORE_MAGIC_NONE); ecore_fd_handler_mp_free(fdh); } if (pd->fd_handlers_with_buffer) pd->fd_handlers_with_buffer = eina_list_free(pd->fd_handlers_with_buffer); if (pd->fd_handlers_with_prep) pd->fd_handlers_with_prep = eina_list_free(pd->fd_handlers_with_prep); if (pd->fd_handlers_to_delete) pd->fd_handlers_to_delete = eina_list_free(pd->fd_handlers_to_delete); if (pd->file_fd_handlers) pd->file_fd_handlers = eina_list_free(pd->file_fd_handlers); pd->fd_handlers_to_call = NULL; pd->fd_handlers_to_call_current = NULL; _dl_uv_prepare_stop(&_ecore_main_uv_prepare); _dl_uv_check_stop(&_ecore_main_uv_check); _dl_uv_stop(_dl_uv_default_loop()); return; } in_main_loop++; pd->in_loop = in_main_loop; if (!_ecore_main_uv_idling) { _ecore_main_uv_idling = EINA_TRUE; efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_ENTER, NULL); _ecore_throttle(); _throttle_do(pd); } if (_ecore_main_uv_idling) { _ecore_main_idler_all_call(obj, pd); DBG("called idles"); if (_ecore_main_idlers_exist(pd) || (pd->message_queue)) t = 0.0; } if (pd->do_quit) { DBG("do quit outside loop"); if (_ecore_main_uv_idling) { eina_file_statgen_next(); _ecore_main_pre_idle_exit(); efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_EXIT, NULL); _ecore_animator_run_reset(); _ecore_main_uv_idling = EINA_FALSE; } t = -1; _update_loop_time(pd); _efl_loop_timer_enable_new(obj, pd); goto done; } assert(!pd->fd_handlers_to_call); _update_loop_time(pd); _efl_loop_timer_enable_new(obj, pd); if (_efl_loop_timers_exists(obj, pd) || (t >= 0)) { double tnext = _efl_loop_timer_next_get(obj, pd); if ((t < 0) || ((tnext >= 0) && (tnext < t))) t = tnext; DBG("Should awake after %f", t); if (t >= 0.0) { // _dl_uv_timer_stop(&_ecore_main_uv_handle_timers); _dl_uv_timer_start(&_ecore_main_uv_handle_timers, &_ecore_main_loop_timer_run, t * 1000, 0); } else DBG("Is not going to awake with timer"); } else DBG("Is not going to awake with timer"); done: if (pd->fd_handlers_with_prep) _ecore_main_prepare_handlers(obj, pd); in_main_loop--; pd->in_loop = in_main_loop; } #endif #if !defined(USE_G_MAIN_LOOP) enum { SPIN_MORE, SPIN_RESTART, LOOP_CONTINUE }; static int _ecore_main_loop_spin_core(Eo *obj, Efl_Loop_Data *pd) { // as we are spinning we need to update loop time per spin _update_loop_time(pd); // call all idlers _ecore_main_idler_all_call(obj, pd); // which returns false if no more idelrs exist if (!_ecore_main_idlers_exist(pd)) return SPIN_RESTART; // sneaky - drop through or if checks - the first one to succeed // drops through and returns "continue" so further ones dont run if ((_ecore_main_select(obj, pd, 0.0) > 0) || (pd->message_queue) || (_ecore_signal_count_get(obj, pd) > 0) || (pd->do_quit)) return LOOP_CONTINUE; // default - spin more return SPIN_MORE; } static int _ecore_main_loop_spin_no_timers(Eo *obj, Efl_Loop_Data *pd) { // if we have idlers we HAVE to spin and handle everything // in a polling way - spin in a tight polling loop for (;;) { int action = _ecore_main_loop_spin_core(obj, pd); if (action != SPIN_MORE) return action; // if an idler has added a timer then we need to go through // the start of the spin cycle again to handle cases properly if (_efl_loop_timers_exists(obj, pd)) return SPIN_RESTART; } // just contiune handling events etc. return LOOP_CONTINUE; } static int _ecore_main_loop_spin_timers(Eo *obj, Efl_Loop_Data *pd) { // if we have idlers we HAVE to spin and handle everything // in a polling way - spin in a tight polling loop for (;;) { int action = _ecore_main_loop_spin_core(obj, pd); if (action != SPIN_MORE) return action; // if next timer expires now or in the past - stop spinning and // continue the mainloop walk as our "select" timeout has // expired now if (_efl_loop_timer_next_get(obj, pd) <= 0.0) return LOOP_CONTINUE; } // just contiune handling events etc. return LOOP_CONTINUE; } static void _ecore_fps_marker_1(void) { if (!_ecore_fps_debug) return; t2 = ecore_time_get(); if ((t1 > 0.0) && (t2 > 0.0)) _ecore_fps_debug_runtime_add(t2 - t1); } static void _ecore_fps_marker_2(void) { if (!_ecore_fps_debug) return; t1 = ecore_time_get(); } static void _ecore_main_loop_iterate_internal(Eo *obj, Efl_Loop_Data *pd, int once_only) { double next_time = -1.0; if (obj == ML_OBJ) { in_main_loop++; pd->in_loop = in_main_loop; } // expire any timers _efl_loop_timer_expired_timers_call(obj, pd, pd->loop_time); // process signals into events .... if (obj == ML_OBJ) _ecore_signal_received_process(obj, pd); // if as a result of timers/animators or signals we have accumulated // events, then instantly handle them if (pd->message_queue) { // but first conceptually enter an idle state efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_ENTER, NULL); _ecore_throttle(); _throttle_do(pd); // now quickly poll to see which input fd's are active _ecore_main_select(obj, pd, 0.0); // allow newly queued timers to expire from now on _efl_loop_timer_enable_new(obj, pd); // go straight to processing the events we had queued goto process_all; } if (once_only) { // in once_only mode we should quickly poll for inputs, signals // if we got any events or signals, allow new timers to process. // use bitwise or to force both conditions to be tested and // merged together if (_ecore_main_select(obj, pd, 0.0) | _ecore_signal_count_get(obj, pd)) { _efl_loop_timer_enable_new(obj, pd); goto process_all; } } else { // call idle enterers ... efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_ENTER, NULL); _ecore_throttle(); _throttle_do(pd); } // if these calls caused any buffered events to appear - deal with them if (pd->fd_handlers_with_buffer) _ecore_main_fd_handlers_buf_call(obj, pd); // if there are any (buffered fd handling may generate them) // then jump to processing them */ if (pd->message_queue) { _ecore_main_select(obj, pd, 0.0); _efl_loop_timer_enable_new(obj, pd); goto process_all; } if (once_only) { // in once_only mode enter idle here instead and then return efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_ENTER, NULL); _ecore_throttle(); _throttle_do(pd); _efl_loop_timer_enable_new(obj, pd); goto done; } if (obj == ML_OBJ) _ecore_fps_marker_1(); // start of the sleeping or looping section start_loop: //-************************************************************* // any timers re-added as a result of these are allowed to go _efl_loop_timer_enable_new(obj, pd); // if we have been asked to quit the mainloop then exit at this point if (pd->do_quit) { _efl_loop_timer_enable_new(obj, pd); goto done; } if (!pd->message_queue) { // init flags next_time = _efl_loop_timer_next_get(obj, pd); // no idlers if (!_ecore_main_idlers_exist(pd)) { // sleep until timeout or forever (-1.0) waiting for on fds _ecore_main_select(obj, pd, next_time); } else { int action = LOOP_CONTINUE; // no timers - spin if (next_time < 0) action = _ecore_main_loop_spin_no_timers(obj, pd); // timers - spin else action = _ecore_main_loop_spin_timers(obj, pd); if (action == SPIN_RESTART) goto start_loop; } } if (obj == ML_OBJ) _ecore_fps_marker_2(); // actually wake up and deal with input, events etc. process_all: //-********************************************************* // we came out of our "wait state" so idle has exited if (!once_only) { _ecore_animator_run_reset(); // XXX: eina_file_statgen_next(); _ecore_main_pre_idle_exit(); efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_EXIT, NULL); } // call the fd handler per fd that became alive... // this should read or write any data to the monitored fd and then // post events onto the ecore event pipe if necessary _ecore_main_fd_handlers_call(obj, pd); if (pd->fd_handlers_with_buffer) _ecore_main_fd_handlers_buf_call(obj, pd); // process signals into events .... _ecore_signal_received_process(obj, pd); // handle events ... efl_loop_message_process(obj); _ecore_main_fd_handlers_cleanup(obj, pd); if (once_only) { // if in once_only mode handle idle exiting efl_event_callback_call(obj, EFL_LOOP_EVENT_IDLE_ENTER, NULL); _ecore_throttle(); _throttle_do(pd); } done: //-***************************************************************** // Agressively flush animator _ecore_animator_flush(); if (!once_only) eina_slstr_local_clear(); // Free all short lived strings if (obj == ML_OBJ) { in_main_loop--; pd->in_loop = in_main_loop; } } #endif #ifdef _WIN32 typedef struct { DWORD objects_nbr; HANDLE *objects; DWORD timeout; } Ecore_Main_Win32_Thread_Data; static unsigned int __stdcall _ecore_main_win32_objects_wait_thread(void *data) { Ecore_Main_Win32_Thread_Data *td = (Ecore_Main_Win32_Thread_Data *)data; return MsgWaitForMultipleObjects(td->objects_nbr, (const HANDLE *)td->objects, FALSE, td->timeout, QS_ALLINPUT); } static DWORD _ecore_main_win32_objects_wait(DWORD objects_nbr, const HANDLE *objects, DWORD timeout) { Ecore_Main_Win32_Thread_Data *threads_data; HANDLE *threads_handles; DWORD threads_nbr, threads_remain, objects_idx, result, i; if (objects_nbr < MAXIMUM_WAIT_OBJECTS) return MsgWaitForMultipleObjects(objects_nbr, objects, EINA_FALSE, timeout, QS_ALLINPUT); // too much objects, so we launch a bunch of threads to // wait for, each one calls MsgWaitForMultipleObjects threads_nbr = objects_nbr / (MAXIMUM_WAIT_OBJECTS - 1); threads_remain = objects_nbr % (MAXIMUM_WAIT_OBJECTS - 1); if (threads_remain > 0) threads_nbr++; if (threads_nbr > MAXIMUM_WAIT_OBJECTS) { CRI("Too much objects to wait for (%lu).", objects_nbr); return WAIT_FAILED; } threads_handles = (HANDLE *)malloc(threads_nbr * sizeof(HANDLE)); if (!threads_handles) { ERR("Can not allocate memory for the waiting thread."); return WAIT_FAILED; } threads_data = (Ecore_Main_Win32_Thread_Data *) malloc(threads_nbr * sizeof(Ecore_Main_Win32_Thread_Data)); if (!threads_data) { ERR("Can not allocate memory for the waiting thread."); goto free_threads_handles; } objects_idx = 0; for (i = 0; i < threads_nbr; i++) { threads_data[i].timeout = timeout; threads_data[i].objects = (HANDLE *)objects + objects_idx; if ((i == (threads_nbr - 1)) && (threads_remain != 0)) { threads_data[i].objects_nbr = threads_remain; objects_idx += threads_remain; } else { threads_data[i].objects_nbr = (MAXIMUM_WAIT_OBJECTS - 1); objects_idx += (MAXIMUM_WAIT_OBJECTS - 1); } threads_handles[i] = (HANDLE)_beginthreadex (NULL, 0, _ecore_main_win32_objects_wait_thread, &threads_data[i], 0, NULL); if (!threads_handles[i]) { DWORD j; ERR("Can not create the waiting threads."); WaitForMultipleObjects(i, threads_handles, TRUE, INFINITE); for (j = 0; j < i; j++) CloseHandle(threads_handles[i]); goto free_threads_data; } } result = WaitForMultipleObjects(threads_nbr, threads_handles, FALSE, // we wait until one thread signaled INFINITE); if (result < (WAIT_OBJECT_0 + threads_nbr)) { DWORD wait_res; // One of the thread callback has exited so we retrieve // its exit status, that is the returned value of // MsgWaitForMultipleObjects() if (GetExitCodeThread(threads_handles[result - WAIT_OBJECT_0], &wait_res)) { WaitForMultipleObjects(threads_nbr, threads_handles, TRUE, INFINITE); for (i = 0; i < threads_nbr; i++) CloseHandle(threads_handles[i]); free(threads_data); free(threads_handles); return wait_res; } } else { ERR("Error when waiting threads."); if (result == WAIT_FAILED) ERR("%s", evil_last_error_get()); goto close_thread; } close_thread: WaitForMultipleObjects(threads_nbr, threads_handles, TRUE, INFINITE); for (i = 0; i < threads_nbr; i++) CloseHandle(threads_handles[i]); free_threads_data: free(threads_data); free_threads_handles: free(threads_handles); return WAIT_FAILED; } static unsigned int _stdin_wait_thread(void *data EINA_UNUSED) { int c = getc(stdin); ungetc(c, stdin); return 0; } static int _ecore_main_win32_select(int nfds EINA_UNUSED, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *tv) { Efl_Loop_Data *pd = ML_DAT; HANDLE *objects; int *sockets; Ecore_Fd_Handler *fdh; Ecore_Win32_Handler *wh; static HANDLE stdin_wait_thread = INVALID_HANDLE_VALUE; HANDLE stdin_handle; DWORD result, timeout; MSG msg; unsigned int fds_nbr = 0; unsigned int objects_nbr = 0; unsigned int events_nbr = 0; unsigned int i; int res; Eina_Bool stdin_thread_done = EINA_FALSE; fds_nbr = eina_inlist_count(EINA_INLIST_GET(pd->fd_handlers)); sockets = (int *)malloc(fds_nbr * sizeof(int)); if (!sockets) return -1; objects = (HANDLE)malloc((fds_nbr + eina_inlist_count (EINA_INLIST_GET(pd->win32_handlers))) * sizeof(HANDLE)); if (!objects) { free(sockets); return -1; } // Create an event object per socket EINA_INLIST_FOREACH(pd->fd_handlers, fdh) { if (fdh->delete_me) continue; WSAEVENT event; long network_event; network_event = 0; if (readfds) { if (FD_ISSET(fdh->fd, readfds)) network_event |= FD_READ | FD_CONNECT | FD_ACCEPT; } if (writefds) { if (FD_ISSET(fdh->fd, writefds)) network_event |= FD_WRITE | FD_CLOSE; } if (exceptfds) { if (FD_ISSET(fdh->fd, exceptfds)) network_event |= FD_OOB; } if (network_event) { event = WSACreateEvent(); WSAEventSelect(fdh->fd, event, network_event); objects[objects_nbr] = event; sockets[events_nbr] = fdh->fd; events_nbr++; objects_nbr++; } } stdin_handle = GetStdHandle(STD_INPUT_HANDLE); // store the HANDLEs in the objects to wait for EINA_INLIST_FOREACH(pd->win32_handlers, wh) { if (wh->delete_me) continue; if (wh->h == stdin_handle) { if (stdin_wait_thread == INVALID_HANDLE_VALUE) stdin_wait_thread = (HANDLE)_beginthreadex(NULL, 0, _stdin_wait_thread, NULL, 0, NULL); objects[objects_nbr] = stdin_wait_thread; } else objects[objects_nbr] = wh->h; objects_nbr++; } // Empty the queue before waiting while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } // Wait for any message sent or posted to this queue // or for one of the passed handles be set to signaled. if (!tv) timeout = INFINITE; else timeout = (DWORD)((tv->tv_sec * 1000.0) + (tv->tv_usec / 1000.0)); if (timeout == 0) { res = 0; goto err; } result = _ecore_main_win32_objects_wait(objects_nbr, (const HANDLE *)objects, timeout); if (readfds) FD_ZERO(readfds); if (writefds) FD_ZERO(writefds); if (exceptfds) FD_ZERO(exceptfds); // The result tells us the type of event we have. if (result == WAIT_FAILED) { WRN("%s", evil_last_error_get()); res = -1; } else if (result == WAIT_TIMEOUT) { INF("time-out interval elapsed."); res = 0; } else if (result == (WAIT_OBJECT_0 + objects_nbr)) { while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } res = 0; } else if (result < WAIT_OBJECT_0 + events_nbr) { WSANETWORKEVENTS network_event; WSAEnumNetworkEvents(sockets[result], objects[result], &network_event); if ((network_event.lNetworkEvents & (FD_READ | FD_CONNECT | FD_ACCEPT)) && readfds) FD_SET(sockets[result], readfds); if ((network_event.lNetworkEvents & (FD_WRITE | FD_CLOSE)) && writefds) FD_SET(sockets[result], writefds); if ((network_event.lNetworkEvents & FD_OOB) && exceptfds) FD_SET(sockets[result], exceptfds); res = 1; } else if ((result >= (WAIT_OBJECT_0 + events_nbr)) && (result < (WAIT_OBJECT_0 + objects_nbr))) { if (!pd->win32_handler_current) // regular main loop, start from head pd->win32_handler_current = pd->win32_handlers; else // recursive main loop, continue from where we were pd->win32_handler_current = (Ecore_Win32_Handler *) EINA_INLIST_GET(pd->win32_handler_current)->next; if (objects[result - WAIT_OBJECT_0] == stdin_wait_thread) stdin_thread_done = EINA_TRUE; while (pd->win32_handler_current) { wh = pd->win32_handler_current; if ((objects[result - WAIT_OBJECT_0] == wh->h) || ((objects[result - WAIT_OBJECT_0] == stdin_wait_thread) && (wh->h == stdin_handle))) { if (!wh->delete_me) { wh->references++; if (!wh->func(wh->data, wh)) { wh->delete_me = EINA_TRUE; pd->win32_handlers_to_delete = eina_list_append (pd->win32_handlers_to_delete, wh); } wh->references--; } } if (pd->win32_handler_current) // may have changed in recursive main loops pd->win32_handler_current = (Ecore_Win32_Handler *) EINA_INLIST_GET(pd->win32_handler_current)->next; } res = 1; } else { ERR("unknown result...\n"); res = -1; } err: // Remove event objects again for (i = 0; i < events_nbr; i++) WSACloseEvent(objects[i]); if (stdin_thread_done) stdin_wait_thread = INVALID_HANDLE_VALUE; free(objects); free(sockets); return res; } #endif