1 /* EIO - EFL data type library
2  * Copyright (C) 2010 Enlightenment Developers:
3  *           Cedric Bail <cedric.bail@free.fr>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library;
17  * if not, see <http://www.gnu.org/licenses/>.
18  */
19 #include <Efreet_Mime.h>
20 #include "eio_private.h"
21 
22 /*============================================================================*
23  *                                  Local                                     *
24  *============================================================================*/
25 
26 static Eio_Version _version = { VMAJ, VMIN, VMIC, VREV };
27 EAPI Eio_Version *eio_version = &_version;
28 
29 /**
30  * @cond LOCAL
31  */
32 
33 /* Progress pool */
34 typedef struct _Eio_Alloc_Pool Eio_Alloc_Pool;
35 
36 struct _Eio_Alloc_Pool
37 {
38    Eina_Lock lock;
39 
40    Eina_Trash *trash;
41    size_t mem_size;
42    int count;
43 };
44 
45 static int _eio_init_count = 0;
46 int _eio_log_dom_global = -1;
47 
48 static Eio_Alloc_Pool progress_pool;
49 static Eio_Alloc_Pool direct_info_pool;
50 static Eio_Alloc_Pool char_pool;
51 static Eio_Alloc_Pool associate_pool;
52 
53 static size_t memory_pool_limit = -1;
54 static size_t memory_pool_usage = 0;
55 static Eina_Spinlock memory_pool_lock;
56 static Eina_Lock memory_pool_mutex;
57 static Eina_Condition memory_pool_cond;
58 static Eina_Bool memory_pool_suspended = 1;
59 static Efl_Io_Manager *io_manager = NULL;
60 
61 static void *
_eio_pool_malloc(Eio_Alloc_Pool * pool)62 _eio_pool_malloc(Eio_Alloc_Pool *pool)
63 {
64    void *result = NULL;
65 
66    if (pool->count)
67      {
68         eina_lock_take(&(pool->lock));
69         result = eina_trash_pop(&pool->trash);
70         if (result) pool->count--;
71         eina_lock_release(&(pool->lock));
72      }
73 
74    if (!result)
75      {
76         result = malloc(pool->mem_size);
77         eina_spinlock_take(&memory_pool_lock);
78         if (result) memory_pool_usage += pool->mem_size;
79         eina_spinlock_release(&memory_pool_lock);
80      }
81    return result;
82 }
83 
84 static void
_eio_pool_free(Eio_Alloc_Pool * pool,void * data)85 _eio_pool_free(Eio_Alloc_Pool *pool, void *data)
86 {
87    if (pool->count >= EIO_PROGRESS_LIMIT)
88      {
89         eina_spinlock_take(&memory_pool_lock);
90         memory_pool_usage -= pool->mem_size;
91         eina_spinlock_release(&memory_pool_lock);
92         free(data);
93 
94         if (memory_pool_limit > 0 &&
95             memory_pool_usage < memory_pool_limit)
96           {
97              eina_lock_take(&(memory_pool_mutex));
98              if (memory_pool_suspended)
99                eina_condition_broadcast(&(memory_pool_cond));
100              eina_lock_release(&(memory_pool_mutex));
101           }
102      }
103    else
104      {
105         eina_lock_take(&(pool->lock));
106         eina_trash_push(&pool->trash, data);
107         pool->count++;
108         eina_lock_release(&(pool->lock));
109      }
110 }
111 
112 /**
113  * @endcond
114  */
115 
116 /*============================================================================*
117  *                                 Global                                     *
118  *============================================================================*/
119 
120 /**
121  * @cond LOCAL
122  */
123 
124 Eio_Progress *
eio_progress_malloc(void)125 eio_progress_malloc(void)
126 {
127    return _eio_pool_malloc(&progress_pool);
128 }
129 
130 void
eio_progress_free(Eio_Progress * data)131 eio_progress_free(Eio_Progress *data)
132 {
133    eina_stringshare_del(data->source);
134    eina_stringshare_del(data->dest);
135 
136    _eio_pool_free(&progress_pool, data);
137 }
138 
139 void
eio_progress_send(Ecore_Thread * thread,Eio_File_Progress * op,long long current,long long max)140 eio_progress_send(Ecore_Thread *thread, Eio_File_Progress *op, long long current, long long max)
141 {
142    Eio_Progress *progress;
143 
144    if (op->progress_cb == NULL)
145      return;
146 
147    progress = eio_progress_malloc();
148    if (!progress) return;
149 
150    progress->op = op->op;
151    progress->current = current;
152    progress->max = max;
153    progress->percent = max ? (float) current * 100.0 / (float) max : 100;
154    progress->source = eina_stringshare_ref(op->source);
155    progress->dest = eina_stringshare_ref(op->dest);
156 
157    ecore_thread_feedback(thread, progress);
158 }
159 
160 Eio_File_Direct_Info *
eio_direct_info_malloc(void)161 eio_direct_info_malloc(void)
162 {
163    return _eio_pool_malloc(&direct_info_pool);
164 }
165 
166 void
eio_direct_info_free(Eio_File_Direct_Info * data)167 eio_direct_info_free(Eio_File_Direct_Info *data)
168 {
169    _eio_pool_free(&direct_info_pool, data);
170 }
171 
172 Eio_File_Char *
eio_char_malloc(void)173 eio_char_malloc(void)
174 {
175   return _eio_pool_malloc(&char_pool);
176 }
177 
178 void
eio_char_free(Eio_File_Char * data)179 eio_char_free(Eio_File_Char *data)
180 {
181   _eio_pool_free(&char_pool, data);
182 }
183 
184 Eio_File_Associate *
eio_associate_malloc(const void * data,Eina_Free_Cb free_cb)185 eio_associate_malloc(const void *data, Eina_Free_Cb free_cb)
186 {
187   Eio_File_Associate *tmp;
188 
189   tmp = _eio_pool_malloc(&associate_pool);
190   if (!tmp) return tmp;
191 
192   tmp->data = (void*) data;
193   tmp->free_cb = free_cb;
194 
195   return tmp;
196 }
197 
198 void
eio_associate_free(void * data)199 eio_associate_free(void *data)
200 {
201   Eio_File_Associate *tmp;
202 
203   if (!data) return;
204 
205   tmp = data;
206   if (tmp->free_cb)
207     tmp->free_cb(tmp->data);
208   _eio_pool_free(&associate_pool, tmp);
209 }
210 
211 Eina_List *
eio_pack_send(Ecore_Thread * thread,Eina_List * pack,double * start)212 eio_pack_send(Ecore_Thread *thread, Eina_List *pack, double *start)
213 {
214    double current;
215 
216    current = ecore_time_get();
217    if (current - *start > EIO_PACKED_TIME)
218      {
219         *start = current;
220         ecore_thread_feedback(thread, pack);
221         return NULL;
222      }
223 
224    if (memory_pool_limit > 0 &&
225        memory_pool_usage > memory_pool_limit)
226      {
227         eina_lock_take(&(memory_pool_mutex));
228         memory_pool_suspended = EINA_TRUE;
229         eina_condition_wait(&(memory_pool_cond));
230         memory_pool_suspended = EINA_FALSE;
231         eina_lock_release(&(memory_pool_mutex));
232      }
233 
234    return pack;
235 }
236 
237 void *
eio_common_alloc(size_t size)238 eio_common_alloc(size_t size)
239 {
240    return calloc(1, size);
241 }
242 
243 void
eio_common_free(Eio_File * common)244 eio_common_free(Eio_File *common)
245 {
246    free(common);
247 }
248 
249 // For now use a list for simplicity and we should not have that many
250 // pending request
251 static Eina_List *tracked_thread = NULL;
252 
253 void
eio_file_register(Eio_File * common)254 eio_file_register(Eio_File *common)
255 {
256    tracked_thread = eina_list_append(tracked_thread, common);
257 }
258 
259 void
eio_file_unregister(Eio_File * common)260 eio_file_unregister(Eio_File *common)
261 {
262    tracked_thread = eina_list_remove(tracked_thread, common);
263    common->thread = NULL;
264 }
265 
266 /**
267  * @endcond
268  */
269 
270 
271 /*============================================================================*
272  *                                   API                                      *
273  *============================================================================*/
274 
275 EAPI int
eio_init(void)276 eio_init(void)
277 {
278    if (++_eio_init_count != 1)
279      return _eio_init_count;
280 
281    if (!eina_init())
282      {
283         fprintf(stderr, "Eio can not initialize Eina\n");
284         return --_eio_init_count;
285      }
286 
287    _eio_log_dom_global = eina_log_domain_register("eio", EIO_DEFAULT_LOG_COLOR);
288    if (_eio_log_dom_global < 0)
289      {
290         EINA_LOG_ERR("Eio can not create a general log domain.");
291         goto shutdown_eina;
292      }
293 
294    if (!ecore_init())
295      {
296         ERR("Can not initialize Ecore\n");
297         goto unregister_log_domain;
298      }
299 
300    memset(&progress_pool, 0, sizeof(progress_pool));
301    memset(&direct_info_pool, 0, sizeof(direct_info_pool));
302    memset(&char_pool, 0, sizeof(char_pool));
303    memset(&associate_pool, 0, sizeof(associate_pool));
304 
305    eina_lock_new(&(progress_pool.lock));
306    progress_pool.mem_size = sizeof (Eio_Progress);
307    eina_lock_new(&(direct_info_pool.lock));
308    direct_info_pool.mem_size = sizeof (Eio_File_Direct_Info);
309    eina_lock_new(&(char_pool.lock));
310    char_pool.mem_size = sizeof (Eio_File_Char);
311    eina_lock_new(&(associate_pool.lock));
312    associate_pool.mem_size = sizeof (Eio_File_Associate);
313 
314    eina_spinlock_new(&(memory_pool_lock));
315    eina_lock_new(&(memory_pool_mutex));
316    eina_condition_new(&(memory_pool_cond), &(memory_pool_mutex));
317 
318    eio_monitor_init();
319 
320    efreet_mime_init();
321 
322    io_manager = efl_add(EFL_IO_MANAGER_CLASS, efl_main_loop_get());
323    efl_provider_register(efl_main_loop_get(), EFL_IO_MANAGER_CLASS, io_manager);
324 
325    eina_log_timing(_eio_log_dom_global,
326                    EINA_LOG_STATE_STOP,
327                    EINA_LOG_STATE_INIT);
328 
329    return _eio_init_count;
330 
331 unregister_log_domain:
332    eina_log_domain_unregister(_eio_log_dom_global);
333    _eio_log_dom_global = -1;
334 shutdown_eina:
335    eina_shutdown();
336    return --_eio_init_count;
337 }
338 
339 EAPI int
eio_shutdown(void)340 eio_shutdown(void)
341 {
342    Eio_File_Direct_Info *info;
343    Eio_File_Char *cin;
344    Eio_Progress *pg;
345    Eio_File_Associate *asso;
346    Eio_File *f;
347    Eina_List *l;
348 
349    if (_eio_init_count <= 0)
350      {
351         ERR("Init count not greater than 0 in shutdown.");
352         return 0;
353      }
354    if (--_eio_init_count != 0)
355      return _eio_init_count;
356 
357    eina_log_timing(_eio_log_dom_global,
358                    EINA_LOG_STATE_START,
359                    EINA_LOG_STATE_SHUTDOWN);
360 
361    efl_provider_unregister(efl_main_loop_get(), EFL_IO_MANAGER_CLASS, io_manager);
362    efl_del(io_manager);
363    io_manager = NULL;
364 
365    EINA_LIST_FOREACH(tracked_thread, l, f)
366      ecore_thread_cancel(f->thread);
367 
368    EINA_LIST_FREE(tracked_thread, f)
369      {
370         if (!ecore_thread_wait(f->thread, 0.5))
371           CRI("We couldn't terminate in less than 30s some pending IO. This can led to some crash.");
372      }
373 
374    efreet_mime_shutdown();
375 
376    eio_monitor_shutdown();
377 
378    eina_condition_free(&(memory_pool_cond));
379    eina_lock_free(&(memory_pool_mutex));
380    eina_spinlock_free(&(memory_pool_lock));
381 
382    eina_lock_free(&(direct_info_pool.lock));
383    eina_lock_free(&(progress_pool.lock));
384    eina_lock_free(&(char_pool.lock));
385    eina_lock_free(&(associate_pool.lock));
386 
387    /* Cleanup pool */
388    EINA_TRASH_CLEAN(&progress_pool.trash, pg)
389      free(pg);
390    progress_pool.count = 0;
391 
392    EINA_TRASH_CLEAN(&direct_info_pool.trash, info)
393      free(info);
394    direct_info_pool.count = 0;
395 
396    EINA_TRASH_CLEAN(&char_pool.trash, cin)
397      free(cin);
398    char_pool.count = 0;
399 
400    EINA_TRASH_CLEAN(&associate_pool.trash, asso)
401      free(asso);
402    associate_pool.count = 0;
403 
404    ecore_shutdown();
405    eina_log_domain_unregister(_eio_log_dom_global);
406    _eio_log_dom_global = -1;
407    eina_shutdown();
408 
409    return _eio_init_count;
410 }
411 
412 EAPI void
eio_memory_burst_limit_set(size_t limit)413 eio_memory_burst_limit_set(size_t limit)
414 {
415    eina_lock_take(&(memory_pool_mutex));
416    memory_pool_limit = limit;
417    if (memory_pool_suspended)
418      {
419         if (memory_pool_usage < memory_pool_limit)
420           eina_condition_broadcast(&(memory_pool_cond));
421      }
422    eina_lock_release(&(memory_pool_mutex));
423 }
424 
425 EAPI size_t
eio_memory_burst_limit_get(void)426 eio_memory_burst_limit_get(void)
427 {
428    return memory_pool_limit;
429 }
430