1 /* Eina - EFL data type library
2  * Copyright (C) 2013 Cedric Bail
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library;
16  * if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22 
23 #ifdef HAVE_BACKTRACE
24 # include <execinfo.h>
25 #endif
26 
27 #ifndef NVALGRIND
28 # include <memcheck.h>
29 #endif
30 
31 #include "eina_config.h"
32 #include "eina_private.h"
33 #include "eina_log.h"
34 #include "eina_mempool.h"
35 #include "eina_types.h"
36 #include "eina_safety_checks.h"
37 #include "eina_list.h"
38 #include "eina_hash.h"
39 
40 #include "eina_cow.h"
41 
42 #ifdef EINA_COW_MAGIC_ON
43 #define EINA_COW_MAGIC 0xDEADBEEF
44 
45 # define EINA_COW_PTR_MAGIC 0xBEEFE00
46 #endif
47 
48 typedef struct _Eina_Cow_Ptr Eina_Cow_Ptr;
49 typedef struct _Eina_Cow_GC Eina_Cow_GC;
50 
51 #ifdef HAVE_BACKTRACE
52 #define EINA_DEBUG_BT_NUM 64
53 typedef void (*Eina_Bt_Func) ();
54 #endif
55 
56 struct _Eina_Cow_Ptr
57 {
58 #ifdef EINA_COW_MAGIC_ON
59    EINA_MAGIC;
60 # ifdef HAVE_BACKTRACE
61    Eina_Bt_Func writer_bt[EINA_DEBUG_BT_NUM];
62    int  writer_bt_num;
63 # endif
64 #endif
65    int refcount;
66 
67 #ifdef EINA_COW_MAGIC_ON
68    unsigned int writing;
69 #endif
70 
71    Eina_Bool hashed : 1;
72    Eina_Bool togc : 1;
73 };
74 
75 struct _Eina_Cow_GC
76 {
77 #ifdef EINA_COW_MAGIC_ON
78    EINA_MAGIC;
79 #endif
80 
81    Eina_Cow_Ptr *ref;
82    const void **dst;
83 };
84 struct _Eina_Cow
85 {
86 #ifdef EINA_COW_MAGIC_ON
87    EINA_MAGIC;
88 #endif
89 
90    Eina_Hash *togc;
91    Eina_Hash *match;
92 
93    Eina_Mempool *pool;
94    const Eina_Cow_Data *default_value;
95 
96    unsigned int struct_size;
97    unsigned int total_size;
98 };
99 
100 typedef int (*Eina_Cow_Hash)(const void *, int);
101 
102 #ifdef EINA_COW_MAGIC_ON
103 # define EINA_COW_MAGIC_CHECK(d)                        \
104   do {                                                  \
105      if (!EINA_MAGIC_CHECK((d), EINA_COW_MAGIC))        \
106        EINA_MAGIC_FAIL((d), EINA_COW_MAGIC);            \
107   } while (0);
108 
109 # define EINA_COW_PTR_MAGIC_CHECK(d)                    \
110   do {                                                  \
111      if (!EINA_MAGIC_CHECK((d), EINA_COW_PTR_MAGIC))    \
112        EINA_MAGIC_FAIL((d), EINA_COW_PTR_MAGIC);        \
113   } while (0);
114 #else
115 # define EINA_COW_MAGIC_CHECK(d)
116 # define EINA_COW_PTR_MAGIC_CHECK(d)
117 #endif
118 
119 #define EINA_COW_PTR_SIZE                       \
120   eina_mempool_alignof(sizeof (Eina_Cow_Ptr))
121 
122 #define EINA_COW_PTR_GET(d)				\
123   (((Eina_Cow_Ptr *)d) - 1)
124 
125 #define EINA_COW_DATA_GET(d)                    \
126   (((Eina_Cow_Ptr *)d) + 1)
127 
128 static int _eina_cow_log_dom = -1;
129 
130 #ifdef ERR
131 #undef ERR
132 #endif
133 #define ERR(...) EINA_LOG_DOM_ERR(_eina_cow_log_dom, __VA_ARGS__)
134 
135 #ifdef INF
136 #undef INF
137 #endif
138 #define INF(...) EINA_LOG_DOM_INFO(_eina_cow_log_dom, __VA_ARGS__)
139 
140 #ifdef DBG
141 #undef DBG
142 #endif
143 #define DBG(...) EINA_LOG_DOM_DBG(_eina_cow_log_dom, __VA_ARGS__)
144 
145 
146 static Eina_Mempool *gc_pool = NULL;
147 
148 static inline int
_eina_cow_hash_gen(const void * key,int key_length,Eina_Cow_Hash hash,int size)149 _eina_cow_hash_gen(const void *key, int key_length,
150                    Eina_Cow_Hash hash,
151                    int size)
152 {
153    const unsigned char *walk = key;
154    int r = 0xDEADBEEF;
155 
156    while (key_length > 0)
157      {
158         r ^= hash(walk, size);
159 
160         walk += size;
161         key_length -= size;
162      }
163 
164    return r;
165 }
166 
167 #ifdef EFL64
168 static int
_eina_cow_hash64(const void * key,int key_length)169 _eina_cow_hash64(const void *key, int key_length)
170 {
171    return _eina_cow_hash_gen(key, key_length,
172                              (Eina_Cow_Hash) eina_hash_int64, sizeof (unsigned long long int));
173 }
174 #else
175 static int
_eina_cow_hash32(const void * key,int key_length)176 _eina_cow_hash32(const void *key, int key_length)
177 {
178    return _eina_cow_hash_gen(key, key_length,
179                              (Eina_Cow_Hash) eina_hash_int32, sizeof (int));
180 }
181 #endif
182 
183 static int current_cow_size = 0;
184 
185 static unsigned int
_eina_cow_length(const void * key EINA_UNUSED)186 _eina_cow_length(const void *key EINA_UNUSED)
187 {
188    /* nasty hack, since only gc needs to access the hash, it will be in charge
189       of that global. access to the hash should be considered global.
190     */
191    return current_cow_size;
192 }
193 
194 static int
_eina_cow_cmp(const void * key1,int key1_length,const void * key2,int key2_length EINA_UNUSED)195 _eina_cow_cmp(const void *key1, int key1_length,
196               const void *key2, int key2_length EINA_UNUSED)
197 {
198    return memcmp(key1, key2, key1_length);
199 }
200 
201 static inline void
_eina_cow_hash_del(Eina_Cow * cow,const void * data,Eina_Cow_Ptr * ref)202 _eina_cow_hash_del(Eina_Cow *cow,
203                    const void *data,
204                    Eina_Cow_Ptr *ref)
205 {
206    /* eina_cow_gc is not supposed to be thread safe */
207    if (!ref->hashed) return;
208 
209    current_cow_size = cow->struct_size;
210    eina_hash_del(cow->match, data, data);
211    ref->hashed = EINA_FALSE;
212 }
213 
214 static void
_eina_cow_gc_free(void * data)215 _eina_cow_gc_free(void *data)
216 {
217    eina_mempool_free(gc_pool, data);
218 }
219 
220 static inline void
_eina_cow_togc_del(Eina_Cow * cow,Eina_Cow_Ptr * ref)221 _eina_cow_togc_del(Eina_Cow *cow, Eina_Cow_Ptr *ref)
222 {
223    /* eina_cow_gc is not supposed to be thread safe */
224    if (!ref->togc) return;
225    eina_hash_del(cow->togc, &ref, NULL);
226    ref->togc = EINA_FALSE;
227 }
228 
229 static void
_eina_cow_togc_add(Eina_Cow * cow,Eina_Cow_Ptr * ref,const Eina_Cow_Data ** dst)230 _eina_cow_togc_add(Eina_Cow *cow,
231                    Eina_Cow_Ptr *ref,
232                    const Eina_Cow_Data ** dst)
233 {
234    Eina_Cow_GC *gc;
235 
236    /* needed if we want to make cow gc safe */
237    if (ref->togc) return;
238 #ifndef NVALGRIND
239    VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
240 #endif
241 
242    gc = eina_mempool_malloc(gc_pool, sizeof (Eina_Cow_GC));
243    if (!gc) return; /* That one will not get gced this time */
244 
245    gc->ref = ref;
246    gc->dst = dst;
247    eina_hash_direct_add(cow->togc, &gc->ref, gc);
248 #ifndef NVALGRIND
249    VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
250 #endif
251    ref->togc = EINA_TRUE;
252 #ifndef NVALGRIND
253    VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
254 #endif
255 }
256 
257 static void
_eina_cow_gc(Eina_Cow * cow,Eina_Cow_GC * gc)258 _eina_cow_gc(Eina_Cow *cow, Eina_Cow_GC *gc)
259 {
260    Eina_Cow_Data *data;
261    Eina_Cow_Data *match;
262 
263    data = EINA_COW_DATA_GET(gc->ref);
264 
265    current_cow_size = cow->struct_size;
266    match = eina_hash_find(cow->match, data);
267    if (match)
268      {
269         Eina_Cow_Ptr *ref = EINA_COW_PTR_GET(match);
270 #ifndef NVALGRIND
271         VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
272 #endif
273         ref->refcount += gc->ref->refcount;
274 
275         *gc->dst = match;
276         eina_cow_free(cow, (const Eina_Cow_Data**) &data);
277 
278 #ifndef NVALGRIND
279         VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
280 #endif
281      }
282    else
283      {
284         eina_hash_direct_add(cow->match, data, data);
285         gc->ref->hashed = EINA_TRUE;
286         gc->ref->togc = EINA_FALSE;
287         eina_hash_del(cow->togc, &gc->ref, gc);
288      }
289 }
290 
291 Eina_Bool
eina_cow_init(void)292 eina_cow_init(void)
293 {
294    const char *choice, *tmp;
295 
296    _eina_cow_log_dom = eina_log_domain_register("eina_cow", EINA_LOG_COLOR_DEFAULT);
297    if (_eina_cow_log_dom < 0)
298      {
299         EINA_LOG_ERR("Could not register log domain: eina_cow");
300         return EINA_FALSE;
301      }
302 
303 #ifdef EINA_DEFAULT_MEMPOOL
304    choice = "pass_through";
305 #else
306    choice = "chained_mempool";
307 #endif
308    tmp = getenv("EINA_MEMPOOL");
309    if (tmp && tmp[0])
310      choice = tmp;
311 
312    gc_pool = eina_mempool_add(choice, "gc", NULL, sizeof (Eina_Cow_GC), 32);
313    if (!gc_pool)
314      {
315         ERR("Mempool for cow gc cannot be allocated.");
316         return EINA_FALSE;
317      }
318    return EINA_TRUE;
319 }
320 
321 Eina_Bool
eina_cow_shutdown(void)322 eina_cow_shutdown(void)
323 {
324    eina_log_domain_unregister(_eina_cow_log_dom);
325    eina_mempool_del(gc_pool);
326    return EINA_TRUE;
327 }
328 
329 EAPI Eina_Cow *
eina_cow_add(const char * name,unsigned int struct_size,unsigned int step,const void * default_value,Eina_Bool gc)330 eina_cow_add(const char *name, unsigned int struct_size, unsigned int step, const void *default_value, Eina_Bool gc)
331 {
332    const char *choice, *tmp;
333    Eina_Cow *cow;
334    unsigned int total_size;
335 
336    EINA_SAFETY_ON_NULL_RETURN_VAL(default_value, NULL);
337    EINA_SAFETY_ON_FALSE_RETURN_VAL(struct_size, NULL);
338    EINA_SAFETY_ON_FALSE_RETURN_VAL(step, NULL);
339 
340    cow = malloc(sizeof (Eina_Cow));
341    if (!cow) return NULL;
342 
343 #ifdef EINA_DEFAULT_MEMPOOL
344    choice = "pass_through";
345 #else
346    choice = "chained_mempool";
347 #endif
348    tmp = getenv("EINA_MEMPOOL");
349    if (tmp && tmp[0])
350      choice = tmp;
351 
352    INF("Creating Cow '%s' with mempool of type '%s'", name, choice);
353    total_size = eina_mempool_alignof(struct_size + EINA_COW_PTR_SIZE);
354    cow->pool = eina_mempool_add(choice, name, NULL, total_size, step);
355    if (!cow->pool)
356      {
357         ERR("Mempool for cow '%s' cannot be allocated.", name);
358         goto on_error;
359      }
360 
361 #ifdef EFL64
362    cow->match = eina_hash_new(_eina_cow_length,
363                               _eina_cow_cmp,
364                               _eina_cow_hash64,
365                               NULL,
366                               6);
367 #else
368    cow->match = eina_hash_new(_eina_cow_length,
369                               _eina_cow_cmp,
370                               _eina_cow_hash32,
371                               NULL,
372                               6);
373 #endif
374    if (gc)
375      cow->togc = eina_hash_pointer_new(_eina_cow_gc_free);
376    else
377      cow->togc = NULL;
378    cow->default_value = default_value;
379    cow->struct_size = struct_size;
380    cow->total_size = total_size;
381 
382 #ifdef EINA_COW_MAGIC_ON
383    EINA_MAGIC_SET(cow, EINA_COW_MAGIC);
384 #endif
385 
386    return cow;
387 
388  on_error:
389    free(cow);
390    return NULL;
391 }
392 
393 EAPI void
eina_cow_del(Eina_Cow * cow)394 eina_cow_del(Eina_Cow *cow)
395 {
396    if (!cow) return;
397 
398 #ifdef EINA_COW_MAGIC_ON
399    EINA_COW_MAGIC_CHECK(cow);
400 #endif
401 
402    eina_mempool_del(cow->pool);
403    eina_hash_free(cow->match);
404    if (cow->togc) eina_hash_free(cow->togc);
405    free(cow);
406 }
407 
408 EAPI const Eina_Cow_Data *
eina_cow_alloc(Eina_Cow * cow)409 eina_cow_alloc(Eina_Cow *cow)
410 {
411 #ifdef EINA_COW_MAGIC_ON
412    EINA_COW_MAGIC_CHECK(cow);
413 #endif
414 
415    return cow->default_value;
416 }
417 
418 EAPI void
eina_cow_free(Eina_Cow * cow,const Eina_Cow_Data ** data)419 eina_cow_free(Eina_Cow *cow, const Eina_Cow_Data **data)
420 {
421    Eina_Cow_Ptr *ref;
422 
423 #ifdef EINA_COW_MAGIC_ON
424    EINA_COW_MAGIC_CHECK(cow);
425 #endif
426 
427    if (!data || !*data) return;
428    if (cow->default_value == *data) return;
429 
430    ref = EINA_COW_PTR_GET(*data);
431 #ifndef NVALGRIND
432    VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
433 #endif
434    ref->refcount--;
435 
436    if (ref->refcount == 0) _eina_cow_hash_del(cow, *data, ref);
437    *data = (Eina_Cow_Data*) cow->default_value;
438 
439    if (ref->refcount > 0)
440      {
441 #ifndef NVALGRIND
442        VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
443 #endif
444        return;
445      }
446 
447 #ifdef EINA_COW_MAGIC_ON
448    EINA_MAGIC_SET(ref, EINA_MAGIC_NONE);
449 #endif
450    _eina_cow_togc_del(cow, ref);
451    eina_mempool_free(cow->pool, (void*) ref);
452 }
453 
454 EAPI void *
eina_cow_write(Eina_Cow * cow,const Eina_Cow_Data * const * data)455 eina_cow_write(Eina_Cow *cow,
456 	       const Eina_Cow_Data * const *data)
457 {
458    Eina_Cow_Ptr *ref;
459    Eina_Cow_Data *r;
460 
461 #ifdef EINA_COW_MAGIC_ON
462    EINA_COW_MAGIC_CHECK(cow);
463 #endif
464 
465    if (!*data) return NULL; /* cow pointer is always != NULL */
466    if (*data == cow->default_value)
467      goto allocate;
468 
469    ref = EINA_COW_PTR_GET(*data);
470 
471 #ifndef NVALGRIND
472    VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
473 #endif
474    if (ref->refcount == 1)
475      {
476 #ifdef EINA_COW_MAGIC_ON
477         EINA_COW_PTR_MAGIC_CHECK(ref);
478 
479         if (ref->writing && ref->togc && ref->hashed)
480           {
481              ERR("Request writing on a GC-ed pointer that is already in a writing process %p\n", data);
482 #ifdef HAVE_BACKTRACE
483              backtrace_symbols_fd((void **) ref->writer_bt,
484                                   ref->writer_bt_num, 1);
485 #endif
486              return NULL;
487           }
488 #endif
489 
490         if (cow->togc)
491           _eina_cow_hash_del(cow, *data, ref);
492 
493 #ifndef NVALGRIND
494         VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
495 #endif
496         goto end;
497      }
498    ref->refcount--;
499 
500  allocate:
501    ref = eina_mempool_malloc(cow->pool, cow->total_size);
502    ref->refcount = 1;
503 #ifdef EINA_COW_MAGIC_ON
504    ref->writing = 0;
505 #endif
506    ref->hashed = EINA_FALSE;
507    ref->togc = EINA_FALSE;
508 #ifdef EINA_COW_MAGIC_ON
509    EINA_MAGIC_SET(ref, EINA_COW_PTR_MAGIC);
510 #endif
511 
512 #ifndef NVALGRIND
513    VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
514 #endif
515 
516    r = EINA_COW_DATA_GET(ref);
517    memcpy(r, *data, cow->struct_size);
518    *((Eina_Cow_Data**) data) = r;
519 
520  end:
521 #ifndef NVALGRIND
522    VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
523 #endif
524 #ifdef EINA_COW_MAGIC_ON
525 # ifdef HAVE_BACKTRACE
526    ref->writer_bt_num = backtrace((void **)(ref->writer_bt),
527                                   EINA_DEBUG_BT_NUM);
528 # endif
529    ref->writing++;
530 #endif
531 #ifndef NVALGRIND
532    VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
533 #endif
534 
535    return (void *) *data;
536 }
537 
538 EAPI void
eina_cow_done(Eina_Cow * cow,const Eina_Cow_Data * const * dst,const void * data,Eina_Bool needed_gc)539 eina_cow_done(Eina_Cow *cow,
540 	      const Eina_Cow_Data * const * dst,
541 	      const void *data,
542               Eina_Bool needed_gc)
543 {
544    Eina_Cow_Ptr *ref;
545 
546    EINA_COW_MAGIC_CHECK(cow);
547 
548    ref = EINA_COW_PTR_GET(data);
549 #ifndef NVALGRIND
550    VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
551 #endif
552    EINA_COW_PTR_MAGIC_CHECK(ref);
553 #ifdef EINA_COW_MAGIC_ON
554    if (!ref->writing)
555      ERR("Pointer %p is not in a writable state !", dst);
556 
557    ref->writing--;
558 #endif
559 #ifndef NVALGRIND
560    VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
561 #endif
562 
563    if (!cow->togc || !needed_gc) return;
564 
565 #ifndef NVALGRIND
566    VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
567 #endif
568 
569    _eina_cow_togc_add(cow, ref, (const Eina_Cow_Data **) dst);
570 }
571 
572 EAPI void
eina_cow_memcpy(Eina_Cow * cow,const Eina_Cow_Data * const * dst,const Eina_Cow_Data * src)573 eina_cow_memcpy(Eina_Cow *cow,
574 		const Eina_Cow_Data * const *dst,
575 		const Eina_Cow_Data *src)
576 {
577    Eina_Cow_Ptr *ref;
578 
579    EINA_COW_MAGIC_CHECK(cow);
580 
581    if (*dst == src) return;
582 
583    if (src != cow->default_value)
584      {
585        ref = EINA_COW_PTR_GET(src);
586 #ifndef NVALGRIND
587        VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
588 #endif
589 
590        EINA_COW_PTR_MAGIC_CHECK(ref);
591        ref->refcount++;
592 
593        if (cow->togc)
594          _eina_cow_togc_del(cow, ref);
595 
596 #ifndef NVALGRIND
597        VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
598 #endif
599      }
600 
601    eina_cow_free(cow, (const Eina_Cow_Data**) dst);
602 
603    *((const void**)dst) = src;
604 }
605 
606 EAPI Eina_Bool
eina_cow_gc(Eina_Cow * cow)607 eina_cow_gc(Eina_Cow *cow)
608 {
609    Eina_Cow_GC *gc;
610    Eina_Iterator *it;
611    Eina_Bool r;
612 #ifndef NVALGRIND
613    Eina_Cow_Ptr *ref;
614 #endif
615 
616    EINA_COW_MAGIC_CHECK(cow);
617 
618    if (!cow->togc || !eina_hash_population(cow->togc))
619      return EINA_FALSE;
620 
621    it = eina_hash_iterator_data_new(cow->togc);
622    r = eina_iterator_next(it, (void**) &gc);
623    eina_iterator_free(it);
624 
625    if (!r) return EINA_FALSE; /* Something did go wrong here */
626 
627 #ifndef NVALGRIND
628    /* Do handle hash and all funky merge thing here */
629    ref = gc->ref;
630 
631    VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
632 #endif
633    _eina_cow_gc(cow, gc);
634 #ifndef NVALGRIND
635    VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
636 #endif
637 
638    return EINA_TRUE;
639 }
640 
641