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