summaryrefslogtreecommitdiff
path: root/src/bin/efreet/efreetd_cache.c
diff options
context:
space:
mode:
authorCarsten Haitzler (Rasterman) <raster@rasterman.com>2015-04-12 21:55:28 +0900
committerCarsten Haitzler (Rasterman) <raster@rasterman.com>2015-04-13 09:09:42 +0900
commitcd2b3c228dab0bc89ca84e414db6ee6c70b75533 (patch)
treed183bd6de97ecbe4c5cc90ca00ac03602cf8c652 /src/bin/efreet/efreetd_cache.c
parent44fe8b987ee692e40c111b441569c96eca14d3b1 (diff)
efreetd - startup slowness fix for recursing directories
this fixes major slowness in things like enlightenment login. when enlightenment starts, efreetd is started. efreetd takes a long time to start because it is scanning lots of directories recursively. it is early in code freeze, so i think this can go in as a fix, even though it is more of an optimization, but as such it has a fairly major speed impact, so i consider this fixing a core performance problem for things like logging in. The solution is a cache file containing all the sub directories in a directory tree. we still have to stat every directory, but this avoids a lot of stating of all files as well and avoids any readirs etc. so it is much faster. on an ssd this comes out to 4 times faster for efreetd to start up. on an hdd it's about 2x faster to start uncached. this should bea good fix for startup times - on my systems thats a 1 second speedup on ssd (out of about 8 seconds boot time) and 3 seconds on hdd (out of about 39 seconds boot). so w save 1 and 3 seconds respecively in boot + login. now can we get this to 0 or close? that's a matter of designing something like a deferred scan + monitor add so we wait until "startup is done" then set up in the bg for a bit. that might shave another 3 seconds off boot time for hdd's but for ssd's wil barely blip (maybe save 0.1 sec). so leave that till normal dev mode. @fix
Diffstat (limited to 'src/bin/efreet/efreetd_cache.c')
-rw-r--r--src/bin/efreet/efreetd_cache.c322
1 files changed, 282 insertions, 40 deletions
diff --git a/src/bin/efreet/efreetd_cache.c b/src/bin/efreet/efreetd_cache.c
index 35be03d0c0..21c21b69a8 100644
--- a/src/bin/efreet/efreetd_cache.c
+++ b/src/bin/efreet/efreetd_cache.c
@@ -5,7 +5,7 @@
5#include <Eina.h> 5#include <Eina.h>
6#include <Ecore.h> 6#include <Ecore.h>
7#include <Ecore_File.h> 7#include <Ecore_File.h>
8 8#include <Eet.h>
9#include "efreetd.h" 9#include "efreetd.h"
10#include "efreetd_dbus.h" 10#include "efreetd_dbus.h"
11 11
@@ -40,12 +40,233 @@ static Eina_Bool icon_flush = EINA_FALSE;
40static Eina_Bool desktop_queue = EINA_FALSE; 40static Eina_Bool desktop_queue = EINA_FALSE;
41static Eina_Bool icon_queue = EINA_FALSE; 41static Eina_Bool icon_queue = EINA_FALSE;
42 42
43static void desktop_changes_monitor_add(const char *path);
44
45static void icon_changes_listen(void); 43static void icon_changes_listen(void);
46static void desktop_changes_listen(void); 44static void desktop_changes_listen(void);
47 45
48/* internal */ 46/* internal */
47typedef struct _Subdir_Cache Subdir_Cache;
48typedef struct _Subdir_Cache_Dir Subdir_Cache_Dir;
49
50struct _Subdir_Cache
51{
52 Eina_Hash *dirs;
53};
54
55struct _Subdir_Cache_Dir
56{
57 unsigned long long dev;
58 unsigned long long ino;
59 unsigned long long mode;
60 unsigned long long uid;
61 unsigned long long gid;
62 unsigned long long size;
63 unsigned long long mtim;
64 unsigned long long ctim;
65 const char **dirs;
66 unsigned int dirs_count;
67};
68
69static Eet_Data_Descriptor *subdir_edd = NULL;
70static Eet_Data_Descriptor *subdir_dir_edd = NULL;
71static Subdir_Cache *subdir_cache = NULL;
72static Eina_Bool subdir_need_save = EINA_FALSE;
73
74static void
75subdir_cache_dir_free(Subdir_Cache_Dir *cd)
76{
77 unsigned int i;
78
79 if (!cd) return;
80 if (cd->dirs)
81 {
82 for (i = 0; i < cd->dirs_count; i++)
83 eina_stringshare_del(cd->dirs[i]);
84 free(cd->dirs);
85 }
86 free(cd);
87}
88
89static void *
90subdir_cache_hash_add(void *hash, const char *key, void *data)
91{
92 if (!hash) hash = eina_hash_string_superfast_new(EINA_FREE_CB(subdir_cache_dir_free));
93 if (!hash) return NULL;
94 eina_hash_add(hash, key, data);
95 return hash;
96}
97
98static void
99subdir_cache_init(void)
100{
101 char buf[PATH_MAX];
102 Eet_Data_Descriptor_Class eddc;
103 Eet_File *ef;
104
105 // set up data codecs for subdirs in memory
106 eet_eina_stream_data_descriptor_class_set(&eddc, sizeof(Subdir_Cache_Dir), "D", sizeof(Subdir_Cache_Dir));
107 EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Subdir_Cache_Dir);
108 subdir_dir_edd = eet_data_descriptor_stream_new(&eddc);
109 EET_DATA_DESCRIPTOR_ADD_BASIC(subdir_dir_edd, Subdir_Cache_Dir, "0", dev, EET_T_ULONG_LONG);
110 EET_DATA_DESCRIPTOR_ADD_BASIC(subdir_dir_edd, Subdir_Cache_Dir, "1", ino, EET_T_ULONG_LONG);
111 EET_DATA_DESCRIPTOR_ADD_BASIC(subdir_dir_edd, Subdir_Cache_Dir, "2", mode, EET_T_ULONG_LONG);
112 EET_DATA_DESCRIPTOR_ADD_BASIC(subdir_dir_edd, Subdir_Cache_Dir, "3", uid, EET_T_ULONG_LONG);
113 EET_DATA_DESCRIPTOR_ADD_BASIC(subdir_dir_edd, Subdir_Cache_Dir, "4", gid, EET_T_ULONG_LONG);
114 EET_DATA_DESCRIPTOR_ADD_BASIC(subdir_dir_edd, Subdir_Cache_Dir, "5", size, EET_T_ULONG_LONG);
115 EET_DATA_DESCRIPTOR_ADD_BASIC(subdir_dir_edd, Subdir_Cache_Dir, "6", mtim, EET_T_ULONG_LONG);
116 EET_DATA_DESCRIPTOR_ADD_BASIC(subdir_dir_edd, Subdir_Cache_Dir, "7", ctim, EET_T_ULONG_LONG);
117 EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(subdir_dir_edd, Subdir_Cache_Dir, "d", dirs);
118
119 eet_eina_stream_data_descriptor_class_set(&eddc, sizeof(Subdir_Cache), "C", sizeof(Subdir_Cache));
120 eddc.func.hash_add = subdir_cache_hash_add;
121 subdir_edd = eet_data_descriptor_stream_new(&eddc);
122 EET_DATA_DESCRIPTOR_ADD_HASH(subdir_edd, Subdir_Cache, "dirs", dirs, subdir_dir_edd);
123
124 // load subdirs from the cache file
125 snprintf(buf, sizeof(buf), "%s/efreet/subdirs_%s.eet", efreet_cache_home_get(), efreet_hostname_get());
126 ef = eet_open(buf, EET_FILE_MODE_READ);
127 if (ef)
128 {
129 subdir_cache = eet_data_read(ef, subdir_edd, "subdirs");
130 eet_close(ef);
131 }
132 // if we don't have a decoded subdir cache - allocate one
133 if (!subdir_cache) subdir_cache = calloc(1, sizeof(Subdir_Cache));
134 if (!subdir_cache) ERR("Cannot allocate subdir cache in memory");
135 // if we don't have a hash in the subdir cache - allocate it
136 if (!subdir_cache->dirs)
137 subdir_cache->dirs = eina_hash_string_superfast_new(EINA_FREE_CB(subdir_cache_dir_free));
138}
139
140static void
141subdir_cache_shutdown(void)
142{
143 // free up in-memory subdir scan info - don't need it anymore
144 if (subdir_cache)
145 {
146 if (subdir_cache->dirs) eina_hash_free(subdir_cache->dirs);
147 free(subdir_cache);
148 }
149 eet_data_descriptor_free(subdir_dir_edd);
150 eet_data_descriptor_free(subdir_edd);
151 subdir_cache = NULL;
152 subdir_dir_edd = NULL;
153 subdir_edd = NULL;
154}
155
156static void
157subdir_cache_save(void)
158{
159 char buf[PATH_MAX], buf2[PATH_MAX];
160 int tmpfd;
161 mode_t um;
162 Eet_File *ef;
163
164 // only if subdirs need saving... and we have subdirs.
165 if (!subdir_need_save) return;
166 if (!subdir_cache) return;
167 if (!subdir_cache->dirs) return;
168
169 // save to tmp file first
170 snprintf(buf2, sizeof(buf2), "%s/efreet/subdirs_%s.eet.XXXXXX", efreet_cache_home_get(), efreet_hostname_get());
171 um = umask(0077);
172 tmpfd = mkstemp(buf2);
173 umask(um);
174 if (tmpfd < 0) return;
175
176 // write out eetf ile to tmp file
177 ef = eet_open(buf2, EET_FILE_MODE_WRITE);
178 eet_data_write(ef, subdir_edd, "subdirs", subdir_cache, EET_COMPRESSION_SUPERFAST);
179 eet_close(ef);
180
181 // atomically rename subdirs file on top from tmp file
182 snprintf(buf, sizeof(buf), "%s/efreet/subdirs_%s.eet", efreet_cache_home_get(), efreet_hostname_get());
183 if (rename(buf2, buf) < 0) ERR("Can't save subdir cache %s", buf);
184 // we dont need saving anymore - we just did
185 subdir_need_save = EINA_FALSE;
186}
187
188static Subdir_Cache_Dir *
189subdir_cache_get(const struct stat *st, const char *path)
190{
191 Eina_Iterator *it;
192 Eina_File_Direct_Info *info;
193 Subdir_Cache_Dir *cd;
194 Eina_List *files = NULL;
195 int i = 0;
196 const char *file;
197
198 // if no subdir cache at all - return null
199 if (!subdir_cache) return NULL;
200 if (!subdir_cache->dirs) return NULL;
201
202 // if found but something invalid in stored stat info...
203 cd = eina_hash_find(subdir_cache->dirs, path);
204 if ((cd) &&
205 ((cd->dev != (unsigned long long)st->st_dev) ||
206 (cd->ino != (unsigned long long)st->st_ino) ||
207 (cd->mode != (unsigned long long)st->st_mode) ||
208 (cd->uid != (unsigned long long)st->st_uid) ||
209 (cd->gid != (unsigned long long)st->st_gid) ||
210 (cd->size != (unsigned long long)st->st_size) ||
211 (cd->mtim != (unsigned long long)st->st_mtime) ||
212 (cd->ctim != (unsigned long long)st->st_ctime)))
213 {
214 // delete old node and prepare to scan a new one
215 eina_hash_del(subdir_cache->dirs, path, cd);
216 cd = NULL;
217 }
218 // if cached dir is ok by now - return it
219 if (cd) return cd;
220
221 // we need a new node (fesh or invalid)
222 cd = calloc(1, sizeof(Subdir_Cache_Dir));
223 if (!cd) return NULL;
224
225 // store stat info
226 cd->dev = (unsigned long long)st->st_dev;
227 cd->ino = (unsigned long long)st->st_ino;
228 cd->mode = (unsigned long long)st->st_mode;
229 cd->uid = (unsigned long long)st->st_uid;
230 cd->gid = (unsigned long long)st->st_gid;
231 cd->size = (unsigned long long)st->st_size;
232 cd->mtim = (unsigned long long)st->st_mtime;
233 cd->ctim = (unsigned long long)st->st_ctime;
234
235 // go through content finding directories
236 it = eina_file_stat_ls(path);
237 if (!it) return cd;
238 EINA_ITERATOR_FOREACH(it, info)
239 {
240 // if ., .. or other "hidden" dot files - ignore
241 if (info->path[info->name_start] == '.') continue;
242 // if it's a dir or link to a dir - store it.
243 if (((info->type == EINA_FILE_LNK) && (ecore_file_is_dir(info->path))) ||
244 (info->type == EINA_FILE_DIR))
245 {
246 // store just the name, not the full path
247 files = eina_list_append
248 (files, eina_stringshare_add(info->path + info->name_start));
249 }
250 }
251 eina_iterator_free(it);
252 // now convert our temporary list into an array of stringshare strings
253 cd->dirs_count = eina_list_count(files);
254 if (cd->dirs_count > 0)
255 {
256 cd->dirs = malloc(cd->dirs_count * sizeof(char *));
257 EINA_LIST_FREE(files, file)
258 {
259 cd->dirs[i] = file;
260 i++;
261 }
262 }
263 // add cache dir to hash with full path as key
264 eina_hash_add(subdir_cache->dirs, path, cd);
265 // mark subdirs as needing a save - something changed
266 subdir_need_save = EINA_TRUE;
267 return cd;
268}
269
49static Eina_Bool 270static Eina_Bool
50icon_cache_update_cache_cb(void *data EINA_UNUSED) 271icon_cache_update_cache_cb(void *data EINA_UNUSED)
51{ 272{
@@ -65,6 +286,7 @@ icon_cache_update_cache_cb(void *data EINA_UNUSED)
65 icon_change_monitors = eina_hash_string_superfast_new 286 icon_change_monitors = eina_hash_string_superfast_new
66 (EINA_FREE_CB(ecore_file_monitor_del)); 287 (EINA_FREE_CB(ecore_file_monitor_del));
67 icon_changes_listen(); 288 icon_changes_listen();
289 subdir_cache_save();
68 290
69 /* TODO: Queue if already running */ 291 /* TODO: Queue if already running */
70 snprintf(file, sizeof(file), 292 snprintf(file, sizeof(file),
@@ -121,6 +343,7 @@ desktop_cache_update_cache_cb(void *data EINA_UNUSED)
121 desktop_change_monitors = eina_hash_string_superfast_new 343 desktop_change_monitors = eina_hash_string_superfast_new
122 (EINA_FREE_CB(ecore_file_monitor_del)); 344 (EINA_FREE_CB(ecore_file_monitor_del));
123 desktop_changes_listen(); 345 desktop_changes_listen();
346 subdir_cache_save();
124 347
125 snprintf(file, sizeof(file), 348 snprintf(file, sizeof(file),
126 "%s/efreet/" MODULE_ARCH "/efreet_desktop_cache_create", 349 "%s/efreet/" MODULE_ARCH "/efreet_desktop_cache_create",
@@ -223,29 +446,41 @@ desktop_changes_cb(void *data EINA_UNUSED, Ecore_File_Monitor *em EINA_UNUSED,
223} 446}
224 447
225static void 448static void
226icon_changes_monitor_add(const char *path) 449icon_changes_monitor_add(const struct stat *st, const char *path)
227{ 450{
228 Ecore_File_Monitor *mon; 451 Ecore_File_Monitor *mon;
229 char *realp; 452 char *realp = NULL;
453 const char *monpath = path;
230 454
231 if (eina_hash_find(icon_change_monitors, path)) return; 455 if (eina_hash_find(icon_change_monitors, path)) return;
232 realp = ecore_file_realpath(path); 456 if (S_ISLNK(st->st_mode))
233 if (!realp) return; 457 {
234 mon = ecore_file_monitor_add(realp, icon_changes_cb, NULL); 458 realp = ecore_file_realpath(path);
459 if (!realp) return;
460 monpath = realp;
461 }
462 mon = ecore_file_monitor_add(monpath, icon_changes_cb, NULL);
235 free(realp); 463 free(realp);
236 if (mon) eina_hash_add(icon_change_monitors, path, mon); 464 if (mon) eina_hash_add(icon_change_monitors, path, mon);
237} 465}
238 466
239static void 467static void
240desktop_changes_monitor_add(const char *path) 468desktop_changes_monitor_add(const struct stat *st, const char *path)
241{ 469{
242 Ecore_File_Monitor *mon; 470 Ecore_File_Monitor *mon;
471 char *realp = NULL;
472 const char *monpath = path;
243 473
244 if (eina_hash_find(desktop_change_monitors, path)) return; 474 if (eina_hash_find(desktop_change_monitors, path)) return;
245 /* TODO: Check for symlink and monitor the real path */ 475 if (S_ISLNK(st->st_mode))
246 mon = ecore_file_monitor_add(path, desktop_changes_cb, NULL); 476 {
247 if (mon) 477 realp = ecore_file_realpath(path);
248 eina_hash_add(desktop_change_monitors, path, mon); 478 if (!realp) return;
479 monpath = realp;
480 }
481 mon = ecore_file_monitor_add(monpath, desktop_changes_cb, NULL);
482 free(realp);
483 if (mon) eina_hash_add(desktop_change_monitors, path, mon);
249} 484}
250 485
251static int 486static int
@@ -286,8 +521,6 @@ _check_recurse_monitor_sanity(Eina_Inarray *stack, const char *path, unsigned in
286static void 521static void
287icon_changes_listen_recursive(Eina_Inarray *stack, const char *path, Eina_Bool base) 522icon_changes_listen_recursive(Eina_Inarray *stack, const char *path, Eina_Bool base)
288{ 523{
289 Eina_Iterator *it;
290 Eina_File_Direct_Info *info;
291 struct stat st; 524 struct stat st;
292 525
293 if (stat(path, &st) == -1) return; 526 if (stat(path, &st) == -1) return;
@@ -295,7 +528,7 @@ icon_changes_listen_recursive(Eina_Inarray *stack, const char *path, Eina_Bool b
295 if (!_check_recurse_monitor_sanity(stack, path, 8)) return; 528 if (!_check_recurse_monitor_sanity(stack, path, 8)) return;
296 eina_inarray_push(stack, &st); 529 eina_inarray_push(stack, &st);
297 530
298 if ((!ecore_file_is_dir(path)) && (base)) 531 if ((!S_ISDIR(st.st_mode)) && (base))
299 { 532 {
300 // XXX: if it doesn't exist... walk the parent dirs back down 533 // XXX: if it doesn't exist... walk the parent dirs back down
301 // to this path until we find one that doesn't exist, then 534 // to this path until we find one that doesn't exist, then
@@ -305,33 +538,36 @@ icon_changes_listen_recursive(Eina_Inarray *stack, const char *path, Eina_Bool b
305 // monitoring the next specific child dir down until we are 538 // monitoring the next specific child dir down until we are
306 // monitoring the original path again. 539 // monitoring the original path again.
307 } 540 }
308 if (ecore_file_is_dir(path)) icon_changes_monitor_add(path); 541 if (S_ISDIR(st.st_mode))
309 it = eina_file_stat_ls(path);
310 if (!it) goto end;
311 EINA_ITERATOR_FOREACH(it, info)
312 { 542 {
313 if (info->path[info->name_start] == '.') continue; 543 unsigned int i;
314 if (((info->type == EINA_FILE_LNK) && (ecore_file_is_dir(info->path))) || 544 Subdir_Cache_Dir *cd = subdir_cache_get(&st, path);
315 (info->type == EINA_FILE_DIR)) 545 icon_changes_monitor_add(&st, path);
316 icon_changes_listen_recursive(stack, info->path, EINA_FALSE); 546 if (cd)
547 {
548 for (i = 0; i < cd->dirs_count; i++)
549 {
550 char buf[PATH_MAX];
551
552 snprintf(buf, sizeof(buf), "%s/%s", path, cd->dirs[i]);
553 icon_changes_listen_recursive(stack, buf, EINA_FALSE);
554 }
555 }
317 } 556 }
318 eina_iterator_free(it);
319end:
320 eina_inarray_pop(stack); 557 eina_inarray_pop(stack);
321} 558}
322 559
323static void 560static void
324desktop_changes_listen_recursive(Eina_Inarray *stack, const char *path, Eina_Bool base) 561desktop_changes_listen_recursive(Eina_Inarray *stack, const char *path, Eina_Bool base)
325{ 562{
326 Eina_Iterator *it;
327 Eina_File_Direct_Info *info;
328 struct stat st; 563 struct stat st;
329 564
330 if (stat(path, &st) == -1) return; 565 if (stat(path, &st) == -1) return;
331 if (eina_inarray_search(stack, &st, stat_cmp) >= 0) return; 566 if (eina_inarray_search(stack, &st, stat_cmp) >= 0) return;
332 if (!_check_recurse_monitor_sanity(stack, path, 3)) return; 567 if (!_check_recurse_monitor_sanity(stack, path, 3)) return;
333 eina_inarray_push(stack, &st); 568 eina_inarray_push(stack, &st);
334 if ((!ecore_file_is_dir(path)) && (base)) 569
570 if ((!S_ISDIR(st.st_mode)) && (base))
335 { 571 {
336 // XXX: if it doesn't exist... walk the parent dirs back down 572 // XXX: if it doesn't exist... walk the parent dirs back down
337 // to this path until we find one that doesn't exist, then 573 // to this path until we find one that doesn't exist, then
@@ -341,18 +577,22 @@ desktop_changes_listen_recursive(Eina_Inarray *stack, const char *path, Eina_Boo
341 // monitoring the next specific child dir down until we are 577 // monitoring the next specific child dir down until we are
342 // monitoring the original path again. 578 // monitoring the original path again.
343 } 579 }
344 if (ecore_file_is_dir(path)) desktop_changes_monitor_add(path); 580 if (S_ISDIR(st.st_mode))
345 it = eina_file_stat_ls(path);
346 if (!it) goto end;
347 EINA_ITERATOR_FOREACH(it, info)
348 { 581 {
349 if (info->path[info->name_start] == '.') continue; 582 unsigned int i;
350 if (((info->type == EINA_FILE_LNK) && (ecore_file_is_dir(info->path))) || 583 Subdir_Cache_Dir *cd = subdir_cache_get(&st, path);
351 (info->type == EINA_FILE_DIR)) 584 desktop_changes_monitor_add(&st, path);
352 desktop_changes_listen_recursive(stack, info->path, EINA_FALSE); 585 if (cd)
586 {
587 for (i = 0; i < cd->dirs_count; i++)
588 {
589 char buf[PATH_MAX];
590
591 snprintf(buf, sizeof(buf), "%s/%s", path, cd->dirs[i]);
592 desktop_changes_listen_recursive(stack, buf, EINA_FALSE);
593 }
594 }
353 } 595 }
354 eina_iterator_free(it);
355end:
356 eina_inarray_pop(stack); 596 eina_inarray_pop(stack);
357} 597}
358 598
@@ -614,7 +854,7 @@ cache_init(void)
614 854
615 efreet_cache_update = 0; 855 efreet_cache_update = 0;
616 if (!efreet_init()) goto error; 856 if (!efreet_init()) goto error;
617 857 subdir_cache_init();
618 read_lists(); 858 read_lists();
619 /* TODO: Should check if system dirs has changed and handles extra_dirs */ 859 /* TODO: Should check if system dirs has changed and handles extra_dirs */
620 desktop_system_dirs = efreet_default_dirs_get(efreet_data_home_get(), 860 desktop_system_dirs = efreet_default_dirs_get(efreet_data_home_get(),
@@ -627,6 +867,7 @@ cache_init(void)
627 desktop_changes_listen(); 867 desktop_changes_listen();
628 cache_icon_update(EINA_FALSE); 868 cache_icon_update(EINA_FALSE);
629 cache_desktop_update(); 869 cache_desktop_update();
870 subdir_cache_save();
630 871
631 return EINA_TRUE; 872 return EINA_TRUE;
632error: 873error:
@@ -645,6 +886,7 @@ cache_shutdown(void)
645 eina_prefix_free(pfx); 886 eina_prefix_free(pfx);
646 pfx = NULL; 887 pfx = NULL;
647 888
889 subdir_cache_shutdown();
648 efreet_shutdown(); 890 efreet_shutdown();
649 891
650 if (cache_exe_del_handler) ecore_event_handler_del(cache_exe_del_handler); 892 if (cache_exe_del_handler) ecore_event_handler_del(cache_exe_del_handler);