efl/legacy/ecore/src/lib/ecore/ecore_main.c

1906 lines
52 KiB
C
Raw Normal View History

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
# include <winsock2.h>
# undef WIN32_LEAN_AND_MEAN
# ifndef USER_TIMER_MINIMUM
# define USER_TIMER_MINIMUM 0x0a
# endif
#endif
2004-10-20 10:51:29 -07:00
#ifdef __SUNPRO_C
# include <ieeefp.h>
# include <string.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#ifndef _MSC_VER
# include <sys/time.h>
# include <unistd.h>
#else
# include <float.h>
#endif
#ifdef HAVE_ISFINITE
# define ECORE_FINITE(t) isfinite(t)
#else
# ifdef _MSC_VER
# define ECORE_FINITE(t) _finite(t)
# else
# define ECORE_FINITE(t) finite(t)
# endif
#endif
//#define FIX_HZ 1
#ifdef FIX_HZ
# ifndef _MSC_VER
# include <sys/param.h>
# endif
# ifndef HZ
# define HZ 100
# endif
#endif
#ifdef HAVE_EVIL
# include <Evil.h>
#endif
#include "Ecore.h"
#include "ecore_private.h"
#ifdef HAVE_SYS_EPOLL_H
# define HAVE_EPOLL 1
# include <sys/epoll.h>
#else
# define HAVE_EPOLL 0
# define EPOLLIN 1
# define EPOLLOUT 2
# define EPOLLERR 8
#define EPOLL_CTL_ADD 1
#define EPOLL_CTL_DEL 2
#define EPOLL_CTL_MOD 3
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events;
epoll_data_t data;
};
static inline int
epoll_create(int size)
{
return -1;
}
static inline int
epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
{
return -1;
}
static inline int
epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
{
return -1;
}
#endif
#ifdef HAVE_SYS_TIMERFD_H
#include <sys/timerfd.h>
#else
/* fallback code if we don't have real timerfd - reduces number of ifdefs */
#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC 0 /* bogus value */
#endif
#ifndef TFD_NONBLOCK
#define TFD_NONBLOCK 0 /* bogus value */
#endif
static inline int
timerfd_create(int clockid __UNUSED__, int flags __UNUSED__)
{
return -1;
}
static inline int
timerfd_settime(int fd __UNUSED__, int flags __UNUSED__,
const struct itimerspec *new_value __UNUSED__,
struct itimerspec *old_value __UNUSED__)
{
return -1;
}
#endif /* HAVE_SYS_TIMERFD_H */
#ifdef USE_G_MAIN_LOOP
# include <glib.h>
#endif
#define NS_PER_SEC (1000.0 * 1000.0 * 1000.0)
struct _Ecore_Fd_Handler
{
EINA_INLIST;
ECORE_MAGIC;
Ecore_Fd_Handler *next_ready;
int fd;
Ecore_Fd_Handler_Flags flags;
Ecore_Fd_Cb func;
void *data;
Ecore_Fd_Cb buf_func;
void *buf_data;
Ecore_Fd_Prep_Cb prep_func;
void *prep_data;
Fix fd_handlers when using recursive main loops. If an fd_handler created a recursive main loop (just called ecore_main_loop_begin()), then this recursive main loop should continue to process fd_handlers from there and on, thus fd_handler_current (and win32_handler_current) was added. When going back from recursion, the current iterator should be updated properly. This patch also fixes the deletion of fd_handler from recursive main loops by reference counting them. This way, the node will not be free()d inside inner loop cleanups and then crash when going back to outer loop. PS: win32 code is untested (or even compiled). The following test case used to crash but not anymore: #include <Ecore.h> #include <Eina.h> #include <unistd.h> static int _log_dom; #define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__) #define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__) static Ecore_Fd_Handler *handle; static int a[2], b[2]; static int cb2(void *data, Ecore_Fd_Handler *h) { INF("cb2 - delete cb1 handle"); ecore_main_fd_handler_del(handle); ecore_main_loop_quit(); /* quits inner main loop */ return 0; } static int cb1(void *data, Ecore_Fd_Handler *h) { unsigned char ch = 222; INF("cb1: begin"); INF(" add cb2"); ecore_main_fd_handler_add(b[0], ECORE_FD_READ, cb2, NULL, NULL, NULL); INF(" wake up pipe b"); if (write(b[1], &ch, 1) != 1) ERR("could not write to pipe b"); INF(" inner main loop begin (recurse)"); ecore_main_loop_begin(); /* will it crash due * ecore_main_fd_handler_del(handle) * inside cb2()? It used to! */ INF("cb1: end"); ecore_main_loop_quit(); /* quits outer main loop */ return 0; } int main(void) { unsigned char ch = 111; ecore_init(); _log_dom = eina_log_domain_register("test", EINA_COLOR_CYAN); pipe(a); pipe(b); /* * Creating a new main loop from inside an fd_handler callback, * and inside this new (inner) main loop deleting the caller * callback used to crash since the handle would be effectively * free()d, but when the recursion is over the pointer would be * used. */ INF("main: begin"); handle = ecore_main_fd_handler_add (a[0], ECORE_FD_READ, cb1, NULL, NULL, NULL); INF("main: wake up pipe a"); if (write(a[1], &ch, 1) != 1) ERR("could not write to pipe a"); ecore_main_loop_begin(); INF("main: end"); return 0; } SVN revision: 46443
2010-02-24 12:59:44 -08:00
int references;
Eina_Bool read_active : 1;
Eina_Bool write_active : 1;
Eina_Bool error_active : 1;
Eina_Bool delete_me : 1;
#if defined(USE_G_MAIN_LOOP)
GPollFD gfd;
#endif
};
#ifdef _WIN32
struct _Ecore_Win32_Handler
{
EINA_INLIST;
ECORE_MAGIC;
HANDLE h;
Ecore_Win32_Handle_Cb func;
void *data;
int references;
Eina_Bool delete_me : 1;
};
#endif
#ifndef USE_G_MAIN_LOOP
static int _ecore_main_select(double timeout);
#endif
static void _ecore_main_prepare_handlers(void);
static void _ecore_main_fd_handlers_cleanup(void);
#ifndef _WIN32
# ifndef USE_G_MAIN_LOOP
static void _ecore_main_fd_handlers_bads_rem(void);
# endif
#endif
static void _ecore_main_fd_handlers_call(void);
static int _ecore_main_fd_handlers_buf_call(void);
#ifndef USE_G_MAIN_LOOP
static void _ecore_main_loop_iterate_internal(int once_only);
#endif
#ifdef _WIN32
static int _ecore_main_win32_select(int nfds, fd_set *readfds, fd_set *writefds,
2010-09-29 23:09:20 -07:00
fd_set *exceptfds, struct timeval *timeout);
static void _ecore_main_win32_handlers_cleanup(void);
#endif
static int in_main_loop = 0;
#ifndef USE_G_MAIN_LOOP
static int do_quit = 0;
#endif
static Ecore_Fd_Handler *fd_handlers = NULL;
Fix fd_handlers when using recursive main loops. If an fd_handler created a recursive main loop (just called ecore_main_loop_begin()), then this recursive main loop should continue to process fd_handlers from there and on, thus fd_handler_current (and win32_handler_current) was added. When going back from recursion, the current iterator should be updated properly. This patch also fixes the deletion of fd_handler from recursive main loops by reference counting them. This way, the node will not be free()d inside inner loop cleanups and then crash when going back to outer loop. PS: win32 code is untested (or even compiled). The following test case used to crash but not anymore: #include <Ecore.h> #include <Eina.h> #include <unistd.h> static int _log_dom; #define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__) #define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__) static Ecore_Fd_Handler *handle; static int a[2], b[2]; static int cb2(void *data, Ecore_Fd_Handler *h) { INF("cb2 - delete cb1 handle"); ecore_main_fd_handler_del(handle); ecore_main_loop_quit(); /* quits inner main loop */ return 0; } static int cb1(void *data, Ecore_Fd_Handler *h) { unsigned char ch = 222; INF("cb1: begin"); INF(" add cb2"); ecore_main_fd_handler_add(b[0], ECORE_FD_READ, cb2, NULL, NULL, NULL); INF(" wake up pipe b"); if (write(b[1], &ch, 1) != 1) ERR("could not write to pipe b"); INF(" inner main loop begin (recurse)"); ecore_main_loop_begin(); /* will it crash due * ecore_main_fd_handler_del(handle) * inside cb2()? It used to! */ INF("cb1: end"); ecore_main_loop_quit(); /* quits outer main loop */ return 0; } int main(void) { unsigned char ch = 111; ecore_init(); _log_dom = eina_log_domain_register("test", EINA_COLOR_CYAN); pipe(a); pipe(b); /* * Creating a new main loop from inside an fd_handler callback, * and inside this new (inner) main loop deleting the caller * callback used to crash since the handle would be effectively * free()d, but when the recursion is over the pointer would be * used. */ INF("main: begin"); handle = ecore_main_fd_handler_add (a[0], ECORE_FD_READ, cb1, NULL, NULL, NULL); INF("main: wake up pipe a"); if (write(a[1], &ch, 1) != 1) ERR("could not write to pipe a"); ecore_main_loop_begin(); INF("main: end"); return 0; } SVN revision: 46443
2010-02-24 12:59:44 -08:00
static Ecore_Fd_Handler *fd_handler_current = NULL;
static Eina_List *fd_handlers_with_prep = NULL;
static Eina_List *fd_handlers_with_buffer = NULL;
static Eina_List *fd_handlers_to_delete = NULL;
/* single linked list of ready fdhs, terminated by loop to self */
static Ecore_Fd_Handler *fd_handlers_to_call;
static Ecore_Fd_Handler *fd_handlers_to_call_current;
#ifdef _WIN32
static Ecore_Win32_Handler *win32_handlers = NULL;
Fix fd_handlers when using recursive main loops. If an fd_handler created a recursive main loop (just called ecore_main_loop_begin()), then this recursive main loop should continue to process fd_handlers from there and on, thus fd_handler_current (and win32_handler_current) was added. When going back from recursion, the current iterator should be updated properly. This patch also fixes the deletion of fd_handler from recursive main loops by reference counting them. This way, the node will not be free()d inside inner loop cleanups and then crash when going back to outer loop. PS: win32 code is untested (or even compiled). The following test case used to crash but not anymore: #include <Ecore.h> #include <Eina.h> #include <unistd.h> static int _log_dom; #define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__) #define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__) static Ecore_Fd_Handler *handle; static int a[2], b[2]; static int cb2(void *data, Ecore_Fd_Handler *h) { INF("cb2 - delete cb1 handle"); ecore_main_fd_handler_del(handle); ecore_main_loop_quit(); /* quits inner main loop */ return 0; } static int cb1(void *data, Ecore_Fd_Handler *h) { unsigned char ch = 222; INF("cb1: begin"); INF(" add cb2"); ecore_main_fd_handler_add(b[0], ECORE_FD_READ, cb2, NULL, NULL, NULL); INF(" wake up pipe b"); if (write(b[1], &ch, 1) != 1) ERR("could not write to pipe b"); INF(" inner main loop begin (recurse)"); ecore_main_loop_begin(); /* will it crash due * ecore_main_fd_handler_del(handle) * inside cb2()? It used to! */ INF("cb1: end"); ecore_main_loop_quit(); /* quits outer main loop */ return 0; } int main(void) { unsigned char ch = 111; ecore_init(); _log_dom = eina_log_domain_register("test", EINA_COLOR_CYAN); pipe(a); pipe(b); /* * Creating a new main loop from inside an fd_handler callback, * and inside this new (inner) main loop deleting the caller * callback used to crash since the handle would be effectively * free()d, but when the recursion is over the pointer would be * used. */ INF("main: begin"); handle = ecore_main_fd_handler_add (a[0], ECORE_FD_READ, cb1, NULL, NULL, NULL); INF("main: wake up pipe a"); if (write(a[1], &ch, 1) != 1) ERR("could not write to pipe a"); ecore_main_loop_begin(); INF("main: end"); return 0; } SVN revision: 46443
2010-02-24 12:59:44 -08:00
static Ecore_Win32_Handler *win32_handler_current = NULL;
static Eina_Bool win32_handlers_delete_me = EINA_FALSE;
#endif
#ifdef _WIN32
Ecore_Select_Function main_loop_select = _ecore_main_win32_select;
#else
Ecore_Select_Function main_loop_select = select;
#endif
#ifndef USE_G_MAIN_LOOP
static double t1 = 0.0;
static double t2 = 0.0;
#endif
static int timer_fd = -1;
static int epoll_fd = -1;
static pid_t epoll_pid;
#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_fds_ready;
#endif
static inline void
_ecore_fd_valid(void)
{
if (HAVE_EPOLL && epoll_fd >= 0)
{
if (fcntl(epoll_fd, F_GETFD) < 0)
{
ERR("arghhh you caught me! report a backtrace to edevel!");
pause();
}
}
}
static inline void
_ecore_try_add_to_call_list(Ecore_Fd_Handler *fdh)
{
/* check if this fdh is already in the list */
if (fdh->next_ready)
return;
if (fdh->read_active || fdh->write_active || fdh->error_active)
{
/*
* 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 = fd_handlers_to_call ? fd_handlers_to_call : fdh;
fd_handlers_to_call = fdh;
}
}
static inline int
_ecore_get_epoll_fd(void)
{
if (epoll_pid && epoll_pid != getpid())
{
/* forked! */
_ecore_main_loop_shutdown();
}
if (epoll_pid == 0 && epoll_fd < 0)
{
_ecore_main_loop_init();
}
return 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;
INF("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;
if (fdh->flags & ECORE_FD_WRITE) events |= EPOLLOUT;
if (fdh->flags & ECORE_FD_ERROR) events |= EPOLLERR;
return events;
}
#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;
if (fdh->flags & ECORE_FD_WRITE) events |= G_IO_OUT;
if (fdh->flags & ECORE_FD_ERROR) events |= G_IO_ERR;
return events;
}
#endif
static inline int
_ecore_main_fdh_poll_add(Ecore_Fd_Handler *fdh)
{
int r = 0;
if (HAVE_EPOLL && epoll_fd >= 0)
{
r = _ecore_epoll_add(_ecore_get_epoll_fd(), fdh->fd,
_ecore_poll_events_from_fdh(fdh), fdh);
}
else
{
#ifdef USE_G_MAIN_LOOP
fdh->gfd.fd = fdh->fd;
fdh->gfd.events = _gfd_events_from_fdh(fdh);
fdh->gfd.revents = 0;
INF("adding gpoll on %d %08x", fdh->fd, fdh->gfd.events);
g_source_add_poll(ecore_glib_source, &fdh->gfd);
2010-09-18 20:09:09 -07:00
#endif
}
return r;
}
static inline void
_ecore_main_fdh_poll_del(Ecore_Fd_Handler *fdh)
{
if (HAVE_EPOLL && epoll_fd >= 0)
{
struct epoll_event ev;
int efd = _ecore_get_epoll_fd();
memset(&ev, 0, sizeof (ev));
INF("removing poll on %d", fdh->fd);
/* could get an EBADF if somebody closed the FD before removing it */
if ((epoll_ctl(efd, EPOLL_CTL_DEL, fdh->fd, &ev) < 0))
{
if (errno == EBADF)
{
WRN("fd %d was closed, can't remove from epoll - reinit!",
fdh->fd);
_ecore_main_loop_shutdown();
_ecore_main_loop_init();
}
else
{
ERR("Failed to delete epoll fd %d! (errno=%d)", fdh->fd, errno);
}
}
}
else
{
#ifdef USE_G_MAIN_LOOP
fdh->gfd.fd = fdh->fd;
fdh->gfd.events = _gfd_events_from_fdh(fdh);
fdh->gfd.revents = 0;
INF("adding gpoll on %d %08x", fdh->fd, fdh->gfd.events);
g_source_add_poll(ecore_glib_source, &fdh->gfd);
2010-09-18 20:09:09 -07:00
#endif
}
}
static inline int
_ecore_main_fdh_poll_modify(Ecore_Fd_Handler *fdh)
{
int r = 0;
if (HAVE_EPOLL && epoll_fd >= 0)
{
struct epoll_event ev;
int efd = _ecore_get_epoll_fd();
memset(&ev, 0, sizeof (ev));
ev.events = _ecore_poll_events_from_fdh(fdh);
ev.data.ptr = fdh;
INF("modifing epoll on %d to %08x", fdh->fd, ev.events);
r = epoll_ctl(efd, EPOLL_CTL_MOD, fdh->fd, &ev);
}
else
{
#ifdef USE_G_MAIN_LOOP
fdh->gfd.fd = fdh->fd;
fdh->gfd.events = _gfd_events_from_fdh(fdh);
fdh->gfd.revents = 0;
INF("modifing gpoll on %d to %08x", fdh->fd, fdh->gfd.events);
2010-09-18 20:09:09 -07:00
#endif
}
return r;
}
static inline int _ecore_main_fdh_epoll_mark_active(void)
{
struct epoll_event ev[32];
int i, ret;
int efd = _ecore_get_epoll_fd();
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 %d", errno);
return -1;
}
for (i = 0; i < ret; i++)
{
Ecore_Fd_Handler *fdh;
2011-01-07 00:18:19 -08:00
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;
_ecore_try_add_to_call_list(fdh);
}
return ret;
}
#ifdef USE_G_MAIN_LOOP
static inline int _ecore_main_fdh_glib_mark_active(void)
{
Ecore_Fd_Handler *fdh;
int ret = 0;
/* call the prepare callback for all handlers */
EINA_INLIST_FOREACH(fd_handlers, fdh)
{
if (fdh->delete_me)
continue;
2010-11-30 19:45:22 -08:00
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;
_ecore_try_add_to_call_list(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 __UNUSED__, gint *next_time)
{
gboolean running;
in_main_loop++;
if (!ecore_idling)
{
while (_ecore_timer_call(_ecore_time_loop_time));
_ecore_timer_cleanup();
/* when idling, busy loop checking the fds only */
_ecore_idle_enterer_call();
_ecore_throttle();
}
while (_ecore_signal_count_get()) _ecore_signal_call();
/* don't check fds if somebody quit */
running = g_main_loop_is_running(ecore_main_loop);
if (running)
{
/* only set idling state in dispatch */
if (ecore_idling && !_ecore_idler_exist() && !_ecore_event_exist())
{
if (_ecore_timers_exists())
{
int r = -1;
double t = _ecore_timer_next_get();
if (timer_fd >= 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(timer_fd, 0, &ts, NULL);
if (r < 0)
{
ERR("timer set returned %d (errno=%d)", r, errno);
close(timer_fd);
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);
}
else
*next_time = -1;
}
else
*next_time = 0;
if (fd_handlers_with_prep)
_ecore_main_prepare_handlers();
}
in_main_loop--;
INF("leave, timeout = %d", *next_time);
/* ready if we're not running (about to quit) */
return !running;
}
static gboolean
_ecore_main_gsource_check(GSource *source __UNUSED__)
{
gboolean ret = FALSE;
in_main_loop++;
/* check if old timers expired */
if (ecore_idling && !_ecore_idler_exist() && !_ecore_event_exist())
{
if (timer_fd >= 0)
{
uint64_t count = 0;
int r = read(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(timer_fd);
timer_fd = -1;
}
}
}
else
ret = TRUE;
/* check if fds are ready */
if (HAVE_EPOLL && epoll_fd >= 0)
ecore_fds_ready = (_ecore_main_fdh_epoll_mark_active() > 0);
else
ecore_fds_ready = (_ecore_main_fdh_glib_mark_active() > 0);
_ecore_main_fd_handlers_cleanup();
/* check timers after updating loop time */
_ecore_time_loop_time = ecore_time_get();
if (!ret && _ecore_timers_exists())
{
double next_time = _ecore_timer_next_get();
ret = _ecore_timers_exists() && (0.0 == next_time);
}
_ecore_timer_enable_new();
in_main_loop--;
return ret || ecore_fds_ready;
}
/* like we just came out of main_loop_select in _ecore_main_select */
static gboolean
_ecore_main_gsource_dispatch(GSource *source __UNUSED__, GSourceFunc callback __UNUSED__, gpointer user_data __UNUSED__)
{
gboolean events_ready, timers_ready, idlers_ready;
double next_time = _ecore_timer_next_get();
events_ready = _ecore_event_exist();
timers_ready = _ecore_timers_exists() && (0.0 == next_time);
idlers_ready = _ecore_idler_exist();
in_main_loop++;
INF("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_idle_exiter_call();
ecore_idling = 0;
}
else if (!ecore_idling && !events_ready)
{
ecore_idling = 1;
}
if (ecore_idling)
{
_ecore_idler_call();
events_ready = _ecore_event_exist();
if (ecore_fds_ready || events_ready || timers_ready)
{
_ecore_idle_exiter_call();
ecore_idling = 0;
}
}
/* process events */
if (!ecore_idling)
{
_ecore_main_fd_handlers_call();
if (fd_handlers_with_buffer)
_ecore_main_fd_handlers_buf_call();
2010-09-29 23:09:20 -07:00
while (_ecore_signal_count_get()) _ecore_signal_call();
_ecore_event_call();
2010-09-29 23:09:20 -07:00
_ecore_main_fd_handlers_cleanup();
}
in_main_loop--;
return TRUE; /* what should be returned here? */
}
static void
_ecore_main_gsource_finalize(GSource *source __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
void
_ecore_main_loop_init(void)
{
epoll_fd = epoll_create(1);
if (epoll_fd < 0)
WRN("Failed to create epoll fd!");
epoll_pid = getpid();
/* add polls on all our file descriptors */
Ecore_Fd_Handler *fdh;
EINA_INLIST_FOREACH(fd_handlers, fdh)
{
if (fdh->delete_me)
continue;
_ecore_epoll_add(epoll_fd, fdh->fd,
_ecore_poll_events_from_fdh(fdh), fdh);
_ecore_main_fdh_poll_add(fdh);
}
/* 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)
CRIT("Failed to create glib source for epoll!");
else
{
if (HAVE_EPOLL && epoll_fd >= 0)
{
/* epoll multiplexes fds into the g_main_loop */
ecore_epoll_fd.fd = epoll_fd;
ecore_epoll_fd.events = G_IO_IN;
ecore_epoll_fd.revents = 0;
g_source_add_poll(ecore_glib_source, &ecore_epoll_fd);
}
/* timerfd gives us better than millisecond accuracy in g_main_loop */
timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
if (timer_fd < 0)
WRN("failed to create timer fd!");
else
{
ecore_timer_fd.fd = 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)
CRIT("Failed to attach glib source to default context");
}
#endif
}
void
_ecore_main_loop_shutdown(void)
{
#ifdef USE_G_MAIN_LOOP
if (ecore_glib_source)
{
g_source_destroy(ecore_glib_source);
ecore_glib_source = NULL;
}
#endif
if (epoll_fd >= 0)
{
close(epoll_fd);
epoll_fd = -1;
}
epoll_pid = 0;
if (timer_fd >= 0)
{
close(timer_fd);
timer_fd = -1;
}
}
2010-10-17 00:03:28 -07:00
/**
* @addtogroup Ecore_Group Ecore - Main Loop and Job Functions.
*
* @{
*/
/**
2010-10-17 00:03:28 -07:00
* @addtogroup Ecore_Main_Loop_Group Ecore Main Loop functions
*
* These functions control the Ecore event handling loop. This loop is
* designed to work on embedded systems all the way to large and
* powerful mutli-cpu workstations.
*
* It serialises all system signals and events into a single event
* queue, that can be easily processed without needing to worry about
* concurrency. A properly written, event-driven program using this
* kind of programming does not need threads. It makes the program very
* robust and easy to follow.
*
* Here is an example of simple program and its basic event loop flow:
* @image html prog_flow.png
*
* For examples of setting up and using a main loop, see @ref
* Ecore_Main_Loop_Page.
2010-10-17 00:03:28 -07:00
*
* @{
*/
/**
* Runs a single iteration of the main loop to process everything on the
* queue.
*
* It does everything that is already done inside an @c Ecore main loop, like
* checking for expired timers, idlers, etc. But it will do it only once and
* return, instead of keep watching for new events.
*/
EAPI void
ecore_main_loop_iterate(void)
{
ECORE_MAIN_LOOP_ASSERT();
#ifndef USE_G_MAIN_LOOP
_ecore_main_loop_iterate_internal(1);
#else
g_main_context_iteration(NULL, 1);
#endif
}
/**
* Runs the application main loop.
*
* This function will not return until @ref ecore_main_loop_quit is called. It
* will check for expired timers, idlers, file descriptors being watched by fd
* handlers, etc. Once everything is done, before entering again on idle state,
* any callback set as @c Idle_Enterer will be called.
*
* Each main loop iteration is done by calling ecore_main_loop_iterate()
* internally.
*
* The polling (select) function used can be changed with
* ecore_main_loop_select_func_set().
*
* The function used to check for file descriptors, events, and that has a
* timeout for the timers can be changed using
* ecore_main_loop_select_func_set().
*/
EAPI void
ecore_main_loop_begin(void)
{
ECORE_MAIN_LOOP_ASSERT();
#ifndef USE_G_MAIN_LOOP
in_main_loop++;
while (do_quit == 0) _ecore_main_loop_iterate_internal(0);
do_quit = 0;
in_main_loop--;
#else
ecore_main_loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(ecore_main_loop);
#endif
}
/**
* Quits the main loop once all the events currently on the queue have
* been processed.
*
* This function returns immediately, but will mark the ecore_main_loop_begin()
* function to return at the end of the current main loop iteration.
*/
EAPI void
ecore_main_loop_quit(void)
{
#ifndef USE_G_MAIN_LOOP
do_quit = 1;
#else
g_main_loop_quit(ecore_main_loop);
#endif
}
/**
* Sets the function to use when monitoring multiple file descriptors,
* and waiting until one of more of the file descriptors before ready
* for some class of I/O operation.
*
* This function will be used instead of the system call select and
* could possible be used to integrate the Ecore event loop with an
* external event loop.
*
* @warning you don't know how to use, don't even try to use it.
*
*/
EAPI void
ecore_main_loop_select_func_set(Ecore_Select_Function func)
{
main_loop_select = func;
}
/**
* Gets the select function set by ecore_select_func_set(),
* or the native select function if none was set.
*
*/
EAPI Ecore_Select_Function
ecore_main_loop_select_func_get(void)
{
return main_loop_select;
}
/**
* @defgroup Ecore_FD_Handler_Group File Event Handling Functions
*
* Functions that deal with file descriptor handlers.
*
* The @ref Ecore_Fd_Handler can be used to watch a file descriptor for data
* available for reading, for the availability to write without blocking, and
* for errors on the file descriptor.
*
* ecore_main_fd_handler_add() is used to setup a handler for a given file
* descriptor. This file descriptor can be the standard input, a network socket,
* a stream received through some driver of a hardware decoder, etc. Thus it can
* contain errors, like a disconnection, a broken pipe, and so, and that's why
* it's possible to check for these errors with the @ref ECORE_FD_ERROR flag.
*
* An @ref Ecore_Fd_Handler can be used to watch on a file descriptor without
* blocking, still being able to receive events, expire timers, and other watch
* for other things that happen in the Ecore main loop.
*
* Example of use of a file descriptor handler:
* @li @ref ecore_fd_handler_example_c
*/
/**
* Adds a callback for activity on the given file descriptor.
*
* @p func will be called during the execution of @ref ecore_main_loop_begin
* when the file descriptor is available for reading, or writing, or both.
*
* Normally the return value from the @p func is "zero means this handler is
* finished and can be deleted" as is usual for handler callbacks. However,
* if the @p buf_func is supplied, then the return value from the @p func is
2008-01-25 10:22:51 -08:00
* "non zero means the handler should be called again in a tight loop".
*
* @p buf_func is called during event loop handling to check if data that has
* been read from the file descriptor is in a buffer and is available to
* read. Some systems (notably xlib) handle their own buffering, and would
* otherwise not work with select(). These systems should use a @p buf_func.
* This is a most annoying hack, only ecore_x uses it, so refer to that for
* an example. NOTE - @p func should probably return "one" always if
* @p buf_func is used, to avoid confusion with the other return value
2008-01-25 10:22:51 -08:00
* semantics.
*
* @param fd The file descriptor to watch.
* @param flags To watch it for read (@c ECORE_FD_READ) and/or
* (@c ECORE_FD_WRITE) write ability. @c ECORE_FD_ERROR
*
* @param func The callback function.
* @param data The data to pass to the callback.
* @param buf_func The function to call to check if any data has been
* buffered and already read from the fd. Can be @c NULL.
* @param buf_data The data to pass to the @p buf_func function.
* @return A fd handler handle if successful. @c NULL otherwise.
* @ingroup Ecore_FD_Handler_Group
*/
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;
ECORE_MAIN_LOOP_ASSERT();
if ((fd < 0) || (flags == 0) || (!func)) return NULL;
2011-01-07 00:18:19 -08:00
fdh = calloc(1, sizeof(Ecore_Fd_Handler));
if (!fdh) return NULL;
ECORE_MAGIC_SET(fdh, ECORE_MAGIC_FD_HANDLER);
fdh->next_ready = NULL;
fdh->fd = fd;
fdh->flags = flags;
if (_ecore_main_fdh_poll_add(fdh) < 0)
{
int err = errno;
ERR("Failed to add poll on fd %d (errno = %d: %s)!", fd, err, strerror(err));
2010-09-29 23:09:20 -07:00
free(fdh);
return NULL;
}
fdh->read_active = EINA_FALSE;
fdh->write_active = EINA_FALSE;
fdh->error_active = EINA_FALSE;
fdh->delete_me = EINA_FALSE;
fdh->func = func;
fdh->data = (void *)data;
fdh->buf_func = buf_func;
if (buf_func)
fd_handlers_with_buffer = eina_list_append(fd_handlers_with_buffer, fdh);
fdh->buf_data = (void *)buf_data;
fd_handlers = (Ecore_Fd_Handler *)
eina_inlist_append(EINA_INLIST_GET(fd_handlers),
EINA_INLIST_GET(fdh));
return fdh;
}
#ifdef _WIN32
EAPI Ecore_Win32_Handler *
ecore_main_win32_handler_add(void *h, Ecore_Win32_Handle_Cb func, const void *data)
{
Ecore_Win32_Handler *wh;
if (!h || !func) return NULL;
wh = calloc(1, sizeof(Ecore_Win32_Handler));
if (!wh) return NULL;
ECORE_MAGIC_SET(wh, ECORE_MAGIC_WIN32_HANDLER);
wh->h = (HANDLE)h;
wh->delete_me = EINA_FALSE;
wh->func = func;
wh->data = (void *)data;
win32_handlers = (Ecore_Win32_Handler *)
eina_inlist_append(EINA_INLIST_GET(win32_handlers),
EINA_INLIST_GET(wh));
return wh;
}
#else
EAPI Ecore_Win32_Handler *
ecore_main_win32_handler_add(void *h __UNUSED__, Ecore_Win32_Handle_Cb func __UNUSED__,
const void *data __UNUSED__)
{
return NULL;
}
#endif
/**
* Deletes the given FD handler.
* @param fd_handler The given FD handler.
* @return The data pointer set using @ref ecore_main_fd_handler_add,
* for @p fd_handler on success. @c NULL otherwise.
* @ingroup Ecore_FD_Handler_Group
*
2010-07-29 19:57:39 -07:00
* Beware that if the fd is already closed, ecore may complain if it uses
* epoll internally, and that in some rare cases this may be able to cause
* crashes and instability. Remember to delete your fd handlers before the
2010-07-29 19:57:39 -07:00
* fd's they listen to are closed.
*/
EAPI void *
ecore_main_fd_handler_del(Ecore_Fd_Handler *fd_handler)
{
ECORE_MAIN_LOOP_ASSERT();
if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER))
{
2010-09-29 23:09:20 -07:00
ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER,
"ecore_main_fd_handler_del");
return NULL;
}
if (fd_handler->delete_me)
{
ERR("fdh %p deleted twice", fd_handler);
return NULL;
}
2011-01-07 00:18:19 -08:00
_ecore_main_fdh_poll_del(fd_handler);
fd_handler->delete_me = EINA_TRUE;
fd_handlers_to_delete = eina_list_append(fd_handlers_to_delete, fd_handler);
if (fd_handler->prep_func && fd_handlers_with_prep)
fd_handlers_with_prep = eina_list_remove(fd_handlers_with_prep, fd_handler);
if (fd_handler->buf_func && fd_handlers_with_buffer)
fd_handlers_with_buffer = eina_list_remove(fd_handlers_with_buffer, fd_handler);
return fd_handler->data;
}
#ifdef _WIN32
EAPI void *
ecore_main_win32_handler_del(Ecore_Win32_Handler *win32_handler)
{
if (!ECORE_MAGIC_CHECK(win32_handler, ECORE_MAGIC_WIN32_HANDLER))
{
2010-09-29 23:09:20 -07:00
ECORE_MAGIC_FAIL(win32_handler, ECORE_MAGIC_WIN32_HANDLER,
"ecore_main_win32_handler_del");
return NULL;
}
win32_handler->delete_me = EINA_TRUE;
win32_handlers_delete_me = EINA_TRUE;
return win32_handler->data;
}
#else
EAPI void *
ecore_main_win32_handler_del(Ecore_Win32_Handler *win32_handler __UNUSED__)
{
return NULL;
}
#endif
/**
* @brief Set the prepare callback with data for a given #Ecore_Fd_Handler
*
* @param fd_handler The fd handler
* @param func The prep function
* @param data The data to pass to the prep function
*
* This function will be called prior to any fd handler's callback function
* (even the other fd handlers), before entering the main loop select function.
*
* @note Once a prepare callback is set for a fd handler, it cannot be changed.
* You need to delete the fd handler and create a new one, to set another
* callback.
* @note You probably don't need this function. It is only necessary for very
* uncommon cases that need special behavior.
*
* @ingroup Ecore_FD_Handler_Group
*/
EAPI void
ecore_main_fd_handler_prepare_callback_set(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Prep_Cb func, const void *data)
{
ECORE_MAIN_LOOP_ASSERT();
if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER))
{
2010-09-29 23:09:20 -07:00
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 ((!fd_handlers_with_prep) ||
(fd_handlers_with_prep && (!eina_list_data_find(fd_handlers_with_prep, fd_handler))))
/* FIXME: THIS WILL NOT SCALE WITH LOTS OF PREP FUNCTIONS!!! */
fd_handlers_with_prep = eina_list_append(fd_handlers_with_prep, fd_handler);
}
/**
* Retrieves the file descriptor that the given handler is handling.
* @param fd_handler The given FD handler.
* @return The file descriptor the handler is watching.
* @ingroup Ecore_FD_Handler_Group
*/
EAPI int
ecore_main_fd_handler_fd_get(Ecore_Fd_Handler *fd_handler)
{
if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER))
{
2010-09-29 23:09:20 -07:00
ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER,
"ecore_main_fd_handler_fd_get");
return -1;
}
return fd_handler->fd;
}
/**
* Return if read, write or error, or a combination thereof, is active on the
* file descriptor of the given FD handler.
* @param fd_handler The given FD handler.
* @param flags The flags, @c ECORE_FD_READ, @c ECORE_FD_WRITE or
* @c ECORE_FD_ERROR to query.
* @return #EINA_TRUE if any of the given flags are active. #EINA_FALSE otherwise.
* @ingroup Ecore_FD_Handler_Group
*/
EAPI Eina_Bool
ecore_main_fd_handler_active_get(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Handler_Flags flags)
{
int ret = EINA_FALSE;
ECORE_MAIN_LOOP_ASSERT();
if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER))
{
2010-09-29 23:09:20 -07:00
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;
}
/**
* Set what active streams the given FD handler should be monitoring.
* @param fd_handler The given FD handler.
* @param flags The flags to be watching.
* @ingroup Ecore_FD_Handler_Group
*/
EAPI void
ecore_main_fd_handler_active_set(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Handler_Flags flags)
{
int ret;
ECORE_MAIN_LOOP_ASSERT();
if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER))
{
2010-09-29 23:09:20 -07:00
ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER,
"ecore_main_fd_handler_active_set");
return;
}
fd_handler->flags = flags;
ret = _ecore_main_fdh_poll_modify(fd_handler);
if (ret < 0)
{
ERR("Failed to mod epoll fd %d: %s!", fd_handler->fd, strerror(ret));
}
}
2010-10-17 00:03:28 -07:00
/**
* @}
*/
/**
* @}
*/
void
_ecore_main_shutdown(void)
{
if (in_main_loop)
{
ERR("\n"
2010-09-29 23:09:20 -07:00
"*** ECORE WARINING: Calling ecore_shutdown() while still in the main loop.\n"
"*** Program may crash or behave strangely now.");
return;
}
while (fd_handlers)
{
2010-09-29 23:09:20 -07:00
Ecore_Fd_Handler *fdh;
2010-09-29 23:09:20 -07:00
fdh = fd_handlers;
fd_handlers = (Ecore_Fd_Handler *) eina_inlist_remove(EINA_INLIST_GET(fd_handlers),
EINA_INLIST_GET(fdh));
ECORE_MAGIC_SET(fdh, ECORE_MAGIC_NONE);
free(fdh);
}
if (fd_handlers_with_buffer)
fd_handlers_with_buffer = eina_list_free(fd_handlers_with_buffer);
if (fd_handlers_with_prep)
fd_handlers_with_prep = eina_list_free(fd_handlers_with_prep);
if (fd_handlers_to_delete)
fd_handlers_to_delete = eina_list_free(fd_handlers_to_delete);
2011-01-07 00:18:19 -08:00
fd_handlers_to_call = NULL;
fd_handlers_to_call_current = NULL;
fd_handlers_to_delete = NULL;
Fix fd_handlers when using recursive main loops. If an fd_handler created a recursive main loop (just called ecore_main_loop_begin()), then this recursive main loop should continue to process fd_handlers from there and on, thus fd_handler_current (and win32_handler_current) was added. When going back from recursion, the current iterator should be updated properly. This patch also fixes the deletion of fd_handler from recursive main loops by reference counting them. This way, the node will not be free()d inside inner loop cleanups and then crash when going back to outer loop. PS: win32 code is untested (or even compiled). The following test case used to crash but not anymore: #include <Ecore.h> #include <Eina.h> #include <unistd.h> static int _log_dom; #define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__) #define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__) static Ecore_Fd_Handler *handle; static int a[2], b[2]; static int cb2(void *data, Ecore_Fd_Handler *h) { INF("cb2 - delete cb1 handle"); ecore_main_fd_handler_del(handle); ecore_main_loop_quit(); /* quits inner main loop */ return 0; } static int cb1(void *data, Ecore_Fd_Handler *h) { unsigned char ch = 222; INF("cb1: begin"); INF(" add cb2"); ecore_main_fd_handler_add(b[0], ECORE_FD_READ, cb2, NULL, NULL, NULL); INF(" wake up pipe b"); if (write(b[1], &ch, 1) != 1) ERR("could not write to pipe b"); INF(" inner main loop begin (recurse)"); ecore_main_loop_begin(); /* will it crash due * ecore_main_fd_handler_del(handle) * inside cb2()? It used to! */ INF("cb1: end"); ecore_main_loop_quit(); /* quits outer main loop */ return 0; } int main(void) { unsigned char ch = 111; ecore_init(); _log_dom = eina_log_domain_register("test", EINA_COLOR_CYAN); pipe(a); pipe(b); /* * Creating a new main loop from inside an fd_handler callback, * and inside this new (inner) main loop deleting the caller * callback used to crash since the handle would be effectively * free()d, but when the recursion is over the pointer would be * used. */ INF("main: begin"); handle = ecore_main_fd_handler_add (a[0], ECORE_FD_READ, cb1, NULL, NULL, NULL); INF("main: wake up pipe a"); if (write(a[1], &ch, 1) != 1) ERR("could not write to pipe a"); ecore_main_loop_begin(); INF("main: end"); return 0; } SVN revision: 46443
2010-02-24 12:59:44 -08:00
fd_handler_current = NULL;
#ifdef _WIN32
while (win32_handlers)
{
2010-09-29 23:09:20 -07:00
Ecore_Win32_Handler *wh;
2010-09-29 23:09:20 -07:00
wh = win32_handlers;
win32_handlers = (Ecore_Win32_Handler *) eina_inlist_remove(EINA_INLIST_GET(win32_handlers),
EINA_INLIST_GET(wh));
ECORE_MAGIC_SET(wh, ECORE_MAGIC_NONE);
free(wh);
}
win32_handlers_delete_me = EINA_FALSE;
Fix fd_handlers when using recursive main loops. If an fd_handler created a recursive main loop (just called ecore_main_loop_begin()), then this recursive main loop should continue to process fd_handlers from there and on, thus fd_handler_current (and win32_handler_current) was added. When going back from recursion, the current iterator should be updated properly. This patch also fixes the deletion of fd_handler from recursive main loops by reference counting them. This way, the node will not be free()d inside inner loop cleanups and then crash when going back to outer loop. PS: win32 code is untested (or even compiled). The following test case used to crash but not anymore: #include <Ecore.h> #include <Eina.h> #include <unistd.h> static int _log_dom; #define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__) #define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__) static Ecore_Fd_Handler *handle; static int a[2], b[2]; static int cb2(void *data, Ecore_Fd_Handler *h) { INF("cb2 - delete cb1 handle"); ecore_main_fd_handler_del(handle); ecore_main_loop_quit(); /* quits inner main loop */ return 0; } static int cb1(void *data, Ecore_Fd_Handler *h) { unsigned char ch = 222; INF("cb1: begin"); INF(" add cb2"); ecore_main_fd_handler_add(b[0], ECORE_FD_READ, cb2, NULL, NULL, NULL); INF(" wake up pipe b"); if (write(b[1], &ch, 1) != 1) ERR("could not write to pipe b"); INF(" inner main loop begin (recurse)"); ecore_main_loop_begin(); /* will it crash due * ecore_main_fd_handler_del(handle) * inside cb2()? It used to! */ INF("cb1: end"); ecore_main_loop_quit(); /* quits outer main loop */ return 0; } int main(void) { unsigned char ch = 111; ecore_init(); _log_dom = eina_log_domain_register("test", EINA_COLOR_CYAN); pipe(a); pipe(b); /* * Creating a new main loop from inside an fd_handler callback, * and inside this new (inner) main loop deleting the caller * callback used to crash since the handle would be effectively * free()d, but when the recursion is over the pointer would be * used. */ INF("main: begin"); handle = ecore_main_fd_handler_add (a[0], ECORE_FD_READ, cb1, NULL, NULL, NULL); INF("main: wake up pipe a"); if (write(a[1], &ch, 1) != 1) ERR("could not write to pipe a"); ecore_main_loop_begin(); INF("main: end"); return 0; } SVN revision: 46443
2010-02-24 12:59:44 -08:00
win32_handler_current = NULL;
#endif
}
static void
_ecore_main_prepare_handlers(void)
{
Ecore_Fd_Handler *fdh;
Eina_List *l, *l2;
/* call the prepare callback for all handlers with prep functions */
EINA_LIST_FOREACH_SAFE(fd_handlers_with_prep, l, l2, fdh)
{
2010-12-01 02:31:20 -08:00
if (!fdh)
{
fd_handlers_with_prep = eina_list_remove_list(l, fd_handlers_with_prep);
continue;
}
if (!fdh->delete_me && fdh->prep_func)
{
fdh->references++;
fdh->prep_func(fdh->prep_data, fdh);
fdh->references--;
}
else
fd_handlers_with_prep = eina_list_remove_list(fd_handlers_with_prep, l);
}
}
#ifndef USE_G_MAIN_LOOP
static int
_ecore_main_select(double timeout)
{
struct timeval tv, *t;
fd_set rfds, wfds, exfds;
int max_fd;
int ret;
t = NULL;
if ((!ECORE_FINITE(timeout)) || (timeout == 0.0)) /* finite() tests for NaN, too big, too small, and infinity. */
{
2010-09-29 23:09:20 -07:00
tv.tv_sec = 0;
tv.tv_usec = 0;
t = &tv;
}
else if (timeout > 0.0)
{
2010-09-29 23:09:20 -07:00
int sec, usec;
#ifdef FIX_HZ
2010-09-29 23:09:20 -07:00
timeout += (0.5 / HZ);
sec = (int)timeout;
usec = (int)((timeout - (double)sec) * 1000000);
#else
2010-09-29 23:09:20 -07:00
sec = (int)timeout;
usec = (int)((timeout - (double)sec) * 1000000);
#endif
2010-09-29 23:09:20 -07:00
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 (fd_handlers_with_prep)
_ecore_main_prepare_handlers();
if (!HAVE_EPOLL || epoll_fd < 0)
{
Ecore_Fd_Handler *fdh;
EINA_INLIST_FOREACH(fd_handlers, fdh)
{
if (!fdh->delete_me)
{
if (fdh->flags & ECORE_FD_READ)
{
FD_SET(fdh->fd, &rfds);
if (fdh->fd > max_fd) max_fd = fdh->fd;
}
if (fdh->flags & ECORE_FD_WRITE)
{
FD_SET(fdh->fd, &wfds);
if (fdh->fd > max_fd) max_fd = fdh->fd;
}
if (fdh->flags & ECORE_FD_ERROR)
{
FD_SET(fdh->fd, &exfds);
if (fdh->fd > max_fd) max_fd = fdh->fd;
}
}
}
}
else
{
/* polling on the epoll fd will wake when an fd in the epoll set is active */
max_fd = _ecore_get_epoll_fd();
FD_SET(max_fd, &rfds);
}
if (_ecore_signal_count_get()) return -1;
ret = main_loop_select(max_fd + 1, &rfds, &wfds, &exfds, t);
_ecore_time_loop_time = ecore_time_get();
if (ret < 0)
{
#ifndef _WIN32
2010-09-29 23:09:20 -07:00
if (errno == EINTR) return -1;
else if (errno == EBADF) _ecore_main_fd_handlers_bads_rem();
#endif
}
if (ret > 0)
{
if (HAVE_EPOLL && epoll_fd >= 0)
_ecore_main_fdh_epoll_mark_active();
else
{
Ecore_Fd_Handler *fdh;
EINA_INLIST_FOREACH(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(fdh);
}
}
}
2010-09-29 23:09:20 -07:00
_ecore_main_fd_handlers_cleanup();
#ifdef _WIN32
2010-09-29 23:09:20 -07:00
_ecore_main_win32_handlers_cleanup();
#endif
2010-09-29 23:09:20 -07:00
return 1;
}
return 0;
}
#endif
#ifndef _WIN32
# ifndef USE_G_MAIN_LOOP
static void
_ecore_main_fd_handlers_bads_rem(void)
{
Ecore_Fd_Handler *fdh;
Eina_Inlist *l;
int found = 0;
2010-04-26 21:30:55 -07:00
ERR("Removing bad fds");
for (l = EINA_INLIST_GET(fd_handlers); l; )
{
2010-09-29 23:09:20 -07:00
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 (!fdh->func(fdh->data, fdh))
{
ERR("Fd function err returned 0, remove it");
if (!fdh->delete_me)
{
fdh->delete_me = EINA_TRUE;
fd_handlers_to_delete = eina_list_append(fd_handlers_to_delete, fdh);
}
found++;
2010-09-29 23:09:20 -07:00
}
fdh->references--;
}
else
{
ERR("Problematic fd found at %d! setting it for delete", fdh->fd);
if (!fdh->delete_me)
{
fdh->delete_me = EINA_TRUE;
fd_handlers_to_delete = eina_list_append(fd_handlers_to_delete, fdh);
}
found++;
2010-09-29 23:09:20 -07:00
}
}
}
if (found == 0)
{
# ifdef HAVE_GLIB
2010-04-27 17:05:56 -07:00
ERR("No bad fd found. Maybe a foreign fd from glib?");
# else
2010-04-27 17:05:56 -07:00
ERR("No bad fd found. EEEK!");
# endif
}
_ecore_main_fd_handlers_cleanup();
}
# endif
#endif
static void
_ecore_main_fd_handlers_cleanup(void)
{
Ecore_Fd_Handler *fdh;
Eina_List *l, *l2;
if (!fd_handlers_to_delete) return;
EINA_LIST_FOREACH_SAFE(fd_handlers_to_delete, l, l2, fdh)
{
2010-12-01 02:31:20 -08:00
if (!fdh)
{
fd_handlers_to_delete = eina_list_remove_list(l, fd_handlers_to_delete);
continue;
}
/* fdh->delete_me should be set for all fdhs at the start of the list */
if (fdh->references)
continue;
if (fdh->buf_func && fd_handlers_with_buffer)
fd_handlers_with_buffer = eina_list_remove(fd_handlers_with_buffer, fdh);
if (fdh->prep_func && fd_handlers_with_prep)
fd_handlers_with_prep = eina_list_remove(fd_handlers_with_prep, fdh);
fd_handlers = (Ecore_Fd_Handler *)
eina_inlist_remove(EINA_INLIST_GET(fd_handlers), EINA_INLIST_GET(fdh));
ECORE_MAGIC_SET(fdh, ECORE_MAGIC_NONE);
free(fdh);
fd_handlers_to_delete = eina_list_remove_list(fd_handlers_to_delete, l);
}
}
#ifdef _WIN32
static void
_ecore_main_win32_handlers_cleanup(void)
{
Ecore_Win32_Handler *wh;
Eina_Inlist *l;
Fix fd_handlers when using recursive main loops. If an fd_handler created a recursive main loop (just called ecore_main_loop_begin()), then this recursive main loop should continue to process fd_handlers from there and on, thus fd_handler_current (and win32_handler_current) was added. When going back from recursion, the current iterator should be updated properly. This patch also fixes the deletion of fd_handler from recursive main loops by reference counting them. This way, the node will not be free()d inside inner loop cleanups and then crash when going back to outer loop. PS: win32 code is untested (or even compiled). The following test case used to crash but not anymore: #include <Ecore.h> #include <Eina.h> #include <unistd.h> static int _log_dom; #define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__) #define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__) static Ecore_Fd_Handler *handle; static int a[2], b[2]; static int cb2(void *data, Ecore_Fd_Handler *h) { INF("cb2 - delete cb1 handle"); ecore_main_fd_handler_del(handle); ecore_main_loop_quit(); /* quits inner main loop */ return 0; } static int cb1(void *data, Ecore_Fd_Handler *h) { unsigned char ch = 222; INF("cb1: begin"); INF(" add cb2"); ecore_main_fd_handler_add(b[0], ECORE_FD_READ, cb2, NULL, NULL, NULL); INF(" wake up pipe b"); if (write(b[1], &ch, 1) != 1) ERR("could not write to pipe b"); INF(" inner main loop begin (recurse)"); ecore_main_loop_begin(); /* will it crash due * ecore_main_fd_handler_del(handle) * inside cb2()? It used to! */ INF("cb1: end"); ecore_main_loop_quit(); /* quits outer main loop */ return 0; } int main(void) { unsigned char ch = 111; ecore_init(); _log_dom = eina_log_domain_register("test", EINA_COLOR_CYAN); pipe(a); pipe(b); /* * Creating a new main loop from inside an fd_handler callback, * and inside this new (inner) main loop deleting the caller * callback used to crash since the handle would be effectively * free()d, but when the recursion is over the pointer would be * used. */ INF("main: begin"); handle = ecore_main_fd_handler_add (a[0], ECORE_FD_READ, cb1, NULL, NULL, NULL); INF("main: wake up pipe a"); if (write(a[1], &ch, 1) != 1) ERR("could not write to pipe a"); ecore_main_loop_begin(); INF("main: end"); return 0; } SVN revision: 46443
2010-02-24 12:59:44 -08:00
int deleted_in_use = 0;
if (!win32_handlers_delete_me) return;
for (l = EINA_INLIST_GET(win32_handlers); l; )
{
wh = (Ecore_Win32_Handler *)l;
l = l->next;
if (wh->delete_me)
{
2010-09-29 23:09:20 -07:00
if (wh->references)
{
deleted_in_use++;
continue;
}
2011-01-07 00:18:19 -08:00
win32_handlers = (Ecore_Win32_Handler *)
eina_inlist_remove(EINA_INLIST_GET(win32_handlers),
EINA_INLIST_GET(wh));
ECORE_MAGIC_SET(wh, ECORE_MAGIC_NONE);
free(wh);
}
}
if (!deleted_in_use) win32_handlers_delete_me = EINA_FALSE;
}
#endif
static void
_ecore_main_fd_handlers_call(void)
{
/* grab a new list */
if (!fd_handlers_to_call_current)
Fix fd_handlers when using recursive main loops. If an fd_handler created a recursive main loop (just called ecore_main_loop_begin()), then this recursive main loop should continue to process fd_handlers from there and on, thus fd_handler_current (and win32_handler_current) was added. When going back from recursion, the current iterator should be updated properly. This patch also fixes the deletion of fd_handler from recursive main loops by reference counting them. This way, the node will not be free()d inside inner loop cleanups and then crash when going back to outer loop. PS: win32 code is untested (or even compiled). The following test case used to crash but not anymore: #include <Ecore.h> #include <Eina.h> #include <unistd.h> static int _log_dom; #define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__) #define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__) static Ecore_Fd_Handler *handle; static int a[2], b[2]; static int cb2(void *data, Ecore_Fd_Handler *h) { INF("cb2 - delete cb1 handle"); ecore_main_fd_handler_del(handle); ecore_main_loop_quit(); /* quits inner main loop */ return 0; } static int cb1(void *data, Ecore_Fd_Handler *h) { unsigned char ch = 222; INF("cb1: begin"); INF(" add cb2"); ecore_main_fd_handler_add(b[0], ECORE_FD_READ, cb2, NULL, NULL, NULL); INF(" wake up pipe b"); if (write(b[1], &ch, 1) != 1) ERR("could not write to pipe b"); INF(" inner main loop begin (recurse)"); ecore_main_loop_begin(); /* will it crash due * ecore_main_fd_handler_del(handle) * inside cb2()? It used to! */ INF("cb1: end"); ecore_main_loop_quit(); /* quits outer main loop */ return 0; } int main(void) { unsigned char ch = 111; ecore_init(); _log_dom = eina_log_domain_register("test", EINA_COLOR_CYAN); pipe(a); pipe(b); /* * Creating a new main loop from inside an fd_handler callback, * and inside this new (inner) main loop deleting the caller * callback used to crash since the handle would be effectively * free()d, but when the recursion is over the pointer would be * used. */ INF("main: begin"); handle = ecore_main_fd_handler_add (a[0], ECORE_FD_READ, cb1, NULL, NULL, NULL); INF("main: wake up pipe a"); if (write(a[1], &ch, 1) != 1) ERR("could not write to pipe a"); ecore_main_loop_begin(); INF("main: end"); return 0; } SVN revision: 46443
2010-02-24 12:59:44 -08:00
{
fd_handlers_to_call_current = fd_handlers_to_call;
fd_handlers_to_call = NULL;
Fix fd_handlers when using recursive main loops. If an fd_handler created a recursive main loop (just called ecore_main_loop_begin()), then this recursive main loop should continue to process fd_handlers from there and on, thus fd_handler_current (and win32_handler_current) was added. When going back from recursion, the current iterator should be updated properly. This patch also fixes the deletion of fd_handler from recursive main loops by reference counting them. This way, the node will not be free()d inside inner loop cleanups and then crash when going back to outer loop. PS: win32 code is untested (or even compiled). The following test case used to crash but not anymore: #include <Ecore.h> #include <Eina.h> #include <unistd.h> static int _log_dom; #define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__) #define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__) static Ecore_Fd_Handler *handle; static int a[2], b[2]; static int cb2(void *data, Ecore_Fd_Handler *h) { INF("cb2 - delete cb1 handle"); ecore_main_fd_handler_del(handle); ecore_main_loop_quit(); /* quits inner main loop */ return 0; } static int cb1(void *data, Ecore_Fd_Handler *h) { unsigned char ch = 222; INF("cb1: begin"); INF(" add cb2"); ecore_main_fd_handler_add(b[0], ECORE_FD_READ, cb2, NULL, NULL, NULL); INF(" wake up pipe b"); if (write(b[1], &ch, 1) != 1) ERR("could not write to pipe b"); INF(" inner main loop begin (recurse)"); ecore_main_loop_begin(); /* will it crash due * ecore_main_fd_handler_del(handle) * inside cb2()? It used to! */ INF("cb1: end"); ecore_main_loop_quit(); /* quits outer main loop */ return 0; } int main(void) { unsigned char ch = 111; ecore_init(); _log_dom = eina_log_domain_register("test", EINA_COLOR_CYAN); pipe(a); pipe(b); /* * Creating a new main loop from inside an fd_handler callback, * and inside this new (inner) main loop deleting the caller * callback used to crash since the handle would be effectively * free()d, but when the recursion is over the pointer would be * used. */ INF("main: begin"); handle = ecore_main_fd_handler_add (a[0], ECORE_FD_READ, cb1, NULL, NULL, NULL); INF("main: wake up pipe a"); if (write(a[1], &ch, 1) != 1) ERR("could not write to pipe a"); ecore_main_loop_begin(); INF("main: end"); return 0; } SVN revision: 46443
2010-02-24 12:59:44 -08:00
}
while (fd_handlers_to_call_current)
Fix fd_handlers when using recursive main loops. If an fd_handler created a recursive main loop (just called ecore_main_loop_begin()), then this recursive main loop should continue to process fd_handlers from there and on, thus fd_handler_current (and win32_handler_current) was added. When going back from recursion, the current iterator should be updated properly. This patch also fixes the deletion of fd_handler from recursive main loops by reference counting them. This way, the node will not be free()d inside inner loop cleanups and then crash when going back to outer loop. PS: win32 code is untested (or even compiled). The following test case used to crash but not anymore: #include <Ecore.h> #include <Eina.h> #include <unistd.h> static int _log_dom; #define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__) #define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__) static Ecore_Fd_Handler *handle; static int a[2], b[2]; static int cb2(void *data, Ecore_Fd_Handler *h) { INF("cb2 - delete cb1 handle"); ecore_main_fd_handler_del(handle); ecore_main_loop_quit(); /* quits inner main loop */ return 0; } static int cb1(void *data, Ecore_Fd_Handler *h) { unsigned char ch = 222; INF("cb1: begin"); INF(" add cb2"); ecore_main_fd_handler_add(b[0], ECORE_FD_READ, cb2, NULL, NULL, NULL); INF(" wake up pipe b"); if (write(b[1], &ch, 1) != 1) ERR("could not write to pipe b"); INF(" inner main loop begin (recurse)"); ecore_main_loop_begin(); /* will it crash due * ecore_main_fd_handler_del(handle) * inside cb2()? It used to! */ INF("cb1: end"); ecore_main_loop_quit(); /* quits outer main loop */ return 0; } int main(void) { unsigned char ch = 111; ecore_init(); _log_dom = eina_log_domain_register("test", EINA_COLOR_CYAN); pipe(a); pipe(b); /* * Creating a new main loop from inside an fd_handler callback, * and inside this new (inner) main loop deleting the caller * callback used to crash since the handle would be effectively * free()d, but when the recursion is over the pointer would be * used. */ INF("main: begin"); handle = ecore_main_fd_handler_add (a[0], ECORE_FD_READ, cb1, NULL, NULL, NULL); INF("main: wake up pipe a"); if (write(a[1], &ch, 1) != 1) ERR("could not write to pipe a"); ecore_main_loop_begin(); INF("main: end"); return 0; } SVN revision: 46443
2010-02-24 12:59:44 -08:00
{
Ecore_Fd_Handler *fdh = fd_handlers_to_call_current;
2010-09-29 23:09:20 -07:00
if (!fdh->delete_me)
{
if ((fdh->read_active) ||
(fdh->write_active) ||
(fdh->error_active))
{
fdh->references++;
if (!fdh->func(fdh->data, fdh))
{
if (!fdh->delete_me)
{
fdh->delete_me = EINA_TRUE;
fd_handlers_to_delete = eina_list_append(fd_handlers_to_delete, fdh);
}
2010-09-29 23:09:20 -07:00
}
fdh->references--;
_ecore_fd_valid();
2010-09-29 23:09:20 -07:00
fdh->read_active = EINA_FALSE;
fdh->write_active = EINA_FALSE;
fdh->error_active = EINA_FALSE;
2010-09-29 23:09:20 -07:00
}
}
/* stop when we point to ourselves */
if (fdh->next_ready == fdh)
{
fdh->next_ready = NULL;
fd_handlers_to_call_current = NULL;
break;
}
fd_handlers_to_call_current = fdh->next_ready;
fdh->next_ready = NULL;
Fix fd_handlers when using recursive main loops. If an fd_handler created a recursive main loop (just called ecore_main_loop_begin()), then this recursive main loop should continue to process fd_handlers from there and on, thus fd_handler_current (and win32_handler_current) was added. When going back from recursion, the current iterator should be updated properly. This patch also fixes the deletion of fd_handler from recursive main loops by reference counting them. This way, the node will not be free()d inside inner loop cleanups and then crash when going back to outer loop. PS: win32 code is untested (or even compiled). The following test case used to crash but not anymore: #include <Ecore.h> #include <Eina.h> #include <unistd.h> static int _log_dom; #define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__) #define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__) static Ecore_Fd_Handler *handle; static int a[2], b[2]; static int cb2(void *data, Ecore_Fd_Handler *h) { INF("cb2 - delete cb1 handle"); ecore_main_fd_handler_del(handle); ecore_main_loop_quit(); /* quits inner main loop */ return 0; } static int cb1(void *data, Ecore_Fd_Handler *h) { unsigned char ch = 222; INF("cb1: begin"); INF(" add cb2"); ecore_main_fd_handler_add(b[0], ECORE_FD_READ, cb2, NULL, NULL, NULL); INF(" wake up pipe b"); if (write(b[1], &ch, 1) != 1) ERR("could not write to pipe b"); INF(" inner main loop begin (recurse)"); ecore_main_loop_begin(); /* will it crash due * ecore_main_fd_handler_del(handle) * inside cb2()? It used to! */ INF("cb1: end"); ecore_main_loop_quit(); /* quits outer main loop */ return 0; } int main(void) { unsigned char ch = 111; ecore_init(); _log_dom = eina_log_domain_register("test", EINA_COLOR_CYAN); pipe(a); pipe(b); /* * Creating a new main loop from inside an fd_handler callback, * and inside this new (inner) main loop deleting the caller * callback used to crash since the handle would be effectively * free()d, but when the recursion is over the pointer would be * used. */ INF("main: begin"); handle = ecore_main_fd_handler_add (a[0], ECORE_FD_READ, cb1, NULL, NULL, NULL); INF("main: wake up pipe a"); if (write(a[1], &ch, 1) != 1) ERR("could not write to pipe a"); ecore_main_loop_begin(); INF("main: end"); return 0; } SVN revision: 46443
2010-02-24 12:59:44 -08:00
}
}
static int
_ecore_main_fd_handlers_buf_call(void)
{
Ecore_Fd_Handler *fdh;
Eina_List *l, *l2;
int ret;
ret = 0;
EINA_LIST_FOREACH_SAFE(fd_handlers_with_buffer, l, l2, fdh)
{
2010-12-01 02:31:20 -08:00
if (!fdh)
{
fd_handlers_with_buffer = eina_list_remove_list(l, fd_handlers_with_buffer);
continue;
}
if ((!fdh->delete_me) && fdh->buf_func)
{
fdh->references++;
if (fdh->buf_func(fdh->buf_data, fdh))
{
ret |= fdh->func(fdh->data, fdh);
fdh->read_active = EINA_TRUE;
_ecore_try_add_to_call_list(fdh);
}
fdh->references--;
}
else
fd_handlers_with_buffer = eina_list_remove_list(fd_handlers_with_buffer, l);
}
return ret;
}
#ifndef USE_G_MAIN_LOOP
static void
_ecore_main_loop_iterate_internal(int once_only)
{
double next_time = -1.0;
int have_event = 0;
int have_signal;
in_main_loop++;
/* expire any timers */
while (_ecore_timer_call(_ecore_time_loop_time));
_ecore_timer_cleanup();
/* process signals into events .... */
while (_ecore_signal_count_get()) _ecore_signal_call();
if (_ecore_event_exist())
{
_ecore_idle_enterer_call();
2011-06-28 05:34:52 -07:00
_ecore_throttle();
2010-09-29 23:09:20 -07:00
have_event = 1;
_ecore_main_select(0.0);
_ecore_timer_enable_new();
2010-09-29 23:09:20 -07:00
goto process_events;
}
/* call idle enterers ... */
2011-06-28 05:34:52 -07:00
if (!once_only)
{
_ecore_idle_enterer_call();
_ecore_throttle();
}
else
{
2010-09-29 23:09:20 -07:00
have_event = have_signal = 0;
2010-09-29 23:09:20 -07:00
if (_ecore_main_select(0.0) > 0) have_event = 1;
if (_ecore_signal_count_get() > 0) have_signal = 1;
if (have_signal || have_event)
{
_ecore_timer_enable_new();
goto process_events;
}
}
/* if these calls caused any buffered events to appear - deal with them */
if (fd_handlers_with_buffer)
_ecore_main_fd_handlers_buf_call();
/* if there are any - jump to processing them */
if (_ecore_event_exist())
{
2010-09-29 23:09:20 -07:00
have_event = 1;
_ecore_main_select(0.0);
_ecore_timer_enable_new();
2010-09-29 23:09:20 -07:00
goto process_events;
}
if (once_only)
{
2010-09-29 23:09:20 -07:00
_ecore_idle_enterer_call();
2011-06-28 05:34:52 -07:00
_ecore_throttle();
2010-09-29 23:09:20 -07:00
in_main_loop--;
_ecore_timer_enable_new();
2010-09-29 23:09:20 -07:00
return;
}
if (_ecore_fps_debug)
{
2010-09-29 23:09:20 -07:00
t2 = ecore_time_get();
if ((t1 > 0.0) && (t2 > 0.0))
_ecore_fps_debug_runtime_add(t2 - t1);
}
start_loop:
/* any timers re-added as a result of these are allowed to go */
_ecore_timer_enable_new();
if (do_quit)
{
2010-09-29 23:09:20 -07:00
in_main_loop--;
_ecore_timer_enable_new();
2010-09-29 23:09:20 -07:00
return;
}
if (!_ecore_event_exist())
{
2010-09-29 23:09:20 -07:00
/* init flags */
have_event = have_signal = 0;
next_time = _ecore_timer_next_get();
/* no timers */
if (next_time < 0)
{
/* no idlers */
if (!_ecore_idler_exist())
{
if (_ecore_main_select(-1.0) > 0) have_event = 1;
}
/* idlers */
else
{
for (;;)
{
_ecore_time_loop_time = ecore_time_get();
2010-09-29 23:09:20 -07:00
if (!_ecore_idler_call()) goto start_loop;
if (_ecore_main_select(0.0) > 0) break;
if (_ecore_event_exist()) break;
if (_ecore_signal_count_get() > 0) break;
2010-09-29 23:09:20 -07:00
if (_ecore_timers_exists()) goto start_loop;
if (do_quit) break;
}
}
}
/* timers */
else
{
/* no idlers */
if (!_ecore_idler_exist())
{
if (_ecore_main_select(next_time) > 0) have_event = 1;
}
/* idlers */
else
{
for (;;)
{
_ecore_time_loop_time = ecore_time_get();
2010-09-29 23:09:20 -07:00
if (!_ecore_idler_call()) goto start_loop;
if (_ecore_main_select(0.0) > 0) break;
if (_ecore_event_exist()) break;
if (_ecore_signal_count_get() > 0) break;
2010-09-29 23:09:20 -07:00
if (have_event || have_signal) break;
next_time = _ecore_timer_next_get();
if (next_time <= 0) break;
if (do_quit) break;
}
}
}
}
if (_ecore_fps_debug) t1 = ecore_time_get();
/* we came out of our "wait state" so idle has exited */
if (!once_only) _ecore_idle_exiter_call();
/* 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 */
process_events:
_ecore_main_fd_handlers_call();
if (fd_handlers_with_buffer)
_ecore_main_fd_handlers_buf_call();
/* process signals into events .... */
while (_ecore_signal_count_get()) _ecore_signal_call();
/* handle events ... */
_ecore_event_call();
_ecore_main_fd_handlers_cleanup();
2011-06-28 05:34:52 -07:00
if (once_only)
{
_ecore_idle_enterer_call();
_ecore_throttle();
}
in_main_loop--;
}
#endif
#ifdef _WIN32
static int
_ecore_main_win32_select(int nfds __UNUSED__, fd_set *readfds, fd_set *writefds,
2010-09-29 23:09:20 -07:00
fd_set *exceptfds, struct timeval *tv)
{
HANDLE objects[MAXIMUM_WAIT_OBJECTS];
int sockets[MAXIMUM_WAIT_OBJECTS];
Ecore_Fd_Handler *fdh;
Ecore_Win32_Handler *wh;
unsigned int objects_nbr = 0;
unsigned int handles_nbr = 0;
unsigned int events_nbr = 0;
DWORD result;
DWORD timeout;
MSG msg;
unsigned int i;
int res;
/* Create an event object per socket */
EINA_INLIST_FOREACH(fd_handlers, fdh)
{
WSAEVENT event;
long network_event;
network_event = 0;
if (FD_ISSET(fdh->fd, readfds))
2010-09-29 23:09:20 -07:00
network_event |= FD_READ;
if (FD_ISSET(fdh->fd, writefds))
2010-09-29 23:09:20 -07:00
network_event |= FD_WRITE;
if (FD_ISSET(fdh->fd, exceptfds))
2010-09-29 23:09:20 -07:00
network_event |= FD_OOB;
if (network_event)
2010-09-29 23:09:20 -07:00
{
event = WSACreateEvent();
2010-09-29 23:09:20 -07:00
WSAEventSelect(fdh->fd, event, network_event);
objects[objects_nbr] = event;
sockets[events_nbr] = fdh->fd;
events_nbr++;
objects_nbr++;
}
}
/* store the HANDLEs in the objects to wait for */
EINA_INLIST_FOREACH(win32_handlers, wh)
{
objects[objects_nbr] = wh->h;
handles_nbr++;
objects_nbr++;
}
/* Empty the queue before waiting */
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
2010-09-29 23:09:20 -07:00
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) return 0;
result = MsgWaitForMultipleObjects(objects_nbr, (const HANDLE *)objects, EINA_FALSE,
2010-09-29 23:09:20 -07:00
timeout, QS_ALLINPUT);
FD_ZERO(readfds);
FD_ZERO(writefds);
FD_ZERO(exceptfds);
/* The result tells us the type of event we have. */
if (result == WAIT_FAILED)
{
char *msg;
msg = evil_last_error_get();
ERR(" * %s\n", msg);
free(msg);
res = -1;
}
else if (result == WAIT_TIMEOUT)
{
/* ERR("time out\n"); */
res = 0;
}
else if (result == (WAIT_OBJECT_0 + objects_nbr))
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
2010-09-29 23:09:20 -07:00
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
res = 0;
}
else if ((result >= 0) && (result < WAIT_OBJECT_0 + events_nbr))
{
WSANETWORKEVENTS network_event;
WSAEnumNetworkEvents(sockets[result], objects[result], &network_event);
2011-01-07 00:18:19 -08:00
if (network_event.lNetworkEvents & FD_READ)
FD_SET(sockets[result], readfds);
if (network_event.lNetworkEvents & FD_WRITE)
FD_SET(sockets[result], writefds);
if (network_event.lNetworkEvents & FD_OOB)
FD_SET(sockets[result], exceptfds);
2011-01-07 00:18:19 -08:00
res = 1;
}
2011-01-07 00:18:19 -08:00
else if ((result >= (WAIT_OBJECT_0 + events_nbr)) &&
(result < (WAIT_OBJECT_0 + objects_nbr)))
{
2010-09-29 23:09:20 -07:00
if (!win32_handler_current)
{
/* regular main loop, start from head */
win32_handler_current = win32_handlers;
}
else
{
/* recursive main loop, continue from where we were */
win32_handler_current = (Ecore_Win32_Handler *)EINA_INLIST_GET(win32_handler_current)->next;
}
while (win32_handler_current)
{
wh = win32_handler_current;
2011-01-07 00:18:19 -08:00
if (objects[result - WAIT_OBJECT_0] == wh->h)
{
if (!wh->delete_me)
{
wh->references++;
if (!wh->func(wh->data, wh))
{
wh->delete_me = EINA_TRUE;
win32_handlers_delete_me = EINA_TRUE;
}
wh->references--;
}
}
if (win32_handler_current) /* may have changed in recursive main loops */
win32_handler_current = (Ecore_Win32_Handler *)EINA_INLIST_GET(win32_handler_current)->next;
}
res = 1;
}
else
{
ERR("unknown result...\n");
res = -1;
}
/* Remove event objects again */
for (i = 0; i < events_nbr; i++) WSACloseEvent(objects[i]);
return res;
}
#endif