efl/src/lib/ecore/efl_loop.c

700 lines
20 KiB
C

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#define EFL_LOOP_PROTECTED
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <sys/time.h>
#include <errno.h>
#include "Ecore.h"
#include "ecore_private.h"
#include "ecore_main_common.h"
typedef struct _Efl_Loop_Promise_Simple_Data Efl_Loop_Promise_Simple_Data;
typedef struct _Efl_Internal_Promise Efl_Internal_Promise;
struct _Efl_Loop_Promise_Simple_Data
{
union {
Efl_Loop_Timer *timer;
Ecore_Idler *idler;
};
Eina_Promise *promise;
};
GENERIC_ALLOC_SIZE_DECLARE(Efl_Loop_Promise_Simple_Data);
Eo *_mainloop_singleton = NULL;
Efl_Loop_Data *_mainloop_singleton_data = NULL;
EAPI Eo *
efl_main_loop_get(void)
{
return efl_app_main_get();
}
EOLIAN static void
_efl_loop_iterate(Eo *obj, Efl_Loop_Data *pd)
{
_ecore_main_loop_iterate(obj, pd);
}
EOLIAN static int
_efl_loop_iterate_may_block(Eo *obj, Efl_Loop_Data *pd, int may_block)
{
return _ecore_main_loop_iterate_may_block(obj, pd, may_block);
}
EOLIAN static Eina_Value *
_efl_loop_begin(Eo *obj, Efl_Loop_Data *pd)
{
_ecore_main_loop_begin(obj, pd);
if (pd->thread_children)
{
Eina_List *l, *ll;
Eo *child;
// request all child threads to die and defer the quit until
// the children have all died and returned.
// run main loop again to clean out children and their exits
pd->quit_on_last_thread_child_del = EINA_TRUE;
EINA_LIST_FOREACH_SAFE(pd->thread_children, l, ll, child)
{
Efl_Task_Flags task_flags = efl_task_flags_get(child);
if (task_flags & EFL_TASK_FLAGS_EXIT_WITH_PARENT)
efl_task_end(child);
else
_efl_thread_child_remove(obj, pd, child);
}
if (pd->thread_children) _ecore_main_loop_begin(obj, pd);
}
return &(pd->exit_code);
}
EOLIAN static void
_efl_loop_quit(Eo *obj, Efl_Loop_Data *pd, Eina_Value exit_code)
{
_ecore_main_loop_quit(obj, pd);
pd->exit_code = exit_code;
}
EOLIAN static void
_efl_loop_time_set(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd, double t)
{
pd->loop_time = t;
}
EOLIAN static double
_efl_loop_time_get(const Eo *obj EINA_UNUSED, Efl_Loop_Data *pd)
{
return pd->loop_time;
}
EAPI void
efl_exit(int exit_code)
{
Eina_Value v = EINA_VALUE_EMPTY;
eina_value_setup(&v, EINA_VALUE_TYPE_INT);
eina_value_set(&v, exit_code);
efl_loop_quit(efl_main_loop_get(), v);
}
EAPI int
efl_loop_exit_code_process(Eina_Value *value)
{
Eina_Value def = EINA_VALUE_EMPTY;
const Eina_Value_Type *t;
int r = 0;
if (value == NULL ||
!value->type)
{
def = eina_value_int_init(0);
value = &def;
}
t = eina_value_type_get(value);
if (t == EINA_VALUE_TYPE_UCHAR ||
t == EINA_VALUE_TYPE_USHORT ||
t == EINA_VALUE_TYPE_UINT ||
t == EINA_VALUE_TYPE_ULONG ||
t == EINA_VALUE_TYPE_UINT64 ||
t == EINA_VALUE_TYPE_CHAR ||
t == EINA_VALUE_TYPE_SHORT ||
t == EINA_VALUE_TYPE_INT ||
t == EINA_VALUE_TYPE_LONG ||
t == EINA_VALUE_TYPE_INT64 ||
t == EINA_VALUE_TYPE_FLOAT ||
t == EINA_VALUE_TYPE_DOUBLE)
{
Eina_Value v = EINA_VALUE_EMPTY;
eina_value_setup(&v, EINA_VALUE_TYPE_INT);
if (!eina_value_convert(value, &v)) r = -1;
else eina_value_get(&v, &r);
}
else
{
FILE *out = stdout;
char *msg;
msg = eina_value_to_string(value);
if (t == EINA_VALUE_TYPE_ERROR)
{
r = -1;
out = stderr;
}
fprintf(out, "%s\n", msg);
free(msg);
}
return r;
}
static void
_poll_trigger(void *data, const Efl_Event *event)
{
Eo *parent = efl_parent_get(event->object);
efl_event_callback_call(parent, data, NULL);
}
static void
_check_event_catcher_add(void *data, const Efl_Event *event)
{
const Efl_Callback_Array_Item_Full *array = event->info;
Efl_Loop_Data *pd = data;
int i;
for (i = 0; array[i].desc != NULL; i++)
{
if (array[i].desc == EFL_LOOP_EVENT_IDLE)
{
++pd->idlers;
}
// XXX: all the below are kind of bad. ecore_pollers were special.
// they all woke up at the SAME time based on interval, (all pollers
// of interval 1 woke up together, those with 2 woke up when 1 and
// 2 woke up, 4 woke up together along with 1 and 2 etc.
// the below means they will just go off whenever but at a pre
// defined interval - 1/60th, 6 and 66 seconds. not really great
// pollers probably should be less frequent that 1/60th even on poll
// high, medium probably down to 1-2 sec and low - yes maybe 30 or 60
// sec... still - not timed to wake up together. :(
else if (array[i].desc == EFL_LOOP_EVENT_POLL_HIGH)
{
if (!pd->poll_high)
{
// Would be better to have it in sync with normal wake up
// of the main loop for better energy efficiency, I guess.
pd->poll_high = efl_add
(EFL_LOOP_TIMER_CLASS, event->object,
efl_event_callback_add(efl_added,
EFL_LOOP_TIMER_EVENT_TIMER_TICK,
_poll_trigger,
EFL_LOOP_EVENT_POLL_HIGH),
efl_loop_timer_interval_set(efl_added, 1.0 / 60.0));
}
++pd->pollers.high;
}
else if (array[i].desc == EFL_LOOP_EVENT_POLL_MEDIUM)
{
if (!pd->poll_medium)
{
pd->poll_medium = efl_add
(EFL_LOOP_TIMER_CLASS, event->object,
efl_event_callback_add(efl_added,
EFL_LOOP_TIMER_EVENT_TIMER_TICK,
_poll_trigger,
EFL_LOOP_EVENT_POLL_MEDIUM),
efl_loop_timer_interval_set(efl_added, 6));
}
++pd->pollers.medium;
}
else if (array[i].desc == EFL_LOOP_EVENT_POLL_LOW)
{
if (!pd->poll_low)
{
pd->poll_low = efl_add
(EFL_LOOP_TIMER_CLASS, event->object,
efl_event_callback_add(efl_added,
EFL_LOOP_TIMER_EVENT_TIMER_TICK,
_poll_trigger,
EFL_LOOP_EVENT_POLL_LOW),
efl_loop_timer_interval_set(efl_added, 66));
}
++pd->pollers.low;
}
}
}
static void
_check_event_catcher_del(void *data, const Efl_Event *event)
{
const Efl_Callback_Array_Item_Full *array = event->info;
Efl_Loop_Data *pd = data;
int i;
for (i = 0; array[i].desc != NULL; i++)
{
if (array[i].desc == EFL_LOOP_EVENT_IDLE)
{
--pd->idlers;
}
else if (array[i].desc == EFL_LOOP_EVENT_POLL_HIGH)
{
--pd->pollers.high;
if (!pd->pollers.high)
{
efl_del(pd->poll_high);
pd->poll_high = NULL;
}
}
else if (array[i].desc == EFL_LOOP_EVENT_POLL_MEDIUM)
{
--pd->pollers.medium;
if (!pd->pollers.medium)
{
efl_del(pd->poll_medium);
pd->poll_medium = NULL;
}
}
else if (array[i].desc == EFL_LOOP_EVENT_POLL_LOW)
{
--pd->pollers.low;
if (!pd->pollers.low)
{
efl_del(pd->poll_low);
pd->poll_low = NULL;
}
}
}
}
EFL_CALLBACKS_ARRAY_DEFINE(event_catcher_watch,
{ EFL_EVENT_CALLBACK_ADD, _check_event_catcher_add },
{ EFL_EVENT_CALLBACK_DEL, _check_event_catcher_del });
EOLIAN static Efl_Object *
_efl_loop_efl_object_constructor(Eo *obj, Efl_Loop_Data *pd)
{
obj = efl_constructor(efl_super(obj, EFL_LOOP_CLASS));
if (!obj) return NULL;
efl_event_callback_array_add(obj, event_catcher_watch(), pd);
pd->loop_time = ecore_time_get();
pd->epoll_fd = -1;
pd->timer_fd = -1;
pd->future_message_handler = efl_add(EFL_LOOP_MESSAGE_FUTURE_HANDLER_CLASS, obj);
efl_provider_register(obj, EFL_LOOP_MESSAGE_FUTURE_HANDLER_CLASS, pd->future_message_handler);
return obj;
}
EOLIAN static void
_efl_loop_efl_object_invalidate(Eo *obj, Efl_Loop_Data *pd)
{
efl_invalidate(efl_super(obj, EFL_LOOP_CLASS));
_ecore_main_content_clear(obj, pd);
pd->poll_low = NULL;
pd->poll_medium = NULL;
pd->poll_high = NULL;
// After invalidate, it won't be possible to parent to the singleton anymore
if (obj == _mainloop_singleton)
{
_mainloop_singleton = NULL;
_mainloop_singleton_data = NULL;
}
}
EOLIAN static void
_efl_loop_efl_object_destructor(Eo *obj, Efl_Loop_Data *pd)
{
pd->future_message_handler = NULL;
while (pd->thread_children)
_efl_thread_child_remove(obj, pd, pd->thread_children->data);
efl_destructor(efl_super(obj, EFL_LOOP_CLASS));
}
static Eina_Value
_efl_loop_arguments_send(Eo *o EINA_UNUSED, void *data, const Eina_Value v)
{
static Eina_Bool initialization = EINA_TRUE;
Efl_Loop_Arguments arge;
Eina_Array *arga = data;
arge.argv = arga;
arge.initialization = initialization;
initialization = EINA_FALSE;
efl_event_callback_call(efl_main_loop_get(),
EFL_LOOP_EVENT_ARGUMENTS, &arge);
return v;
}
static void
_efl_loop_arguments_cleanup(Eo *o EINA_UNUSED, void *data, const Eina_Future *dead_future EINA_UNUSED)
{
Eina_Array *arga = data;
Eina_Stringshare *s;
while ((s = eina_array_pop(arga))) eina_stringshare_del(s);
eina_array_free(arga);
}
// It doesn't make sense to send those argument to any other mainloop
// As it also doesn't make sense to allow anyone to override this, so
// should be internal for sure, not even protected.
EAPI void
ecore_loop_arguments_send(int argc, const char **argv)
{
Eina_Array *arga, *cml;
int i = 0;
arga = eina_array_new(argc);
cml = eina_array_new(argc);
for (i = 0; i < argc; i++)
{
Eina_Stringshare *arg;
arg = eina_stringshare_add(argv[i]);
eina_array_push(arga, arg);
arg = eina_stringshare_add(argv[i]);
eina_array_push(cml, arg);
}
efl_core_command_line_command_array_set(efl_app_main_get(), cml);
efl_future_then(efl_main_loop_get(), efl_loop_job(efl_main_loop_get()),
.success = _efl_loop_arguments_send,
.free = _efl_loop_arguments_cleanup,
.data = arga);
}
static Eina_Future *
_efl_loop_job(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED)
{
// NOTE: Eolian should do efl_future_then() to bind future to object.
return efl_future_then(obj,
eina_future_resolved(efl_loop_future_scheduler_get(obj), EINA_VALUE_EMPTY));
}
EOLIAN static void
_efl_loop_throttle_set(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd, double amount)
{
pd->throttle = ((double)amount) * 1000000.0;
}
EOLIAN static double
_efl_loop_throttle_get(const Eo *obj EINA_UNUSED, Efl_Loop_Data *pd)
{
return (double)(pd->throttle) / 1000000.0;
}
static void
_efl_loop_idle_cancel(void *data, const Eina_Promise *dead_ptr EINA_UNUSED)
{
Efl_Loop_Promise_Simple_Data *d = data;
ecore_idler_del(d->idler);
d->idler = NULL;
d->promise = NULL;
efl_loop_promise_simple_data_mp_free(d);
}
static Eina_Bool
_efl_loop_idle_done(void *data)
{
Efl_Loop_Promise_Simple_Data *d = data;
eina_promise_resolve(d->promise, EINA_VALUE_EMPTY);
d->idler = NULL;
d->promise = NULL;
efl_loop_promise_simple_data_mp_free(d);
return EINA_FALSE;
}
static Eina_Future *
_efl_loop_idle(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED)
{
Efl_Loop_Promise_Simple_Data *d;
Eina_Promise *p;
Eina_Future_Scheduler *sched = efl_loop_future_scheduler_get(obj);
d = efl_loop_promise_simple_data_calloc(1);
EINA_SAFETY_ON_NULL_RETURN_VAL(d, NULL);
d->idler = ecore_idler_add(_efl_loop_idle_done, d);
EINA_SAFETY_ON_NULL_GOTO(d->idler, idler_error);
p = eina_promise_new(sched, _efl_loop_idle_cancel, d);
// d is dead if p is NULL
EINA_SAFETY_ON_NULL_RETURN_VAL(p, NULL);
d->promise = p;
// NOTE: Eolian should do efl_future_then() to bind future to object.
return efl_future_then(obj, eina_future_new(p));
idler_error:
d->idler = NULL;
d->promise = NULL;
efl_loop_promise_simple_data_mp_free(d);
return NULL;
}
static void
_efl_loop_timeout_cancel(void *data, const Eina_Promise *dead_ptr EINA_UNUSED)
{
Efl_Loop_Promise_Simple_Data *d = data;
if (d->timer)
efl_del(d->timer);
}
static void
_efl_loop_timeout_done(void *data, const Efl_Event *event)
{
Efl_Loop_Promise_Simple_Data *d = data;
eina_promise_resolve(d->promise, EINA_VALUE_EMPTY);
d->timer = NULL;
efl_del(event->object);
}
static void
_efl_loop_timeout_del(void *data, const Efl_Event *event EINA_UNUSED)
{
Efl_Loop_Promise_Simple_Data *d = data;
d->timer = NULL;
d->promise = NULL;
efl_loop_promise_simple_data_mp_free(d);
}
static Eina_Future *
_efl_loop_timeout(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED, double tim)
{
Efl_Loop_Promise_Simple_Data *d;
Eina_Promise *p;
Eina_Future_Scheduler *sched = efl_loop_future_scheduler_get(obj);
d = efl_loop_promise_simple_data_calloc(1);
EINA_SAFETY_ON_NULL_RETURN_VAL(d, NULL);
d->timer = efl_add(EFL_LOOP_TIMER_CLASS, obj,
efl_loop_timer_interval_set(efl_added, tim),
efl_event_callback_add(efl_added,
EFL_LOOP_TIMER_EVENT_TIMER_TICK,
_efl_loop_timeout_done, d),
efl_event_callback_add(efl_added,
EFL_EVENT_DEL,
_efl_loop_timeout_del, d)
);
EINA_SAFETY_ON_NULL_GOTO(d->timer, timer_error);
p = eina_promise_new(sched, _efl_loop_timeout_cancel, d);
// d is dead if p is NULL
EINA_SAFETY_ON_NULL_RETURN_VAL(p, NULL);
d->promise = p;
// NOTE: Eolian should do efl_future_then() to bind future to object.
return efl_future_then(obj, eina_future_new(p));
timer_error:
d->timer = NULL;
d->promise = NULL;
efl_loop_promise_simple_data_mp_free(d);
return NULL;
}
static Eina_Bool
_efl_loop_register(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED,
const Efl_Class *klass, const Efl_Object *provider)
{
return efl_provider_register(obj, klass, provider);
}
EFL_FUNC_BODYV(efl_loop_register, Eina_Bool, EINA_FALSE, EFL_FUNC_CALL(klass, provider), const Efl_Class *klass, const Efl_Object *provider);
static Eina_Bool
_efl_loop_unregister(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED,
const Efl_Class *klass, const Efl_Object *provider)
{
return efl_provider_unregister(obj, klass, provider);
}
EFL_FUNC_BODYV(efl_loop_unregister, Eina_Bool, EINA_FALSE, EFL_FUNC_CALL(klass, provider), const Efl_Class *klass, const Efl_Object *provider);
void
_efl_loop_messages_filter(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd, void *handler_pd)
{
Message *msg;
pd->message_walking++;
EINA_INLIST_FOREACH(pd->message_queue, msg)
{
if ((msg->handler) && (msg->message) && (!msg->delete_me))
{
if (!_ecore_event_do_filter(handler_pd,
msg->handler, msg->message))
{
efl_del(msg->message);
msg->handler = NULL;
msg->message = NULL;
msg->delete_me = EINA_TRUE;
}
}
}
pd->message_walking--;
}
void
_efl_loop_messages_call(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd, void *func, void *data)
{
Message *msg;
pd->message_walking++;
EINA_INLIST_FOREACH(pd->message_queue, msg)
{
if ((msg->handler) && (msg->message) && (!msg->delete_me))
{
Eina_Bool (*fn) (void *data, void *handler, void *msg);
fn = func;
if (!fn(data, msg->handler, msg->message))
{
efl_del(msg->message);
msg->handler = NULL;
msg->message = NULL;
msg->delete_me = EINA_TRUE;
}
}
}
pd->message_walking--;
}
EOLIAN static Eina_Bool
_efl_loop_message_process(Eo *obj, Efl_Loop_Data *pd)
{
if (!pd->message_queue) return EINA_FALSE;
pd->message_walking++;
_ecore_event_filters_call(obj, pd);
while (pd->message_queue)
{
Message *msg = (Message *)pd->message_queue;
if (!msg->delete_me)
efl_loop_message_handler_message_call(msg->handler, msg->message);
else
{
if (msg->message) efl_del(msg->message);
pd->message_queue =
eina_inlist_remove(pd->message_queue,
pd->message_queue);
free(msg);
}
}
pd->message_walking--;
if (pd->message_walking == 0)
{
Message *msg;
EINA_INLIST_FREE(pd->message_queue, msg)
{
if (msg->message)
{
if (!msg->delete_me)
ERR("Found unprocessed event msg=%p handler=%p on queue",
msg->message, msg->handler);
efl_del(msg->message);
}
else free(msg);
}
while (pd->message_pending_queue)
{
msg = (Message *)pd->message_pending_queue;
pd->message_pending_queue = eina_inlist_remove(pd->message_pending_queue,
pd->message_pending_queue);
pd->message_queue = eina_inlist_append(pd->message_queue, EINA_INLIST_GET(msg));
}
}
return EINA_TRUE;
}
EOAPI EFL_FUNC_BODY(efl_loop_message_process, Eina_Bool, 0);
EWAPI void
efl_build_version_set(int vmaj, int vmin, int vmic, int revision,
const char *flavor, const char *build_id)
{
// note: EFL has not been initialized yet at this point (ie. no eina call)
_app_efl_version.major = vmaj;
_app_efl_version.minor = vmin;
_app_efl_version.micro = vmic;
_app_efl_version.revision = revision;
free((char *)_app_efl_version.flavor);
free((char *)_app_efl_version.build_id);
_app_efl_version.flavor = flavor ? strdup(flavor) : NULL;
_app_efl_version.build_id = build_id ? strdup(build_id) : NULL;
}
EOLIAN static Eina_Bool
_efl_loop_efl_task_run(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED)
{
efl_loop_exit_code_process(efl_loop_begin(obj));
return EINA_TRUE;
}
EOLIAN static void
_efl_loop_efl_task_end(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED)
{
efl_loop_quit(obj, eina_value_int_init(0));
}
EFL_SCHEDULER_ARRAY_DEFINE(loop_scheduler,
EFL_LOOP_EVENT_IDLE_ENTER,
EFL_LOOP_EVENT_IDLE);
EAPI Eina_Future_Scheduler *
efl_loop_future_scheduler_get(const Eo *obj)
{
Efl_Loop *loop;
if (!obj) return NULL;
if (efl_isa(obj, EFL_LOOP_CLASS))
{
Efl_Loop_Data *pd = efl_data_scope_get(obj, EFL_LOOP_CLASS);
if (!pd) return NULL;
return efl_event_future_scheduler_get(obj, loop_scheduler());
}
if (efl_isa(obj, EFL_LOOP_CONSUMER_CLASS))
return efl_loop_future_scheduler_get(efl_loop_get(obj));
loop = efl_provider_find(obj, EFL_LOOP_CLASS);
if (loop)
return efl_loop_future_scheduler_get(loop);
return NULL;
}
#define EFL_LOOP_EXTRA_OPS \
EFL_OBJECT_OP_FUNC(efl_loop_message_process, _efl_loop_message_process), \
EFL_OBJECT_OP_FUNC(efl_loop_register, _efl_loop_register), \
EFL_OBJECT_OP_FUNC(efl_loop_unregister, _efl_loop_unregister)
#include "efl_loop.eo.c"