#ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #ifndef _MSC_VER # include #endif #ifdef HAVE_LOCALE_H # include #endif #ifdef HAVE_LANGINFO_H # include #endif #ifdef HAVE_SYS_MMAN_H # include #endif #ifdef HAVE_EVIL # include #endif #include #include "Ecore.h" #include "ecore_private.h" #if HAVE_MALLINFO #include static Ecore_Version _version = { VERS_MAJ, VERS_MIN, VERS_MIC, VERS_REV }; EAPI Ecore_Version *ecore_version = &_version; #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; #endif static const char *_ecore_magic_string_get(Ecore_Magic m); static int _ecore_init_count = 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; 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 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; Eina_Lock _ecore_main_loop_lock; int _ecore_main_lock_count; /** OpenBSD does not define CODESET * FIXME ?? */ #ifndef CODESET # define CODESET "INVALID" #endif /** * @addtogroup Ecore_Init_Group * * @{ */ /** * Set up connections, signal handlers, sockets etc. * @return 1 or greater on success, 0 otherwise * * This function sets up all singal handlers and the basic event loop. If it * succeeds, 1 will be returned, otherwise 0 will be returned. * * @code * #include * * int main(int argc, char **argv) * { * if (!ecore_init()) * { * printf("ERROR: Cannot init Ecore!\n"); * return -1; * } * ecore_main_loop_begin(); * ecore_shutdown(); * } * @endcode */ EAPI int ecore_init(void) { if (++_ecore_init_count != 1) return _ecore_init_count; #ifdef HAVE_LOCALE_H setlocale(LC_CTYPE, ""); #endif /* if (strcmp(nl_langinfo(CODESET), "UTF-8")) { WRN("Not a utf8 locale!"); } */ #ifdef HAVE_EVIL if (!evil_init()) return --_ecore_init_count; #endif if (!eina_init()) goto shutdown_evil; _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; } if (getenv("ECORE_FPS_DEBUG")) _ecore_fps_debug = 1; if (_ecore_fps_debug) _ecore_fps_debug_init(); _ecore_main_loop_init(); _ecore_signal_init(); _ecore_thread_init(); _ecore_exe_init(); _ecore_glib_init(); _ecore_job_init(); _ecore_time_init(); eina_lock_new(&_thread_safety); 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_id_lock); eina_lock_new(&_ecore_main_loop_lock); #if HAVE_MALLINFO if (getenv("ECORE_MEM_STAT")) { _ecore_memory_pid = getpid(); ecore_animator_add(_ecore_memory_statistic, NULL); } #endif #if defined(GLIB_INTEGRATION_ALWAYS) if (_ecore_glib_always_integrate) ecore_main_loop_glib_integrate(); #endif return _ecore_init_count; shutdown_log_dom: eina_shutdown(); shutdown_evil: #ifdef HAVE_EVIL evil_shutdown(); #endif return --_ecore_init_count; } /** * Shut down connections, signal handlers sockets etc. * * This function shuts down all things set up in ecore_init() and cleans up all * event queues, handlers, filters, timers, idlers, idle enterers/exiters * etc. set up after ecore_init() was called. * * Do not call this function from any callback that may be called from the main * loop, as the main loop will then fall over and not function properly. */ EAPI int ecore_shutdown(void) { /* * take a lock here because _ecore_event_shutdown() does callbacks */ _ecore_lock(); if (--_ecore_init_count != 0) goto unlock; ecore_pipe_del(_thread_call); 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); if (_ecore_fps_debug) _ecore_fps_debug_shutdown(); _ecore_poller_shutdown(); _ecore_animator_shutdown(); _ecore_glib_shutdown(); _ecore_job_shutdown(); _ecore_thread_shutdown(); _ecore_exe_shutdown(); _ecore_idle_enterer_shutdown(); _ecore_idle_exiter_shutdown(); _ecore_idler_shutdown(); _ecore_timer_shutdown(); _ecore_event_shutdown(); _ecore_main_shutdown(); _ecore_signal_shutdown(); _ecore_main_loop_shutdown(); #if HAVE_MALLINFO 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); } #endif eina_log_domain_unregister(_ecore_log_dom); _ecore_log_dom = -1; eina_shutdown(); #ifdef HAVE_EVIL evil_shutdown(); #endif unlock: _ecore_unlock(); return _ecore_init_count; } /** * @} */ static int wakeup = 42; 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; _ecore_main_loop_thread_safe_call(order); eina_lock_take(&order->m); 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 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; eina_lock_take(&order->m); _ecore_main_loop_thread_safe_call(order); while (order->current_id != _thread_id) eina_condition_wait(&order->c); eina_lock_release(&order->m); eina_main_loop_define(); _thread_loop = 1; return EINA_TRUE; } 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; current_id = _thread_id; eina_lock_take(&_thread_mutex); eina_condition_broadcast(&_thread_cond); eina_lock_release(&_thread_mutex); eina_lock_take(&_thread_feedback_mutex); while (current_id == _thread_id) eina_condition_wait(&_thread_feedback_cond); eina_lock_release(&_thread_feedback_mutex); return 0; } EAPI void ecore_print_warning(const char *function, const char *sparam) { 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) { 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" " 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: 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 ""; }; } /* 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; #ifndef HAVE_EVIL tmp = "/tmp"; #else tmp = evil_tmpdir_get (); #endif /* HAVE_EVIL */ pid = (int)getpid(); snprintf(buf, sizeof(buf), "%s/.ecore_fps_debug-%i", tmp, pid); _ecore_fps_debug_fd = open(buf, O_CREAT | O_TRUNC | O_RDWR, 0644); if (_ecore_fps_debug_fd < 0) { unlink(buf); _ecore_fps_debug_fd = open(buf, O_CREAT | 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]; const char *tmp; int pid; #ifndef HAVE_EVIL tmp = "/tmp"; #else tmp = (char *)evil_tmpdir_get (); #endif /* HAVE_EVIL */ pid = (int)getpid(); snprintf(buf, sizeof(buf), "%s/.ecore_fps_debug-%i", tmp, pid); 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)) { 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; } } #if HAVE_MALLINFO static Eina_Bool _ecore_memory_statistic(__UNUSED__ void *data) { 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); 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); } static void _thread_callback(void *data __UNUSED__, void *buffer __UNUSED__, unsigned int nbyte __UNUSED__) { 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); _thread_id = call->current_id; eina_condition_broadcast(&call->c); eina_condition_wait(&_thread_cond); eina_lock_release(&_thread_mutex); _thread_id = -1; eina_main_loop_define(); eina_lock_take(&_thread_feedback_mutex); 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_condition_broadcast(&call->c); } else { call->cb.async(call->data); free(call); } } }