diff --git a/src/bin/e.h b/src/bin/e.h index dd8c35b8f..62a8e61fe 100644 --- a/src/bin/e.h +++ b/src/bin/e.h @@ -205,6 +205,7 @@ extern E_API Eina_Bool e_precache_end; extern E_API Eina_Bool x_fatal; extern E_API Eina_Bool after_restart; +extern E_API Eina_Bool e_main_loop_running; extern EINTERN const char *e_first_frame; extern EINTERN double e_first_frame_start_time; diff --git a/src/bin/e_config.c b/src/bin/e_config.c index 3e5d68f76..201853676 100644 --- a/src/bin/e_config.c +++ b/src/bin/e_config.c @@ -9,7 +9,6 @@ static int _e_config_revisions = 9; static void _e_config_save_cb(void *data); static void _e_config_free(E_Config *cfg); static Eina_Bool _e_config_cb_timer(void *data); -static int _e_config_eet_close_handle(Eet_File *ef, char *file); /* local subsystem globals */ static int _e_config_save_block = 0; @@ -49,7 +48,10 @@ E_API int E_EVENT_CONFIG_ICON_THEME = 0; E_API int E_EVENT_CONFIG_MODE_CHANGED = 0; E_API int E_EVENT_CONFIG_LOADED = 0; -static E_Dialog *_e_config_error_dialog = NULL; +static Eina_Lock _e_config_pending_files_lock; +static Eina_Hash *_e_config_pending_files = NULL; +static Eina_Thread_Queue *_e_config_thread_thq = NULL; + static Eina_List *handlers = NULL; typedef struct _E_Color_Class @@ -60,6 +62,19 @@ typedef struct _E_Color_Class int r3, g3, b3, a3; } E_Color_Class; +typedef enum +{ + E_CONFIG_SAVE_THREAD_SAVE, + E_CONFIG_SAVE_THREAD_QUIT +} E_Config_Save_Thread_Message_Type; + +typedef struct _E_Config_Save_Thread_Message +{ + Eina_Thread_Queue_Msg head; + E_Config_Save_Thread_Message_Type type; + char *path, *destpath; +} E_Config_Save_Thread_Message; + static Eina_Bool _e_config_cb_efreet_cache_update(void *data EINA_UNUSED, int type EINA_UNUSED, void *ev EINA_UNUSED) { @@ -78,13 +93,6 @@ _e_config_cb_efreet_cache_update(void *data EINA_UNUSED, int type EINA_UNUSED, v } -static void -_e_config_error_dialog_cb_delete(void *dia) -{ - if (dia == _e_config_error_dialog) - _e_config_error_dialog = NULL; -} - static const char * _e_config_profile_name_get(Eet_File *ef) { @@ -117,6 +125,223 @@ _e_config_profile_name_get(Eet_File *ef) return s; } +static Eet_File * +_e_config_pending_file_find(const char *path) +{ + Eet_File *ef, *ef2; + + eina_lock_take(&_e_config_pending_files_lock); + ef = eina_hash_find(_e_config_pending_files, path); + if (!ef) + { + eina_lock_release(&_e_config_pending_files_lock); + // do this unlocked as it could block for a while + ef = eet_open(path, EET_FILE_MODE_WRITE); + eina_lock_take(&_e_config_pending_files_lock); + if (ef) + { + // handle race due to the above unlock and lock - should be + // super rare... + ef2 = eina_hash_find(_e_config_pending_files, path); + if (ef2) + { + eina_hash_del(_e_config_pending_files, path, ef2); + eet_close(ef2); + } + eina_hash_add(_e_config_pending_files, path, ef); + } + else + printf("CF: Error: file find %s - create new fail\n", path); + } + eina_lock_release(&_e_config_pending_files_lock); + return ef; +} + +static Eina_Bool +_e_config_pending_file_del(const char *path) +{ + Eet_File *ef; + Eina_Bool ok = EINA_FALSE; + + eina_lock_take(&_e_config_pending_files_lock); + ef = eina_hash_find(_e_config_pending_files, path); + if (ef) + { + Eet_Error err; + const char *erstr = ""; + + eina_hash_del(_e_config_pending_files, path, ef); + eina_lock_release(&_e_config_pending_files_lock); + err = eet_close(ef); + switch (err) + { + case EET_ERROR_NONE: + ok = EINA_TRUE; + /* all good - no error */ + break; + case EET_ERROR_BAD_OBJECT: + erstr = _("The EET file handle is bad."); + break; + case EET_ERROR_EMPTY: + erstr = _("The file data is empty."); + break; + case EET_ERROR_NOT_WRITABLE: + erstr = _("The file is not writable. Perhaps the disk is read-onlyor you lost permissions to your files."); + break; + case EET_ERROR_OUT_OF_MEMORY: + erstr = _("Memory ran out while preparing the write.Please free up memory."); + break; + case EET_ERROR_WRITE_ERROR: + erstr = _("This is a generic error."); + break; + case EET_ERROR_WRITE_ERROR_FILE_TOO_BIG: + erstr = _("The settings file is too large.It should be very small (a few hundred KB at most)."); + break; + case EET_ERROR_WRITE_ERROR_IO_ERROR: + erstr = _("You have I/O errors on the disk.Maybe it needs replacing?"); + break; + case EET_ERROR_WRITE_ERROR_OUT_OF_SPACE: + erstr = _("You ran out of space while writing the file."); + break; + case EET_ERROR_WRITE_ERROR_FILE_CLOSED: + erstr = _("The file was closed while writing."); + break; + case EET_ERROR_MMAP_FAILED: + erstr = _("Memory-mapping (mmap) of the file failed."); + break; + case EET_ERROR_X509_ENCODING_FAILED: + erstr = _("X509 Encoding failed."); + break; + case EET_ERROR_SIGNATURE_FAILED: + erstr = _("Signature failed."); + break; + case EET_ERROR_INVALID_SIGNATURE: + erstr = _("The signature was invalid."); + break; + case EET_ERROR_NOT_SIGNED: + erstr = _("Not signed."); + break; + case EET_ERROR_NOT_IMPLEMENTED: + erstr = _("Feature not implemented."); + break; + case EET_ERROR_PRNG_NOT_SEEDED: + erstr = _("PRNG was not seeded."); + break; + case EET_ERROR_ENCRYPT_FAILED: + erstr = _("Encryption failed."); + break; + case EET_ERROR_DECRYPT_FAILED: + erstr = _("Decryption failed."); + break; + default: /* if we get here eet added errors we don't know */ + erstr = _("The error is unknown to Enlightenment."); + break; + } + if (!ok) printf("CF: Write Error: %s\n", erstr); + } + else + eina_lock_release(&_e_config_pending_files_lock); + return ok; +} + +static void +_e_config_save_thread_main(void *data EINA_UNUSED, Ecore_Thread *eth) +{ + E_Config_Save_Thread_Message *msg; + void *ref; + Eina_Bool run = EINA_TRUE; + + while (run) + { + msg = eina_thread_queue_wait(_e_config_thread_thq, &ref); + switch (msg->type) + { + case E_CONFIG_SAVE_THREAD_SAVE: + if (_e_config_pending_file_del(msg->path)) + { + Eina_Bool ret = EINA_TRUE; + + if (_e_config_revisions > 0) + { + int i; + char bsrc[4096], bdst[4096]; + + for (i = _e_config_revisions; i > 1; i--) + { + snprintf(bsrc, sizeof(bsrc), "%s.%i", msg->destpath, i - 1); + snprintf(bdst, sizeof(bdst), "%s.%i", msg->destpath, i); + if ((ecore_file_exists(bsrc)) && + (ecore_file_size(bsrc))) + { + ret = ecore_file_mv(bsrc, bdst); + if (!ret) + { + printf("CF: Error: Can't rename %s to %s\n", bsrc, bdst); + break; + } + } + } + if (ret) + { + snprintf(bdst, sizeof(bdst), "%s.1", msg->destpath); + ecore_file_mv(msg->destpath, bdst); + } + } + if (!ecore_file_mv(msg->path, msg->destpath)) + printf("CF: Error: Can't rename %s to %s\n", msg->path, msg->destpath); + // open another tmp file now in a thread ready for writes next + // time. This can just dangle - no harm + _e_config_pending_file_find(msg->path); + ecore_thread_feedback(eth, strdup(msg->destpath)); + } + free(msg->path); + free(msg->destpath); + break; + case E_CONFIG_SAVE_THREAD_QUIT: + run = EINA_FALSE; + break; + default: + break; + } + eina_thread_queue_wait_done(_e_config_thread_thq, ref); + } +} + +static void +_e_config_save_thread_notify(void *data EINA_UNUSED, Ecore_Thread *eth EINA_UNUSED, void *msgdata) +{ + char *path = msgdata; + free(path); +} + +static void +_e_config_save_thread_end(void *data EINA_UNUSED, Ecore_Thread *eth EINA_UNUSED) +{ + ecore_main_loop_quit(); +} + +static void +_e_config_save_thread_cancel(void *data EINA_UNUSED, Ecore_Thread *eth EINA_UNUSED) +{ + ecore_main_loop_quit(); +} + +static void +_e_config_save_thread_send(E_Config_Save_Thread_Message_Type type, const char *path, const char *destpath) +{ + E_Config_Save_Thread_Message *msg; + void *ref; + + msg = eina_thread_queue_send + (_e_config_thread_thq, sizeof(E_Config_Save_Thread_Message), &ref); + msg->type = type; + if (path) msg->path = strdup(path); + else msg->path = NULL; + if (destpath) msg->destpath = strdup(destpath); + else msg->destpath = NULL; + eina_thread_queue_send_done(_e_config_thread_thq, ref); +} + static void _e_config_edd_shutdown(void) { @@ -778,6 +1003,16 @@ e_config_init(void) E_EVENT_CONFIG_MODE_CHANGED = ecore_event_type_new(); E_EVENT_CONFIG_LOADED = ecore_event_type_new(); + eina_lock_new(&_e_config_pending_files_lock); + _e_config_pending_files = eina_hash_string_superfast_new(NULL); + + _e_config_thread_thq = eina_thread_queue_new(); + ecore_thread_feedback_run(_e_config_save_thread_main, + _e_config_save_thread_notify, + _e_config_save_thread_end, + _e_config_save_thread_cancel, + NULL, EINA_TRUE); + /* if environment var set - use this profile name */ _e_config_profile = eina_stringshare_add(getenv("E_CONF_PROFILE")); @@ -1752,6 +1987,12 @@ e_config_save_flush(void) _e_config_save_defer = NULL; _e_config_save_cb(NULL); } + if (!e_main_loop_running) + { + _e_config_save_thread_send(E_CONFIG_SAVE_THREAD_QUIT, NULL, NULL); + // wait for save thread to exit... + ecore_main_loop_begin(); + } } E_API void @@ -1962,39 +2203,6 @@ e_config_domain_system_load(const char *domain, E_Config_DD *edd) return data; } -static void -_e_config_mv_error(const char *from, const char *to) -{ - E_Dialog *dia; - char buf[8192]; - - if (_e_config_error_dialog) return; - - dia = e_dialog_new(NULL, "E", "_sys_error_logout_slow"); - EINA_SAFETY_ON_NULL_RETURN(dia); - - e_dialog_title_set(dia, _("Enlightenment Settings Write Problems")); - e_dialog_icon_set(dia, "dialog-error", 64); - snprintf(buf, sizeof(buf), - _("Enlightenment has had an error while moving config files" - "from:" - "%s" - "" - "to:" - "%s" - "" - "The rest of the write has been aborted for safety."), - from, to); - e_dialog_text_set(dia, buf); - e_dialog_button_add(dia, _("OK"), NULL, NULL, NULL); - e_dialog_button_focus_num(dia, 0); - elm_win_center(dia->win, 1, 1); - e_object_del_attach_func_set(E_OBJECT(dia), - _e_config_error_dialog_cb_delete); - e_dialog_show(dia); - _e_config_error_dialog = dia; -} - E_API int e_config_profile_save(void) { @@ -2015,48 +2223,12 @@ e_config_profile_save(void) e_user_dir_concat_static(buf, "config/profile.cfg"); e_user_dir_concat_static(buf2, "config/profile.cfg.tmp"); - ef = eet_open(buf2, EET_FILE_MODE_WRITE); + ef = _e_config_pending_file_find(buf2); if (ef) { ok = eet_write(ef, "config", _e_config_profile, strlen(_e_config_profile), 0); - if (_e_config_eet_close_handle(ef, buf2)) - { - Eina_Bool ret = EINA_TRUE; - - if (_e_config_revisions > 0) - { - int i; - char bsrc[4096], bdst[4096]; - - for (i = _e_config_revisions; i > 1; i--) - { - e_user_dir_snprintf(bsrc, sizeof(bsrc), "config/profile.%i.cfg", i - 1); - e_user_dir_snprintf(bdst, sizeof(bdst), "config/profile.%i.cfg", i); - if ((ecore_file_exists(bsrc)) && - (ecore_file_size(bsrc))) - { - ret = ecore_file_mv(bsrc, bdst); - if (!ret) - { - _e_config_mv_error(bsrc, bdst); - break; - } - } - } - if (ret) - { - e_user_dir_snprintf(bsrc, sizeof(bsrc), "config/profile.cfg"); - e_user_dir_snprintf(bdst, sizeof(bdst), "config/profile.1.cfg"); - ecore_file_mv(bsrc, bdst); -// if (!ret) -// _e_config_mv_error(bsrc, bdst); - } - } - ret = ecore_file_mv(buf2, buf); - if (!ret) _e_config_mv_error(buf2, buf); - } - ecore_file_unlink(buf2); + _e_config_save_thread_send(E_CONFIG_SAVE_THREAD_SAVE, buf2, buf); } return ok; } @@ -2076,7 +2248,7 @@ e_config_domain_save(const char *domain, E_Config_DD *edd, const void *data) { Eet_File *ef; char buf[4096], buf2[4096]; - int ok = 0, ret; + int ok = 0; size_t len, len2; if (_e_config_save_block) return 0; @@ -2101,36 +2273,12 @@ e_config_domain_save(const char *domain, E_Config_DD *edd, const void *data) memcpy(buf2, buf, len); memcpy(buf2 + len, ".tmp", sizeof(".tmp")); - ef = eet_open(buf2, EET_FILE_MODE_WRITE); + ef = _e_config_pending_file_find(buf2); if (ef) { - ok = eet_data_write(ef, edd, "config", data, 1); - if (_e_config_eet_close_handle(ef, buf2)) - { - if (_e_config_revisions > 0) - { - int i; - char bsrc[4096], bdst[4096]; - - for (i = _e_config_revisions; i > 1; i--) - { - e_user_dir_snprintf(bsrc, sizeof(bsrc), "config/%s/%s.%i.cfg", _e_config_profile, domain, i - 1); - e_user_dir_snprintf(bdst, sizeof(bdst), "config/%s/%s.%i.cfg", _e_config_profile, domain, i); - if ((ecore_file_exists(bsrc)) && - (ecore_file_size(bsrc))) - { - ecore_file_mv(bsrc, bdst); - } - } - e_user_dir_snprintf(bsrc, sizeof(bsrc), "config/%s/%s.cfg", _e_config_profile, domain); - e_user_dir_snprintf(bdst, sizeof(bdst), "config/%s/%s.1.cfg", _e_config_profile, domain); - ecore_file_mv(bsrc, bdst); - } - ret = ecore_file_mv(buf2, buf); - if (!ret) - ERR("*** Error saving config. ***"); - } - ecore_file_unlink(buf2); + ok = eet_data_write(ef, edd, "config", data, + EET_COMPRESSION_SUPERFAST); + _e_config_save_thread_send(E_CONFIG_SAVE_THREAD_SAVE, buf2, buf); } return ok; } @@ -2524,135 +2672,3 @@ _e_config_cb_timer(void *data) e_util_dialog_show(_("Settings Upgraded"), "%s", (char *)data); return 0; } - -static int -_e_config_eet_close_handle(Eet_File *ef, char *file) -{ - Eet_Error err; - char *erstr = NULL; - - err = eet_close(ef); - switch (err) - { - case EET_ERROR_NONE: - /* all good - no error */ - break; - - case EET_ERROR_BAD_OBJECT: - erstr = _("The EET file handle is bad."); - break; - - case EET_ERROR_EMPTY: - erstr = _("The file data is empty."); - break; - - case EET_ERROR_NOT_WRITABLE: - erstr = _("The file is not writable. Perhaps the disk is read-onlyor you lost permissions to your files."); - break; - - case EET_ERROR_OUT_OF_MEMORY: - erstr = _("Memory ran out while preparing the write.Please free up memory."); - break; - - case EET_ERROR_WRITE_ERROR: - erstr = _("This is a generic error."); - break; - - case EET_ERROR_WRITE_ERROR_FILE_TOO_BIG: - erstr = _("The settings file is too large.It should be very small (a few hundred KB at most)."); - break; - - case EET_ERROR_WRITE_ERROR_IO_ERROR: - erstr = _("You have I/O errors on the disk.Maybe it needs replacing?"); - break; - - case EET_ERROR_WRITE_ERROR_OUT_OF_SPACE: - erstr = _("You ran out of space while writing the file."); - break; - - case EET_ERROR_WRITE_ERROR_FILE_CLOSED: - erstr = _("The file was closed while writing."); - break; - - case EET_ERROR_MMAP_FAILED: - erstr = _("Memory-mapping (mmap) of the file failed."); - break; - - case EET_ERROR_X509_ENCODING_FAILED: - erstr = _("X509 Encoding failed."); - break; - - case EET_ERROR_SIGNATURE_FAILED: - erstr = _("Signature failed."); - break; - - case EET_ERROR_INVALID_SIGNATURE: - erstr = _("The signature was invalid."); - break; - - case EET_ERROR_NOT_SIGNED: - erstr = _("Not signed."); - break; - - case EET_ERROR_NOT_IMPLEMENTED: - erstr = _("Feature not implemented."); - break; - - case EET_ERROR_PRNG_NOT_SEEDED: - erstr = _("PRNG was not seeded."); - break; - - case EET_ERROR_ENCRYPT_FAILED: - erstr = _("Encryption failed."); - break; - - case EET_ERROR_DECRYPT_FAILED: - erstr = _("Decryption failed."); - break; - - default: /* if we get here eet added errors we don't know */ - erstr = _("The error is unknown to Enlightenment."); - break; - } - if (erstr) - { - /* delete any partially-written file */ - ecore_file_unlink(file); - /* only show dialog for first error - further ones are likely */ - /* more of the same error */ - if (!_e_config_error_dialog) - { - E_Dialog *dia; - - dia = e_dialog_new(NULL, "E", "_sys_error_logout_slow"); - if (dia) - { - char buf[8192]; - - e_dialog_title_set(dia, _("Enlightenment Settings Write Problems")); - e_dialog_icon_set(dia, "dialog-error", 64); - snprintf(buf, sizeof(buf), - _("Enlightenment has had an error while writing" - "its config file." - "%s" - "" - "The file where the error occurred was:" - "%s" - "" - "This file has been deleted to avoid corrupt data."), - erstr, file); - e_dialog_text_set(dia, buf); - e_dialog_button_add(dia, _("OK"), NULL, NULL, NULL); - e_dialog_button_focus_num(dia, 0); - elm_win_center(dia->win, 1, 1); - e_object_del_attach_func_set(E_OBJECT(dia), - _e_config_error_dialog_cb_delete); - e_dialog_show(dia); - _e_config_error_dialog = dia; - } - } - return 0; - } - return 1; -} - diff --git a/src/bin/e_main.c b/src/bin/e_main.c index 6ccb1f53d..6a9eab315 100644 --- a/src/bin/e_main.c +++ b/src/bin/e_main.c @@ -113,6 +113,7 @@ E_API Eina_Bool stopping = EINA_FALSE; E_API Eina_Bool restart = EINA_FALSE; E_API Eina_Bool e_nopause = EINA_FALSE; E_API Eina_Bool after_restart = EINA_FALSE; +E_API Eina_Bool e_main_loop_running = EINA_FALSE; EINTERN const char *e_first_frame = NULL; EINTERN double e_first_frame_start_time = -1; @@ -1087,9 +1088,13 @@ main(int argc, char **argv) TS("MAIN LOOP AT LAST"); if (!setjmp(x_fatal_buff)) - ecore_main_loop_begin(); + { + e_main_loop_running = EINA_TRUE; + ecore_main_loop_begin(); + } else CRI("FATAL: X Died. Connection gone. Abbreviated Shutdown\n"); + e_main_loop_running = EINA_FALSE; inloop = EINA_FALSE; stopping = EINA_TRUE;