efl/src/lib/ecore/ecore.c

1127 lines
28 KiB
C
Raw Normal View History

#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>
2013-08-27 00:05:15 -07:00
#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;
eo - make eo id table TLS private data for thread safety and speed This moved all the eoid tables, eoid lookup caches, generation count information ad eo_isa cache into a TLS segment of memory that is thread private. There is also a shared domain for EO objects that all threads can access, but it has an added cost of a lock. This means objects accessed outside the thread they were created in cannot be accessed by another thread unless they are adopted in temporarily, or create4d with the shared domain active at the time of creation. child objects will use their parent object domain if created with a parent object passed in. If you were accessing EO (EFL) objects across threads before then this will actually now cause your code to fail as it was invalid before to do this as no actual objects were threadsafe in EFL, so this will force things to "fail early". ecore_thread_main_loop_begin() and end() still work as this uses the eo domain adoption features to temporarily adopt a domain during this section and then return it when done. This returns speed back to eo brining the overhead in my tests of lookup for the elm genlist autobounce test in elementary from about 5-7% down to 2.5-2.6%. A steep drop. This does not mean everything is perfect. Still to do are: 1. Tests in the test suite 2. Some API's to help for sending objects from thread to thread 3. Make the eo call cache TLS data to make it also safe 4. Look at other locks in eo and probably move them to TLS data 5. Make eo resolve and call wrappers that call the real method func do recursive mutex wrapping of the given object IF it is a shared object to provide threadsafety transparently for shared objects (but adding some overhead as a result) 6. Test test est, and that is why this commit is going in now for wider testing 7. Decide how to make this work with sending IPC (between threads) 8. Deciding what makes an object sendable (a sendable property in base?) 9. Deciding what makes an object shareable (a sharable property in base?)
2016-09-07 01:53:33 -07:00
Efl_Domain_Data *eo_domain_data;
int current_id;
Eina_Bool sync : 1;
Eina_Bool suspend : 1;
};
#ifdef HAVE_SYSTEMD
static Eina_Bool _systemd_watchdog_cb(void *data);
#endif
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 Ecore_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
2014-01-08 05:06:41 -08:00
#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",
2013-08-14 12:06:22 -07:00
#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"))
{
2010-09-29 23:09:20 -07:00
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;
}
2017-02-28 11:01:09 -08:00
_ecore_animator_init();
2013-03-27 05:42:55 -07:00
_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();
2013-03-27 05:42:55 -07:00
if (getenv("ECORE_FPS_DEBUG")) _ecore_fps_debug = 1;
if (_ecore_fps_debug) _ecore_fps_debug_init();
2011-12-02 19:39:07 -08:00
if (!ecore_mempool_init()) goto shutdown_mempool;
if (!_ecore_event_init()) goto shutdown_mempool;
_ecore_main_loop_init();
vpath = efl_add(EFL_VPATH_CORE_CLASS, NULL);
if (vpath) efl_vpath_manager_register(EFL_VPATH_MANAGER_CLASS, 0, vpath);
_mainloop_singleton = efl_add(EFL_LOOP_CLASS, NULL);
_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
2010-09-29 23:09:20 -07:00
_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 = ecore_timer_add(sec / 2, _systemd_watchdog_cb, NULL);
unsetenv("WATCHDOG_USEC");
INF("Setup systemd watchdog to : %f", sec);
_systemd_watchdog_cb(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(ecore_main_loop_get(),
EFL_LOOP_EVENT_RESUME,
_efl_first_loop_iterate,
getenv("EFL_FIRST_LOOP"));
_ecore_init_count_threshold = _ecore_init_count;
2013-03-27 05:42:55 -07:00
eina_log_timing(_ecore_log_dom,
EINA_LOG_STATE_STOP,
EINA_LOG_STATE_INIT);
2013-03-27 05:42:55 -07:00
return _ecore_init_count;
2011-12-02 19:39:07 -08:00
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;
ecore_system_modules_unload();
2013-03-27 05:42:55 -07:00
eina_log_timing(_ecore_log_dom,
EINA_LOG_STATE_START,
EINA_LOG_STATE_SHUTDOWN);
2013-03-27 05:42:55 -07:00
#ifdef HAVE_SYSTEMD
if (_systemd_watchdog)
{
ecore_timer_del(_systemd_watchdog);
_systemd_watchdog = NULL;
}
#endif
efl_del(_mainloop_singleton);
_mainloop_singleton = NULL;
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
_efl_loop_timer_shutdown();
_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
2011-12-02 19:39:07 -08:00
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);
}
eo - make eo id table TLS private data for thread safety and speed This moved all the eoid tables, eoid lookup caches, generation count information ad eo_isa cache into a TLS segment of memory that is thread private. There is also a shared domain for EO objects that all threads can access, but it has an added cost of a lock. This means objects accessed outside the thread they were created in cannot be accessed by another thread unless they are adopted in temporarily, or create4d with the shared domain active at the time of creation. child objects will use their parent object domain if created with a parent object passed in. If you were accessing EO (EFL) objects across threads before then this will actually now cause your code to fail as it was invalid before to do this as no actual objects were threadsafe in EFL, so this will force things to "fail early". ecore_thread_main_loop_begin() and end() still work as this uses the eo domain adoption features to temporarily adopt a domain during this section and then return it when done. This returns speed back to eo brining the overhead in my tests of lookup for the elm genlist autobounce test in elementary from about 5-7% down to 2.5-2.6%. A steep drop. This does not mean everything is perfect. Still to do are: 1. Tests in the test suite 2. Some API's to help for sending objects from thread to thread 3. Make the eo call cache TLS data to make it also safe 4. Look at other locks in eo and probably move them to TLS data 5. Make eo resolve and call wrappers that call the real method func do recursive mutex wrapping of the given object IF it is a shared object to provide threadsafety transparently for shared objects (but adding some overhead as a result) 6. Test test est, and that is why this commit is going in now for wider testing 7. Decide how to make this work with sending IPC (between threads) 8. Deciding what makes an object sendable (a sendable property in base?) 9. Deciding what makes an object shareable (a sharable property in base?)
2016-09-07 01:53:33 -07:00
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;
eo - make eo id table TLS private data for thread safety and speed This moved all the eoid tables, eoid lookup caches, generation count information ad eo_isa cache into a TLS segment of memory that is thread private. There is also a shared domain for EO objects that all threads can access, but it has an added cost of a lock. This means objects accessed outside the thread they were created in cannot be accessed by another thread unless they are adopted in temporarily, or create4d with the shared domain active at the time of creation. child objects will use their parent object domain if created with a parent object passed in. If you were accessing EO (EFL) objects across threads before then this will actually now cause your code to fail as it was invalid before to do this as no actual objects were threadsafe in EFL, so this will force things to "fail early". ecore_thread_main_loop_begin() and end() still work as this uses the eo domain adoption features to temporarily adopt a domain during this section and then return it when done. This returns speed back to eo brining the overhead in my tests of lookup for the elm genlist autobounce test in elementary from about 5-7% down to 2.5-2.6%. A steep drop. This does not mean everything is perfect. Still to do are: 1. Tests in the test suite 2. Some API's to help for sending objects from thread to thread 3. Make the eo call cache TLS data to make it also safe 4. Look at other locks in eo and probably move them to TLS data 5. Make eo resolve and call wrappers that call the real method func do recursive mutex wrapping of the given object IF it is a shared object to provide threadsafety transparently for shared objects (but adding some overhead as a result) 6. Test test est, and that is why this commit is going in now for wider testing 7. Decide how to make this work with sending IPC (between threads) 8. Deciding what makes an object sendable (a sendable property in base?) 9. Deciding what makes an object shareable (a sharable property in base?)
2016-09-07 01:53:33 -07:00
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);
eo - make eo id table TLS private data for thread safety and speed This moved all the eoid tables, eoid lookup caches, generation count information ad eo_isa cache into a TLS segment of memory that is thread private. There is also a shared domain for EO objects that all threads can access, but it has an added cost of a lock. This means objects accessed outside the thread they were created in cannot be accessed by another thread unless they are adopted in temporarily, or create4d with the shared domain active at the time of creation. child objects will use their parent object domain if created with a parent object passed in. If you were accessing EO (EFL) objects across threads before then this will actually now cause your code to fail as it was invalid before to do this as no actual objects were threadsafe in EFL, so this will force things to "fail early". ecore_thread_main_loop_begin() and end() still work as this uses the eo domain adoption features to temporarily adopt a domain during this section and then return it when done. This returns speed back to eo brining the overhead in my tests of lookup for the elm genlist autobounce test in elementary from about 5-7% down to 2.5-2.6%. A steep drop. This does not mean everything is perfect. Still to do are: 1. Tests in the test suite 2. Some API's to help for sending objects from thread to thread 3. Make the eo call cache TLS data to make it also safe 4. Look at other locks in eo and probably move them to TLS data 5. Make eo resolve and call wrappers that call the real method func do recursive mutex wrapping of the given object IF it is a shared object to provide threadsafety transparently for shared objects (but adding some overhead as a result) 6. Test test est, and that is why this commit is going in now for wider testing 7. Decide how to make this work with sending IPC (between threads) 8. Deciding what makes an object sendable (a sendable property in base?) 9. Deciding what makes an object shareable (a sharable property in base?)
2016-09-07 01:53:33 -07:00
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;
eo - make eo id table TLS private data for thread safety and speed This moved all the eoid tables, eoid lookup caches, generation count information ad eo_isa cache into a TLS segment of memory that is thread private. There is also a shared domain for EO objects that all threads can access, but it has an added cost of a lock. This means objects accessed outside the thread they were created in cannot be accessed by another thread unless they are adopted in temporarily, or create4d with the shared domain active at the time of creation. child objects will use their parent object domain if created with a parent object passed in. If you were accessing EO (EFL) objects across threads before then this will actually now cause your code to fail as it was invalid before to do this as no actual objects were threadsafe in EFL, so this will force things to "fail early". ecore_thread_main_loop_begin() and end() still work as this uses the eo domain adoption features to temporarily adopt a domain during this section and then return it when done. This returns speed back to eo brining the overhead in my tests of lookup for the elm genlist autobounce test in elementary from about 5-7% down to 2.5-2.6%. A steep drop. This does not mean everything is perfect. Still to do are: 1. Tests in the test suite 2. Some API's to help for sending objects from thread to thread 3. Make the eo call cache TLS data to make it also safe 4. Look at other locks in eo and probably move them to TLS data 5. Make eo resolve and call wrappers that call the real method func do recursive mutex wrapping of the given object IF it is a shared object to provide threadsafety transparently for shared objects (but adding some overhead as a result) 6. Test test est, and that is why this commit is going in now for wider testing 7. Decide how to make this work with sending IPC (between threads) 8. Deciding what makes an object sendable (a sendable property in base?) 9. Deciding what makes an object shareable (a sharable property in base?)
2016-09-07 01:53:33 -07:00
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();
}
2006-01-06 10:05:08 -08:00
EAPI void
_ecore_magic_fail(const void *d,
Ecore_Magic m,
Ecore_Magic req_m,
const char *fname EINA_UNUSED)
{
ERR("\n"
"*** ECORE ERROR: Ecore Magic Check Failed!!!\n"
"*** IN FUNCTION: %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"
2010-09-29 23:09:20 -07:00
" 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));
ERR("*** NAUGHTY PROGRAMMER!!!\n"
"*** SPANK SPANK SPANK!!!\n"
"*** Now go fix your code. Tut tut tut!");
if (getenv("ECORE_ERROR_ABORT")) abort();
}
static const char *
_ecore_magic_string_get(Ecore_Magic m)
{
switch (m)
{
case ECORE_MAGIC_NONE:
2010-09-29 23:09:20 -07:00
return "None (Freed Object)";
break;
case ECORE_MAGIC_EXE:
2010-09-29 23:09:20 -07:00
return "Ecore_Exe (Executable)";
break;
case ECORE_MAGIC_TIMER:
2010-09-29 23:09:20 -07:00
return "Ecore_Timer (Timer)";
break;
case ECORE_MAGIC_IDLER:
2010-09-29 23:09:20 -07:00
return "Ecore_Idler (Idler)";
break;
case ECORE_MAGIC_IDLE_ENTERER:
2010-09-29 23:09:20 -07:00
return "Ecore_Idle_Enterer (Idler Enterer)";
break;
case ECORE_MAGIC_IDLE_EXITER:
2010-09-29 23:09:20 -07:00
return "Ecore_Idle_Exiter (Idler Exiter)";
break;
case ECORE_MAGIC_FD_HANDLER:
2010-09-29 23:09:20 -07:00
return "Ecore_Fd_Handler (Fd Handler)";
break;
case ECORE_MAGIC_WIN32_HANDLER:
2010-09-29 23:09:20 -07:00
return "Ecore_Win32_Handler (Win32 Handler)";
break;
case ECORE_MAGIC_EVENT_HANDLER:
2010-09-29 23:09:20 -07:00
return "Ecore_Event_Handler (Event Handler)";
break;
case ECORE_MAGIC_EVENT:
2010-09-29 23:09:20 -07:00
return "Ecore_Event (Event)";
break;
default:
2010-09-29 23:09:20 -07:00
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... */
2004-04-04 10:12:14 -07:00
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;
2015-07-21 22:31:26 -07:00
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)
{
2010-09-29 23:09:20 -07:00
unlink(buf);
_ecore_fps_debug_fd = open(buf, O_CREAT | O_BINARY | O_TRUNC | O_RDWR, 0644);
}
if (_ecore_fps_debug_fd >= 0)
{
2010-09-29 23:09:20 -07:00
unsigned int zero = 0;
char *buf2 = (char *)&zero;
2010-09-29 23:09:20 -07:00
ssize_t todo = sizeof(unsigned int);
while (todo > 0)
{
ssize_t r = write(_ecore_fps_debug_fd, buf2, todo);
2010-09-29 23:09:20 -07:00
if (r > 0)
{
todo -= r;
buf2 += r;
2010-09-29 23:09:20 -07:00
}
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();
2015-07-21 22:31:26 -07:00
snprintf(buf, sizeof(buf), "%s/.ecore_fps_debug-%i",
eina_environment_tmp_get(), pid);
2010-09-29 23:09:20 -07:00
unlink(buf);
if (_ecore_fps_runtime_mmap)
{
munmap(_ecore_fps_runtime_mmap, sizeof(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))
{
2010-09-29 23:09:20 -07:00
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 Eina_Bool
_systemd_watchdog_cb(EINA_UNUSED void *data)
{
sd_notify(0, "WATCHDOG=1");
return ECORE_CALLBACK_RENEW;
}
#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",
2010-09-29 23:09:20 -07:00
_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;
}
2010-10-17 00:03:28 -07:00
#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;
eo - make eo id table TLS private data for thread safety and speed This moved all the eoid tables, eoid lookup caches, generation count information ad eo_isa cache into a TLS segment of memory that is thread private. There is also a shared domain for EO objects that all threads can access, but it has an added cost of a lock. This means objects accessed outside the thread they were created in cannot be accessed by another thread unless they are adopted in temporarily, or create4d with the shared domain active at the time of creation. child objects will use their parent object domain if created with a parent object passed in. If you were accessing EO (EFL) objects across threads before then this will actually now cause your code to fail as it was invalid before to do this as no actual objects were threadsafe in EFL, so this will force things to "fail early". ecore_thread_main_loop_begin() and end() still work as this uses the eo domain adoption features to temporarily adopt a domain during this section and then return it when done. This returns speed back to eo brining the overhead in my tests of lookup for the elm genlist autobounce test in elementary from about 5-7% down to 2.5-2.6%. A steep drop. This does not mean everything is perfect. Still to do are: 1. Tests in the test suite 2. Some API's to help for sending objects from thread to thread 3. Make the eo call cache TLS data to make it also safe 4. Look at other locks in eo and probably move them to TLS data 5. Make eo resolve and call wrappers that call the real method func do recursive mutex wrapping of the given object IF it is a shared object to provide threadsafety transparently for shared objects (but adding some overhead as a result) 6. Test test est, and that is why this commit is going in now for wider testing 7. Decide how to make this work with sending IPC (between threads) 8. Deciding what makes an object sendable (a sendable property in base?) 9. Deciding what makes an object shareable (a sharable property in base?)
2016-09-07 01:53:33 -07:00
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);
}