forked from enlightenment/efl
1124 lines
28 KiB
C
1124 lines
28 KiB
C
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <locale.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
|
|
#ifdef HAVE_LANGINFO_H
|
|
# include <langinfo.h>
|
|
#endif
|
|
|
|
#if defined(HAVE_SYS_MMAN_H) || defined(_WIN32)
|
|
# include <sys/mman.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYSTEMD
|
|
# include <systemd/sd-daemon.h>
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
# include <Evil.h>
|
|
#endif
|
|
#include <Eina.h>
|
|
#include <Efl.h>
|
|
|
|
#include "Ecore.h"
|
|
#include "Efl_Core.h"
|
|
#include "ecore_private.h"
|
|
|
|
#if defined(HAVE_MALLINFO) || defined(HAVE_MALLOC_INFO)
|
|
#include <malloc.h>
|
|
#endif
|
|
|
|
#ifndef O_BINARY
|
|
# define O_BINARY 0
|
|
#endif
|
|
|
|
static Ecore_Version _version = { VMAJ, VMIN, VMIC, VREV };
|
|
EAPI Ecore_Version *ecore_version = &_version;
|
|
|
|
EAPI double _efl_startup_time = 0;
|
|
|
|
#if defined(HAVE_MALLINFO) || defined(HAVE_MALLOC_INFO)
|
|
#define KEEP_MAX(Global, Local) \
|
|
if (Global < (Local)) \
|
|
Global = Local;
|
|
|
|
static Eina_Bool _ecore_memory_statistic(void *data);
|
|
static int _ecore_memory_max_total = 0;
|
|
static int _ecore_memory_max_free = 0;
|
|
static pid_t _ecore_memory_pid = 0;
|
|
#ifdef HAVE_MALLOC_INFO
|
|
static FILE *_ecore_memory_statistic_file = NULL;
|
|
#endif
|
|
#endif
|
|
|
|
static Eina_Bool _no_system_modules = 0xff;
|
|
|
|
static const char *_ecore_magic_string_get(Ecore_Magic m);
|
|
static int _ecore_init_count = 0;
|
|
static int _ecore_init_count_threshold = 0;
|
|
int _ecore_log_dom = -1;
|
|
int _ecore_fps_debug = 0;
|
|
|
|
typedef struct _Ecore_Safe_Call Ecore_Safe_Call;
|
|
struct _Ecore_Safe_Call
|
|
{
|
|
union {
|
|
Ecore_Cb async;
|
|
Ecore_Data_Cb sync;
|
|
} cb;
|
|
void *data;
|
|
|
|
Eina_Lock m;
|
|
Eina_Condition c;
|
|
|
|
Efl_Domain_Data *eo_domain_data;
|
|
int current_id;
|
|
|
|
Eina_Bool sync : 1;
|
|
Eina_Bool suspend : 1;
|
|
};
|
|
|
|
static void _ecore_main_loop_thread_safe_call(Ecore_Safe_Call *order);
|
|
static void _thread_safe_cleanup(void *data);
|
|
static void _thread_callback(void *data,
|
|
void *buffer,
|
|
unsigned int nbyte);
|
|
static Eina_List *_thread_cb = NULL;
|
|
static Ecore_Pipe *_thread_call = NULL;
|
|
static Eina_Lock _thread_safety;
|
|
static const int wakeup = 42;
|
|
|
|
static int _thread_loop = 0;
|
|
static Eina_Lock _thread_mutex;
|
|
static Eina_Condition _thread_cond;
|
|
static Eina_Lock _thread_feedback_mutex;
|
|
static Eina_Condition _thread_feedback_cond;
|
|
|
|
static Eina_Lock _thread_id_lock;
|
|
static int _thread_id = -1;
|
|
static int _thread_id_max = 0;
|
|
static int _thread_id_update = 0;
|
|
|
|
static Ecore_Power_State _ecore_power_state = ECORE_POWER_STATE_MAINS;
|
|
static Ecore_Memory_State _ecore_memory_state = ECORE_MEMORY_STATE_NORMAL;
|
|
|
|
#ifdef HAVE_SYSTEMD
|
|
static void _systemd_watchdog_cb(void *data, const Efl_Event *event);
|
|
|
|
static Efl_Loop_Timer *_systemd_watchdog = NULL;
|
|
#endif
|
|
|
|
static Efl_Vpath *vpath = NULL;
|
|
|
|
Eina_Lock _ecore_main_loop_lock;
|
|
int _ecore_main_lock_count;
|
|
|
|
/* OpenBSD does not define CODESET
|
|
* FIXME ??
|
|
*/
|
|
|
|
#ifndef CODESET
|
|
# define CODESET "INVALID"
|
|
#endif
|
|
|
|
static Eina_Prefix *_ecore_pfx = NULL;
|
|
static Eina_Array *module_list = NULL;
|
|
|
|
static void
|
|
ecore_system_modules_load(void)
|
|
{
|
|
char buf[PATH_MAX] = "";
|
|
|
|
#ifdef NEED_RUN_IN_TREE
|
|
#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
|
|
if (getuid() == geteuid())
|
|
#endif
|
|
{
|
|
if (getenv("EFL_RUN_IN_TREE"))
|
|
{
|
|
struct stat st;
|
|
snprintf(buf, sizeof(buf), "%s/src/modules/ecore/system",
|
|
PACKAGE_BUILD_DIR);
|
|
if (stat(buf, &st) == 0)
|
|
{
|
|
const char *built_modules[] = {
|
|
#ifdef HAVE_SYSTEMD
|
|
"systemd",
|
|
#endif
|
|
#ifdef HAVE_TIZEN_CONFIGURATION_MANAGER
|
|
"tizen",
|
|
#endif
|
|
NULL
|
|
};
|
|
const char **itr;
|
|
for (itr = built_modules; *itr != NULL; itr++)
|
|
{
|
|
snprintf(buf, sizeof(buf),
|
|
"%s/src/modules/ecore/system/%s/.libs",
|
|
PACKAGE_BUILD_DIR, *itr);
|
|
module_list = eina_module_list_get(module_list, buf,
|
|
EINA_FALSE, NULL, NULL);
|
|
}
|
|
|
|
if (module_list)
|
|
eina_module_list_load(module_list);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
snprintf(buf, sizeof(buf), "%s/ecore/system",
|
|
eina_prefix_lib_get(_ecore_pfx));
|
|
module_list = eina_module_arch_list_get(module_list, buf, MODULE_ARCH);
|
|
|
|
// XXX: MODFIX: do not list ALL modules and load them ALL! this is
|
|
// just polluting memory pages and I/O with modules and code that
|
|
// is then never used. detetc the module we need to use them use
|
|
// that. if anything load each module, have it do a detect and if
|
|
// it fails UNLOAD and try the next one.
|
|
eina_module_list_load(module_list);
|
|
}
|
|
|
|
static void
|
|
ecore_system_modules_unload(void)
|
|
{
|
|
if (module_list)
|
|
{
|
|
eina_module_list_free(module_list);
|
|
eina_array_free(module_list);
|
|
module_list = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
_efl_first_loop_iterate(void *data, const Efl_Event *event)
|
|
{
|
|
double end = ecore_time_unix_get();
|
|
char *first = data;
|
|
|
|
switch (*first)
|
|
{
|
|
case 'A': abort();
|
|
case 'E':
|
|
case 'D': exit(-1);
|
|
case 'T': fprintf(stderr, "Loop started: '%f' - '%f' = '%f' sec\n", end, _efl_startup_time, end - _efl_startup_time);
|
|
break;
|
|
}
|
|
|
|
efl_event_callback_del(event->object, EFL_LOOP_EVENT_RESUME,
|
|
_efl_first_loop_iterate, data);
|
|
}
|
|
|
|
EAPI void
|
|
ecore_app_no_system_modules(void)
|
|
{
|
|
_no_system_modules = EINA_TRUE;
|
|
}
|
|
|
|
EAPI int
|
|
ecore_init(void)
|
|
{
|
|
if (++_ecore_init_count != 1)
|
|
return _ecore_init_count;
|
|
|
|
/* make sure libecore is linked to libefl - workaround gcc bug */
|
|
__efl_internal_init();
|
|
|
|
setlocale(LC_CTYPE, "");
|
|
/*
|
|
if (strcmp(nl_langinfo(CODESET), "UTF-8"))
|
|
{
|
|
WRN("Not a utf8 locale!");
|
|
}
|
|
*/
|
|
#ifdef _WIN32
|
|
if (!evil_init())
|
|
return --_ecore_init_count;
|
|
#endif
|
|
if (!eina_init())
|
|
goto shutdown_evil;
|
|
eina_evlog(">RUN", NULL, 0.0, NULL);
|
|
_ecore_log_dom = eina_log_domain_register("ecore", ECORE_DEFAULT_LOG_COLOR);
|
|
if (_ecore_log_dom < 0)
|
|
{
|
|
EINA_LOG_ERR("Ecore was unable to create a log domain.");
|
|
goto shutdown_log_dom;
|
|
}
|
|
_ecore_animator_init();
|
|
|
|
_ecore_pfx = eina_prefix_new(NULL, ecore_init,
|
|
"ECORE", "ecore", "checkme",
|
|
PACKAGE_BIN_DIR, PACKAGE_LIB_DIR,
|
|
PACKAGE_DATA_DIR, PACKAGE_DATA_DIR);
|
|
if (!_ecore_pfx)
|
|
{
|
|
ERR("Could not get ecore installation prefix");
|
|
goto shutdown_log_dom;
|
|
}
|
|
|
|
efl_object_init();
|
|
|
|
if (getenv("ECORE_FPS_DEBUG")) _ecore_fps_debug = 1;
|
|
if (_ecore_fps_debug) _ecore_fps_debug_init();
|
|
if (!ecore_mempool_init()) goto shutdown_mempool;
|
|
_ecore_main_loop_init();
|
|
if (!_ecore_event_init()) goto shutdown_event;
|
|
|
|
vpath = efl_add(EFL_VPATH_CORE_CLASS, NULL);
|
|
if (vpath) efl_vpath_manager_register(EFL_VPATH_MANAGER_CLASS, 0, vpath);
|
|
|
|
_ecore_signal_init();
|
|
#ifndef HAVE_EXOTIC
|
|
_ecore_exe_init();
|
|
#endif
|
|
_ecore_thread_init();
|
|
_ecore_job_init();
|
|
_ecore_time_init();
|
|
|
|
eina_lock_new(&_thread_mutex);
|
|
eina_condition_new(&_thread_cond, &_thread_mutex);
|
|
eina_lock_new(&_thread_feedback_mutex);
|
|
eina_condition_new(&_thread_feedback_cond, &_thread_feedback_mutex);
|
|
_thread_call = _ecore_pipe_add(_thread_callback, NULL);
|
|
eina_lock_new(&_thread_safety);
|
|
|
|
eina_lock_new(&_thread_id_lock);
|
|
|
|
eina_lock_new(&_ecore_main_loop_lock);
|
|
|
|
#if defined(GLIB_INTEGRATION_ALWAYS)
|
|
if (_ecore_glib_always_integrate) ecore_main_loop_glib_integrate();
|
|
#endif
|
|
|
|
#if defined(HAVE_MALLINFO) || defined(HAVE_MALLOC_INFO)
|
|
if (getenv("ECORE_MEM_STAT"))
|
|
{
|
|
#ifdef HAVE_MALLOC_INFO
|
|
char tmp[1024];
|
|
|
|
snprintf(tmp, sizeof(tmp), "ecore_mem_stat.%i", getpid());
|
|
_ecore_memory_statistic_file = fopen(tmp, "wb");
|
|
#endif
|
|
_ecore_memory_pid = getpid();
|
|
ecore_animator_add(_ecore_memory_statistic, NULL);
|
|
_ecore_memory_statistic(NULL);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_SYSTEMD
|
|
if (getenv("WATCHDOG_USEC"))
|
|
{
|
|
double sec;
|
|
|
|
sec = ((double) atoi(getenv("WATCHDOG_USEC"))) / 1000 / 1000;
|
|
|
|
_systemd_watchdog =
|
|
efl_add(EFL_LOOP_TIMER_CLASS, efl_main_loop_get(),
|
|
efl_loop_timer_interval_set(efl_added, sec / 2),
|
|
efl_event_callback_add(efl_added,
|
|
EFL_LOOP_TIMER_EVENT_TICK,
|
|
_systemd_watchdog_cb, NULL));
|
|
unsetenv("WATCHDOG_USEC");
|
|
|
|
INF("Setup systemd watchdog to : %f", sec);
|
|
|
|
_systemd_watchdog_cb(NULL, NULL);
|
|
}
|
|
#endif
|
|
|
|
if (_no_system_modules == 0xff)
|
|
{
|
|
const char *s = getenv("ECORE_NO_SYSTEM_MODULES");
|
|
if (s) _no_system_modules = atoi(s);
|
|
else _no_system_modules = EINA_FALSE;
|
|
}
|
|
|
|
if (!_no_system_modules)
|
|
ecore_system_modules_load();
|
|
|
|
if (getenv("EFL_FIRST_LOOP"))
|
|
efl_event_callback_add(efl_main_loop_get(),
|
|
EFL_LOOP_EVENT_RESUME,
|
|
_efl_first_loop_iterate,
|
|
getenv("EFL_FIRST_LOOP"));
|
|
|
|
_ecore_init_count_threshold = _ecore_init_count;
|
|
|
|
eina_log_timing(_ecore_log_dom,
|
|
EINA_LOG_STATE_STOP,
|
|
EINA_LOG_STATE_INIT);
|
|
|
|
return _ecore_init_count;
|
|
|
|
shutdown_event:
|
|
_ecore_event_shutdown();
|
|
_ecore_main_shutdown();
|
|
shutdown_mempool:
|
|
ecore_mempool_shutdown();
|
|
efl_object_shutdown();
|
|
shutdown_log_dom:
|
|
eina_shutdown();
|
|
shutdown_evil:
|
|
#ifdef _WIN32
|
|
evil_shutdown();
|
|
#endif
|
|
|
|
return --_ecore_init_count;
|
|
}
|
|
|
|
EAPI int
|
|
ecore_shutdown(void)
|
|
{
|
|
Ecore_Pipe *p;
|
|
/*
|
|
* take a lock here because _ecore_event_shutdown() does callbacks
|
|
*/
|
|
if (_ecore_init_count <= 0)
|
|
{
|
|
ERR("Init count not greater than 0 in shutdown.");
|
|
return 0;
|
|
}
|
|
if (_ecore_init_count-- != _ecore_init_count_threshold)
|
|
goto end;
|
|
|
|
efl_event_callback_call(efl_main_loop_get(), EFL_LOOP_EVENT_TERMINATE, NULL);
|
|
|
|
ecore_system_modules_unload();
|
|
|
|
eina_log_timing(_ecore_log_dom,
|
|
EINA_LOG_STATE_START,
|
|
EINA_LOG_STATE_SHUTDOWN);
|
|
|
|
#ifdef HAVE_SYSTEMD
|
|
if (_systemd_watchdog)
|
|
{
|
|
efl_del(_systemd_watchdog);
|
|
_systemd_watchdog = NULL;
|
|
}
|
|
#endif
|
|
|
|
if (_ecore_fps_debug) _ecore_fps_debug_shutdown();
|
|
_ecore_poller_shutdown();
|
|
_ecore_animator_shutdown();
|
|
_ecore_glib_shutdown();
|
|
_ecore_job_shutdown();
|
|
_ecore_thread_shutdown();
|
|
|
|
/* this looks horrible - a hack for now, but something to note. as
|
|
* we delete the _thread_call pipe a thread COULD be doing
|
|
* ecore_pipe_write() or what not to it at the same time - we
|
|
* must ensure all possible users of this _thread_call are finished
|
|
* and exited before we delete it here */
|
|
/*
|
|
* ok - this causes other valgrind complaints regarding glib aquiring
|
|
* locks internally. so fix bug a or bug b. let's leave the original
|
|
* bug in then and leave this as a note for now
|
|
*/
|
|
/*
|
|
* It should be fine now as we do wait for thread to shutdown before
|
|
* we try to destroy the pipe.
|
|
*/
|
|
p = _thread_call;
|
|
_thread_call = NULL;
|
|
_ecore_pipe_wait(p, 1, 0.1);
|
|
_ecore_pipe_del(p);
|
|
eina_lock_free(&_thread_safety);
|
|
eina_condition_free(&_thread_cond);
|
|
eina_lock_free(&_thread_mutex);
|
|
eina_condition_free(&_thread_feedback_cond);
|
|
eina_lock_free(&_thread_feedback_mutex);
|
|
eina_lock_free(&_thread_id_lock);
|
|
|
|
|
|
#ifndef HAVE_EXOTIC
|
|
_ecore_exe_shutdown();
|
|
#endif
|
|
_ecore_event_shutdown();
|
|
_ecore_main_shutdown();
|
|
_ecore_signal_shutdown();
|
|
|
|
if (vpath)
|
|
{
|
|
efl_del(vpath);
|
|
vpath = NULL;
|
|
}
|
|
|
|
_ecore_main_loop_shutdown();
|
|
|
|
#if defined(HAVE_MALLINFO) || defined(HAVE_MALLOC_INFO)
|
|
if (getenv("ECORE_MEM_STAT"))
|
|
{
|
|
_ecore_memory_statistic(NULL);
|
|
|
|
ERR("[%i] Memory MAX total: %i, free: %i",
|
|
_ecore_memory_pid,
|
|
_ecore_memory_max_total,
|
|
_ecore_memory_max_free);
|
|
|
|
|
|
#ifdef HAVE_MALLOC_INFO
|
|
fclose(_ecore_memory_statistic_file);
|
|
_ecore_memory_statistic_file = NULL;
|
|
#endif
|
|
}
|
|
#endif
|
|
ecore_mempool_shutdown();
|
|
eina_log_domain_unregister(_ecore_log_dom);
|
|
_ecore_log_dom = -1;
|
|
|
|
eina_prefix_free(_ecore_pfx);
|
|
_ecore_pfx = NULL;
|
|
|
|
efl_object_shutdown();
|
|
|
|
eina_evlog("<RUN", NULL, 0.0, NULL);
|
|
eina_shutdown();
|
|
#ifdef _WIN32
|
|
evil_shutdown();
|
|
#endif
|
|
|
|
end:
|
|
return _ecore_init_count;
|
|
}
|
|
|
|
static unsigned int _ecore_init_ex = 0;
|
|
|
|
EAPI unsigned int
|
|
ecore_init_ex(int argc, char **argv)
|
|
{
|
|
if (_ecore_init_ex++ != 0) return _ecore_init_ex;
|
|
|
|
ecore_init();
|
|
ecore_loop_arguments_send(argc - 1,
|
|
(argc > 1) ? ((const char **) argv + 1) : NULL);
|
|
ecore_app_args_set(argc, (const char**) argv);
|
|
|
|
return _ecore_init_ex;
|
|
}
|
|
|
|
EAPI unsigned int
|
|
ecore_shutdown_ex(void)
|
|
{
|
|
if (--_ecore_init_ex != 0) return _ecore_init_ex;
|
|
|
|
ecore_shutdown();
|
|
|
|
return _ecore_init_ex;
|
|
}
|
|
|
|
struct _Ecore_Fork_Cb
|
|
{
|
|
Ecore_Cb func;
|
|
void *data;
|
|
Eina_Bool delete_me : 1;
|
|
};
|
|
|
|
typedef struct _Ecore_Fork_Cb Ecore_Fork_Cb;
|
|
|
|
static int fork_cbs_walking = 0;
|
|
static Eina_List *fork_cbs = NULL;
|
|
|
|
EAPI Eina_Bool
|
|
ecore_fork_reset_callback_add(Ecore_Cb func, const void *data)
|
|
{
|
|
Ecore_Fork_Cb *fcb;
|
|
|
|
if (!func) return EINA_FALSE;
|
|
fcb = calloc(1, sizeof(Ecore_Fork_Cb));
|
|
if (!fcb) return EINA_FALSE;
|
|
fcb->func = func;
|
|
fcb->data = (void *)data;
|
|
fork_cbs = eina_list_append(fork_cbs, fcb);
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
ecore_fork_reset_callback_del(Ecore_Cb func, const void *data)
|
|
{
|
|
Eina_List *l;
|
|
Ecore_Fork_Cb *fcb;
|
|
|
|
EINA_LIST_FOREACH(fork_cbs, l, fcb)
|
|
{
|
|
if ((fcb->func == func) && (fcb->data == data))
|
|
{
|
|
if (!fork_cbs_walking)
|
|
{
|
|
fork_cbs = eina_list_remove_list(fork_cbs, l);
|
|
free(fcb);
|
|
}
|
|
else
|
|
fcb->delete_me = EINA_TRUE;
|
|
return EINA_TRUE;
|
|
}
|
|
}
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_fork_reset(void)
|
|
{
|
|
Eina_List *l, *ln;
|
|
Ecore_Fork_Cb *fcb;
|
|
|
|
eina_main_loop_define();
|
|
eina_lock_take(&_thread_safety);
|
|
|
|
ecore_pipe_del(_thread_call);
|
|
_thread_call = ecore_pipe_add(_thread_callback, NULL);
|
|
/* If there was something in the pipe, trigger a wakeup again */
|
|
if (_thread_cb) ecore_pipe_write(_thread_call, &wakeup, sizeof (int));
|
|
|
|
eina_lock_release(&_thread_safety);
|
|
|
|
// should this be done withing the eina lock stuff?
|
|
fork_cbs_walking++;
|
|
EINA_LIST_FOREACH(fork_cbs, l, fcb)
|
|
{
|
|
fcb->func(fcb->data);
|
|
}
|
|
fork_cbs_walking--;
|
|
|
|
EINA_LIST_FOREACH_SAFE(fork_cbs, l, ln, fcb)
|
|
{
|
|
if (fcb->delete_me)
|
|
{
|
|
fork_cbs = eina_list_remove_list(fork_cbs, l);
|
|
free(fcb);
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_SYSTEMD
|
|
unsetenv("NOTIFY_SOCKET");
|
|
#endif
|
|
}
|
|
|
|
EAPI void
|
|
ecore_main_loop_thread_safe_call_async(Ecore_Cb callback,
|
|
void *data)
|
|
{
|
|
Ecore_Safe_Call *order;
|
|
|
|
if (!callback) return;
|
|
|
|
if (eina_main_loop_is())
|
|
{
|
|
callback(data);
|
|
return;
|
|
}
|
|
|
|
order = malloc(sizeof (Ecore_Safe_Call));
|
|
if (!order) return;
|
|
|
|
order->cb.async = callback;
|
|
order->data = data;
|
|
order->sync = EINA_FALSE;
|
|
order->suspend = EINA_FALSE;
|
|
|
|
_ecore_main_loop_thread_safe_call(order);
|
|
}
|
|
|
|
EAPI void *
|
|
ecore_main_loop_thread_safe_call_sync(Ecore_Data_Cb callback,
|
|
void *data)
|
|
{
|
|
Ecore_Safe_Call *order;
|
|
void *ret;
|
|
|
|
if (!callback) return NULL;
|
|
|
|
if (eina_main_loop_is())
|
|
{
|
|
return callback(data);
|
|
}
|
|
|
|
order = malloc(sizeof (Ecore_Safe_Call));
|
|
if (!order) return NULL;
|
|
|
|
order->cb.sync = callback;
|
|
order->data = data;
|
|
eina_lock_new(&order->m);
|
|
eina_condition_new(&order->c, &order->m);
|
|
order->sync = EINA_TRUE;
|
|
order->suspend = EINA_FALSE;
|
|
|
|
eina_lock_take(&order->m);
|
|
_ecore_main_loop_thread_safe_call(order);
|
|
eina_condition_wait(&order->c);
|
|
eina_lock_release(&order->m);
|
|
|
|
ret = order->data;
|
|
|
|
order->sync = EINA_FALSE;
|
|
order->cb.async = _thread_safe_cleanup;
|
|
order->data = order;
|
|
|
|
_ecore_main_loop_thread_safe_call(order);
|
|
|
|
return ret;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_main_loop_thread_safe_call_wait(double wait)
|
|
{
|
|
ecore_pipe_wait(_thread_call, 1, wait);
|
|
}
|
|
|
|
static Efl_Id_Domain _ecore_main_domain = EFL_ID_DOMAIN_INVALID;
|
|
|
|
EAPI int
|
|
ecore_thread_main_loop_begin(void)
|
|
{
|
|
Ecore_Safe_Call *order;
|
|
|
|
if (eina_main_loop_is())
|
|
{
|
|
return ++_thread_loop;
|
|
}
|
|
|
|
order = malloc(sizeof (Ecore_Safe_Call));
|
|
if (!order) return -1;
|
|
|
|
eina_lock_take(&_thread_id_lock);
|
|
order->current_id = ++_thread_id_max;
|
|
if (order->current_id < 0)
|
|
{
|
|
_thread_id_max = 0;
|
|
order->current_id = ++_thread_id_max;
|
|
}
|
|
eina_lock_release(&_thread_id_lock);
|
|
|
|
eina_lock_new(&order->m);
|
|
eina_condition_new(&order->c, &order->m);
|
|
order->suspend = EINA_TRUE;
|
|
order->eo_domain_data = NULL;
|
|
|
|
_ecore_main_loop_thread_safe_call(order);
|
|
|
|
eina_lock_take(&order->m);
|
|
while (order->current_id != _thread_id)
|
|
eina_condition_wait(&order->c);
|
|
|
|
if (order->eo_domain_data)
|
|
{
|
|
_ecore_main_domain =
|
|
efl_domain_data_adopt(order->eo_domain_data);
|
|
if (_ecore_main_domain == EFL_ID_DOMAIN_INVALID)
|
|
ERR("Cannot adopt mainloop eo domain");
|
|
}
|
|
|
|
eina_lock_release(&order->m);
|
|
|
|
eina_main_loop_define();
|
|
|
|
_thread_loop = 1;
|
|
|
|
return _thread_loop;
|
|
}
|
|
|
|
EAPI int
|
|
ecore_thread_main_loop_end(void)
|
|
{
|
|
int current_id;
|
|
|
|
if (_thread_loop == 0)
|
|
{
|
|
ERR("the main loop is not locked ! No matching call to ecore_thread_main_loop_begin().");
|
|
return -1;
|
|
}
|
|
|
|
/* until we unlock the main loop, this thread has the main loop id */
|
|
if (!eina_main_loop_is())
|
|
{
|
|
ERR("Not in a locked thread !");
|
|
return -1;
|
|
}
|
|
|
|
_thread_loop--;
|
|
if (_thread_loop > 0)
|
|
return _thread_loop;
|
|
|
|
if (_ecore_main_domain != EFL_ID_DOMAIN_INVALID)
|
|
{
|
|
efl_domain_data_return(_ecore_main_domain);
|
|
_ecore_main_domain = EFL_ID_DOMAIN_INVALID;
|
|
}
|
|
|
|
current_id = _thread_id;
|
|
|
|
eina_lock_take(&_thread_mutex);
|
|
_thread_id_update = _thread_id;
|
|
eina_condition_broadcast(&_thread_cond);
|
|
eina_lock_release(&_thread_mutex);
|
|
|
|
eina_lock_take(&_thread_feedback_mutex);
|
|
while (current_id == _thread_id && _thread_id != -1)
|
|
eina_condition_wait(&_thread_feedback_cond);
|
|
eina_lock_release(&_thread_feedback_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_print_warning(const char *function EINA_UNUSED,
|
|
const char *sparam EINA_UNUSED)
|
|
{
|
|
WRN("***** Developer Warning ***** :\n"
|
|
"\tThis program is calling:\n\n"
|
|
"\t%s();\n\n"
|
|
"\tWith the parameter:\n\n"
|
|
"\t%s\n\n"
|
|
"\tbeing NULL. Please fix your program.", function, sparam);
|
|
if (getenv("ECORE_ERROR_ABORT")) abort();
|
|
}
|
|
|
|
EAPI void
|
|
_ecore_magic_fail(const void *d,
|
|
Ecore_Magic m,
|
|
Ecore_Magic req_m,
|
|
const char *fname EINA_UNUSED)
|
|
{
|
|
ERR("*** ECORE ERROR: Ecore Magic Check Failed!!! in: %s()", fname);
|
|
if (!d)
|
|
ERR(" Input handle pointer is NULL!");
|
|
else if (m == ECORE_MAGIC_NONE)
|
|
ERR(" Input handle has already been freed!");
|
|
else if (m != req_m)
|
|
ERR(" Input handle is wrong type\n"
|
|
" Expected: %08x - %s\n"
|
|
" Supplied: %08x - %s",
|
|
(unsigned int)req_m, _ecore_magic_string_get(req_m),
|
|
(unsigned int)m, _ecore_magic_string_get(m));
|
|
|
|
if (getenv("ECORE_ERROR_ABORT")) abort();
|
|
}
|
|
|
|
static const char *
|
|
_ecore_magic_string_get(Ecore_Magic m)
|
|
{
|
|
switch (m)
|
|
{
|
|
case ECORE_MAGIC_NONE:
|
|
return "None (Freed Object)";
|
|
break;
|
|
|
|
case ECORE_MAGIC_EXE:
|
|
return "Ecore_Exe (Executable)";
|
|
break;
|
|
|
|
case ECORE_MAGIC_TIMER:
|
|
return "Ecore_Timer (Timer)";
|
|
break;
|
|
|
|
case ECORE_MAGIC_IDLER:
|
|
return "Ecore_Idler (Idler)";
|
|
break;
|
|
|
|
case ECORE_MAGIC_IDLE_ENTERER:
|
|
return "Ecore_Idle_Enterer (Idler Enterer)";
|
|
break;
|
|
|
|
case ECORE_MAGIC_IDLE_EXITER:
|
|
return "Ecore_Idle_Exiter (Idler Exiter)";
|
|
break;
|
|
|
|
case ECORE_MAGIC_FD_HANDLER:
|
|
return "Ecore_Fd_Handler (Fd Handler)";
|
|
break;
|
|
|
|
case ECORE_MAGIC_WIN32_HANDLER:
|
|
return "Ecore_Win32_Handler (Win32 Handler)";
|
|
break;
|
|
|
|
case ECORE_MAGIC_EVENT_HANDLER:
|
|
return "Ecore_Event_Handler (Event Handler)";
|
|
break;
|
|
|
|
case ECORE_MAGIC_EVENT:
|
|
return "Ecore_Event (Event)";
|
|
break;
|
|
|
|
default:
|
|
return "<UNKNOWN>";
|
|
}
|
|
}
|
|
|
|
/* fps debug calls - for debugging how much time your app actually spends */
|
|
/* "running" (and the inverse being time spent running)... this does not */
|
|
/* account for other apps and multitasking... */
|
|
|
|
static int _ecore_fps_debug_init_count = 0;
|
|
static int _ecore_fps_debug_fd = -1;
|
|
unsigned int *_ecore_fps_runtime_mmap = NULL;
|
|
|
|
void
|
|
_ecore_fps_debug_init(void)
|
|
{
|
|
char buf[PATH_MAX];
|
|
const char *tmp;
|
|
int pid;
|
|
|
|
_ecore_fps_debug_init_count++;
|
|
if (_ecore_fps_debug_init_count > 1) return;
|
|
|
|
tmp = eina_environment_tmp_get();
|
|
pid = (int)getpid();
|
|
snprintf(buf, sizeof(buf), "%s/.ecore_fps_debug-%i", tmp, pid);
|
|
_ecore_fps_debug_fd = open(buf, O_CREAT | O_BINARY | O_TRUNC | O_RDWR, 0644);
|
|
if (_ecore_fps_debug_fd < 0)
|
|
{
|
|
unlink(buf);
|
|
_ecore_fps_debug_fd = open(buf, O_CREAT | O_BINARY | O_TRUNC | O_RDWR, 0644);
|
|
}
|
|
if (_ecore_fps_debug_fd >= 0)
|
|
{
|
|
unsigned int zero = 0;
|
|
char *buf2 = (char *)&zero;
|
|
ssize_t todo = sizeof(unsigned int);
|
|
|
|
while (todo > 0)
|
|
{
|
|
ssize_t r = write(_ecore_fps_debug_fd, buf2, todo);
|
|
if (r > 0)
|
|
{
|
|
todo -= r;
|
|
buf2 += r;
|
|
}
|
|
else if ((r < 0) && (errno == EINTR))
|
|
continue;
|
|
else
|
|
{
|
|
ERR("could not write to file '%s' fd %d: %s",
|
|
tmp, _ecore_fps_debug_fd, strerror(errno));
|
|
close(_ecore_fps_debug_fd);
|
|
_ecore_fps_debug_fd = -1;
|
|
return;
|
|
}
|
|
}
|
|
_ecore_fps_runtime_mmap = mmap(NULL, sizeof(unsigned int),
|
|
PROT_READ | PROT_WRITE,
|
|
MAP_SHARED,
|
|
_ecore_fps_debug_fd, 0);
|
|
if (_ecore_fps_runtime_mmap == MAP_FAILED)
|
|
_ecore_fps_runtime_mmap = NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
_ecore_fps_debug_shutdown(void)
|
|
{
|
|
_ecore_fps_debug_init_count--;
|
|
if (_ecore_fps_debug_init_count > 0) return;
|
|
if (_ecore_fps_debug_fd >= 0)
|
|
{
|
|
char buf[4096];
|
|
int pid;
|
|
|
|
pid = (int)getpid();
|
|
snprintf(buf, sizeof(buf), "%s/.ecore_fps_debug-%i",
|
|
eina_environment_tmp_get(), pid);
|
|
unlink(buf);
|
|
if (_ecore_fps_runtime_mmap)
|
|
{
|
|
munmap(_ecore_fps_runtime_mmap, sizeof(unsigned int));
|
|
_ecore_fps_runtime_mmap = NULL;
|
|
}
|
|
close(_ecore_fps_debug_fd);
|
|
_ecore_fps_debug_fd = -1;
|
|
}
|
|
}
|
|
|
|
void
|
|
_ecore_fps_debug_runtime_add(double t)
|
|
{
|
|
if ((_ecore_fps_debug_fd >= 0) &&
|
|
(_ecore_fps_runtime_mmap))
|
|
{
|
|
unsigned int tm;
|
|
|
|
tm = (unsigned int)(t * 1000000.0);
|
|
/* i know its not 100% theoretically guaranteed, but i'd say a write */
|
|
/* of an int could be considered atomic for all practical purposes */
|
|
/* oh and since this is cumulative, 1 second = 1,000,000 ticks, so */
|
|
/* this can run for about 4294 seconds becore looping. if you are */
|
|
/* doing performance testing in one run for over an hour... well */
|
|
/* time to restart or handle a loop condition :) */
|
|
*(_ecore_fps_runtime_mmap) += tm;
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_SYSTEMD
|
|
static void
|
|
_systemd_watchdog_cb(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
|
|
{
|
|
sd_notify(0, "WATCHDOG=1");
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAVE_MALLINFO) || defined(HAVE_MALLOC_INFO)
|
|
static Eina_Bool
|
|
_ecore_memory_statistic(EINA_UNUSED void *data)
|
|
{
|
|
#ifdef HAVE_MALLOC_INFO
|
|
static int frame = 0;
|
|
#endif
|
|
#ifdef HAVE_MALLINFO
|
|
struct mallinfo mi;
|
|
static int uordblks = 0;
|
|
static int fordblks = 0;
|
|
Eina_Bool changed = EINA_FALSE;
|
|
|
|
mi = mallinfo();
|
|
|
|
#define HAS_CHANGED(Global, Local) \
|
|
if (Global != Local) \
|
|
{ \
|
|
Global = Local; \
|
|
changed = EINA_TRUE; \
|
|
}
|
|
|
|
HAS_CHANGED(uordblks, mi.uordblks);
|
|
HAS_CHANGED(fordblks, mi.fordblks);
|
|
|
|
if (changed)
|
|
ERR("[%i] Memory total: %i, free: %i",
|
|
_ecore_memory_pid,
|
|
mi.uordblks,
|
|
mi.fordblks);
|
|
|
|
KEEP_MAX(_ecore_memory_max_total, mi.uordblks);
|
|
KEEP_MAX(_ecore_memory_max_free, mi.fordblks);
|
|
#endif
|
|
#ifdef HAVE_MALLOC_INFO
|
|
if (frame) fputs("\n", _ecore_memory_statistic_file);
|
|
malloc_info(0, _ecore_memory_statistic_file);
|
|
#endif
|
|
|
|
return ECORE_CALLBACK_RENEW;
|
|
}
|
|
|
|
#endif
|
|
|
|
static void
|
|
_ecore_main_loop_thread_safe_call(Ecore_Safe_Call *order)
|
|
{
|
|
Eina_Bool count;
|
|
|
|
eina_lock_take(&_thread_safety);
|
|
|
|
count = _thread_cb ? 0 : 1;
|
|
_thread_cb = eina_list_append(_thread_cb, order);
|
|
if (count) ecore_pipe_write(_thread_call, &wakeup, sizeof (int));
|
|
|
|
eina_lock_release(&_thread_safety);
|
|
}
|
|
|
|
static void
|
|
_thread_safe_cleanup(void *data)
|
|
{
|
|
Ecore_Safe_Call *call = data;
|
|
|
|
eina_condition_free(&call->c);
|
|
eina_lock_free(&call->m);
|
|
}
|
|
|
|
void
|
|
_ecore_main_call_flush(void)
|
|
{
|
|
Ecore_Safe_Call *call;
|
|
Eina_List *callback;
|
|
|
|
eina_lock_take(&_thread_safety);
|
|
callback = _thread_cb;
|
|
_thread_cb = NULL;
|
|
eina_lock_release(&_thread_safety);
|
|
|
|
EINA_LIST_FREE(callback, call)
|
|
{
|
|
if (call->suspend)
|
|
{
|
|
eina_lock_take(&_thread_mutex);
|
|
|
|
eina_lock_take(&call->m);
|
|
_thread_id = call->current_id;
|
|
call->eo_domain_data = efl_domain_data_get();
|
|
eina_condition_broadcast(&call->c);
|
|
eina_lock_release(&call->m);
|
|
|
|
while (_thread_id_update != _thread_id)
|
|
eina_condition_wait(&_thread_cond);
|
|
eina_lock_release(&_thread_mutex);
|
|
|
|
eina_main_loop_define();
|
|
|
|
eina_lock_take(&_thread_feedback_mutex);
|
|
|
|
_thread_id = -1;
|
|
|
|
eina_condition_broadcast(&_thread_feedback_cond);
|
|
eina_lock_release(&_thread_feedback_mutex);
|
|
|
|
_thread_safe_cleanup(call);
|
|
free(call);
|
|
}
|
|
else if (call->sync)
|
|
{
|
|
call->data = call->cb.sync(call->data);
|
|
eina_lock_take(&call->m);
|
|
eina_condition_broadcast(&call->c);
|
|
eina_lock_release(&call->m);
|
|
}
|
|
else
|
|
{
|
|
call->cb.async(call->data);
|
|
free(call);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
_thread_callback(void *data EINA_UNUSED,
|
|
void *buffer EINA_UNUSED,
|
|
unsigned int nbyte EINA_UNUSED)
|
|
{
|
|
_ecore_main_call_flush();
|
|
}
|
|
|
|
EAPI Ecore_Power_State
|
|
ecore_power_state_get(void)
|
|
{
|
|
return _ecore_power_state;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_power_state_set(Ecore_Power_State state)
|
|
{
|
|
if (_ecore_power_state == state) return;
|
|
_ecore_power_state = state;
|
|
ecore_event_add(ECORE_EVENT_POWER_STATE, NULL, NULL, NULL);
|
|
}
|
|
|
|
EAPI Ecore_Memory_State
|
|
ecore_memory_state_get(void)
|
|
{
|
|
return _ecore_memory_state;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_memory_state_set(Ecore_Memory_State state)
|
|
{
|
|
if (_ecore_memory_state == state) return;
|
|
_ecore_memory_state = state;
|
|
ecore_event_add(ECORE_EVENT_MEMORY_STATE, NULL, NULL, NULL);
|
|
}
|