summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCedric BAIL <cedric.bail@free.fr>2013-01-14 03:34:19 +0000
committerCedric BAIL <cedric.bail@free.fr>2013-01-14 03:34:19 +0000
commitc7aead6fdc8d29932b090857a6e47581b3c5f20c (patch)
tree311bfa0af5049045667760f8b88ef778b6e604ba
parentc42971cd273b31ce9ec55206eb7d178ffd3f8c88 (diff)
efl: fix cow after review by k-s.
NOTE: I didn't find a way to tell valgrind that some memory is read only. SVN revision: 82742
-rw-r--r--src/lib/eina/eina_cow.c152
-rw-r--r--src/lib/eina/eina_cow.h8
-rw-r--r--src/tests/eina/eina_test_cow.c61
3 files changed, 153 insertions, 68 deletions
diff --git a/src/lib/eina/eina_cow.c b/src/lib/eina/eina_cow.c
index e891b5c6c2..9513420682 100644
--- a/src/lib/eina/eina_cow.c
+++ b/src/lib/eina/eina_cow.c
@@ -1,4 +1,4 @@
1/* EINA - EFL data type library 1/* Eina - EFL data type library
2 * Copyright (C) 2013 Cedric Bail 2 * Copyright (C) 2013 Cedric Bail
3 * 3 *
4 * This library is free software; you can redistribute it and/or 4 * This library is free software; you can redistribute it and/or
@@ -21,6 +21,8 @@
21#endif 21#endif
22 22
23#include "eina_config.h" 23#include "eina_config.h"
24#include "eina_private.h"
25#include "eina_log.h"
24#include "eina_mempool.h" 26#include "eina_mempool.h"
25#include "eina_types.h" 27#include "eina_types.h"
26#include "eina_safety_checks.h" 28#include "eina_safety_checks.h"
@@ -38,13 +40,13 @@ struct _Eina_Cow_Ptr
38{ 40{
39 int refcount; 41 int refcount;
40 42
41 Eina_Bool hashed; 43 Eina_Bool hashed : 1;
42 Eina_Bool togc; 44 Eina_Bool togc : 1;
45 Eina_Bool writing : 1;
43}; 46};
44 47
45struct _Eina_Cow_GC 48struct _Eina_Cow_GC
46{ 49{
47 Eina_List *togc;
48 Eina_Cow_Ptr *ref; 50 Eina_Cow_Ptr *ref;
49 const void * const *dst; 51 const void * const *dst;
50}; 52};
@@ -70,6 +72,27 @@ typedef int (*Eina_Cow_Hash)(const void *, int);
70 EINA_MAGIC_FAIL((d), EINA_COW_MAGIC); \ 72 EINA_MAGIC_FAIL((d), EINA_COW_MAGIC); \
71 } while (0); 73 } while (0);
72 74
75#define EINA_COW_PTR_SIZE \
76 (sizeof (Eina_Cow_Ptr) < sizeof (void*) ? sizeof (void*) : sizeof (Eina_Cow_Ptr))
77
78#define EINA_COW_PTR_GET(d) \
79 (Eina_Cow_Ptr*) (((unsigned char *)d) - EINA_COW_PTR_SIZE)
80
81#define EINA_COW_DATA_GET(d) \
82 (((unsigned char *)d) + EINA_COW_PTR_SIZE)
83
84static int _eina_cow_log_dom = -1;
85
86#ifdef ERR
87#undef ERR
88#endif
89#define ERR(...) EINA_LOG_DOM_ERR(_eina_cow_log_dom, __VA_ARGS__)
90
91#ifdef DBG
92#undef DBG
93#endif
94#define DBG(...) EINA_LOG_DOM_DBG(_eina_cow_log_dom, __VA_ARGS__)
95
73static Eina_Mempool *gc_pool = NULL; 96static Eina_Mempool *gc_pool = NULL;
74 97
75static inline int 98static inline int
@@ -113,8 +136,7 @@ static unsigned int
113_eina_cow_length(const void *key EINA_UNUSED) 136_eina_cow_length(const void *key EINA_UNUSED)
114{ 137{
115 /* nasty hack, has only gc need to access the hash, he will be in charge 138 /* nasty hack, has only gc need to access the hash, he will be in charge
116 of that global. access to the hash should be considered global. so a 139 of that global. access to the hash should be considered global.
117 lock will be needed to make multiple gc run at the same safely.
118 */ 140 */
119 return current_cow_size; 141 return current_cow_size;
120} 142}
@@ -131,32 +153,31 @@ _eina_cow_hash_del(Eina_Cow *cow,
131 const void *data, 153 const void *data,
132 Eina_Cow_Ptr *ref) 154 Eina_Cow_Ptr *ref)
133{ 155{
134 /* if eina_cow_gc is supposed to be thread safe, lock the cow here */ 156 /* eina_cow_gc is not supposed to be thread safe */
135 if (ref->hashed) 157 if (!ref->hashed) return ;
136 { 158
137 current_cow_size = cow->struct_size; 159 current_cow_size = cow->struct_size;
138 eina_hash_del(cow->match, data, ref); 160 eina_hash_del(cow->match, data, ref);
139 ref->hashed = EINA_FALSE; 161 ref->hashed = EINA_FALSE;
140 }
141} 162}
142 163
143static void 164static void
144_eina_cow_togc_del(Eina_Cow *cow, Eina_Cow_Ptr *ref) 165_eina_cow_togc_del(Eina_Cow *cow, Eina_Cow_Ptr *ref)
145{ 166{
146 /* if eina_cow_gc is supposed to be thread safe, lock the cow here */ 167 Eina_Cow_GC *gc;
147 if (ref->togc) 168 Eina_List *l;
148 { 169
149 Eina_Cow_GC *gc; 170 /* eina_cow_gc is not supposed to be thread safe */
150 Eina_List *l; 171 if (!ref->togc) return ;
151 172
152 EINA_LIST_FOREACH(cow->togc, l, gc) 173 EINA_LIST_FOREACH(cow->togc, l, gc)
153 if (gc->ref == ref) 174 if (gc->ref == ref)
154 { 175 {
155 cow->togc = eina_list_remove_list(cow->togc, l); 176 cow->togc = eina_list_remove_list(cow->togc, l);
156 break; 177 eina_mempool_free(gc_pool, gc);
157 } 178 break;
158 ref->togc = EINA_FALSE; 179 }
159 } 180 ref->togc = EINA_FALSE;
160} 181}
161 182
162Eina_Bool 183Eina_Bool
@@ -164,6 +185,13 @@ eina_cow_init(void)
164{ 185{
165 const char *choice, *tmp; 186 const char *choice, *tmp;
166 187
188 _eina_cow_log_dom = eina_log_domain_register("eina_cow", EINA_LOG_COLOR_DEFAULT);
189 if (_eina_cow_log_dom < 0)
190 {
191 EINA_LOG_ERR("Could not register log domain: eina_cow");
192 return EINA_FALSE;
193 }
194
167#ifdef EINA_DEFAULT_MEMPOOL 195#ifdef EINA_DEFAULT_MEMPOOL
168 choice = "pass_through"; 196 choice = "pass_through";
169#else 197#else
@@ -176,7 +204,7 @@ eina_cow_init(void)
176 gc_pool = eina_mempool_add(choice, "gc", NULL, sizeof (Eina_Cow_GC), 32); 204 gc_pool = eina_mempool_add(choice, "gc", NULL, sizeof (Eina_Cow_GC), 32);
177 if (!gc_pool) 205 if (!gc_pool)
178 { 206 {
179 /* ERR("ERROR: Mempool for cow '%s' cannot be allocated in eina_cow_new.", name); */ 207 ERR("Mempool for cow gc cannot be allocated.");
180 return EINA_FALSE; 208 return EINA_FALSE;
181 } 209 }
182 return EINA_TRUE; 210 return EINA_TRUE;
@@ -213,10 +241,10 @@ eina_cow_add(const char *name, unsigned int struct_size, unsigned int step, cons
213 241
214 cow->pool = eina_mempool_add(choice, name, 242 cow->pool = eina_mempool_add(choice, name,
215 NULL, 243 NULL,
216 struct_size + sizeof (Eina_Cow_Ptr), step); 244 struct_size + EINA_COW_PTR_SIZE, step);
217 if (!cow->pool) 245 if (!cow->pool)
218 { 246 {
219 /* ERR("ERROR: Mempool for cow '%s' cannot be allocated in eina_cow_new.", name); */ 247 ERR("Mempool for cow '%s' cannot be allocated.", name);
220 goto on_error; 248 goto on_error;
221 } 249 }
222 250
@@ -279,7 +307,7 @@ eina_cow_free(Eina_Cow *cow, const void *data)
279 if (!data) return ; 307 if (!data) return ;
280 if (cow->default_value == data) return ; 308 if (cow->default_value == data) return ;
281 309
282 ref = (Eina_Cow_Ptr*)(((unsigned char*) data) + cow->struct_size); 310 ref = EINA_COW_PTR_GET(data);
283 ref->refcount--; 311 ref->refcount--;
284 312
285 if (ref->refcount > 0) return ; 313 if (ref->refcount > 0) return ;
@@ -293,65 +321,67 @@ EAPI void *
293eina_cow_write(Eina_Cow *cow, const void * const *data) 321eina_cow_write(Eina_Cow *cow, const void * const *data)
294{ 322{
295 Eina_Cow_Ptr *ref; 323 Eina_Cow_Ptr *ref;
296 const void *src;
297 void *r; 324 void *r;
298 325
299 EINA_COW_MAGIC_CHECK(cow); 326 EINA_COW_MAGIC_CHECK(cow);
300 327
301 if (!*data) return NULL; /* cow pointer is always != NULL */ 328 if (!*data) return NULL; /* cow pointer is always != NULL */
302 if (*data == cow->default_value) 329 if (*data == cow->default_value)
303 { 330 goto allocate;
304 src = cow->default_value;
305 goto allocate;
306 }
307 331
308 ref = (Eina_Cow_Ptr*)(((unsigned char*) *data) + cow->struct_size); 332 ref = EINA_COW_PTR_GET(*data);
309 333
310 if (ref->refcount == 1) 334 if (ref->refcount == 1)
311 { 335 {
336 if (ref->writing)
337 {
338 ERR("Request writing on an pointer that is already in a writing process %p\n", data);
339 return NULL;
340 }
341
312 _eina_cow_hash_del(cow, *data, ref); 342 _eina_cow_hash_del(cow, *data, ref);
313 return (void *) *data; 343 goto end;
314 } 344 }
315 345
316 src = *data;
317
318 allocate: 346 allocate:
319 r = eina_mempool_malloc(cow->pool, 347 ref = eina_mempool_malloc(cow->pool,
320 cow->struct_size + sizeof (Eina_Cow_Ptr)); 348 cow->struct_size + EINA_COW_PTR_SIZE);
321 memcpy(r, src, cow->struct_size);
322
323 ref = (Eina_Cow_Ptr*)(((unsigned char*) r) + cow->struct_size);
324 ref->refcount = 1; 349 ref->refcount = 1;
325 ref->hashed = EINA_FALSE; 350 ref->hashed = EINA_FALSE;
326 ref->togc = EINA_FALSE; 351 ref->togc = EINA_FALSE;
327 352
353 r = EINA_COW_DATA_GET(ref);
354 memcpy(r, *data, cow->struct_size);
328 *((void**) data) = r; 355 *((void**) data) = r;
329 356
330 return r; 357 end:
358 ref->writing = EINA_TRUE;
359 return (void *) *data;
331} 360}
332 361
333EAPI void 362EAPI void
334eina_cow_commit(Eina_Cow *cow, const void * const * dst, const void *data) 363eina_cow_done(Eina_Cow *cow, const void * const * dst, const void *data)
335{ 364{
336 Eina_Cow_Ptr *ref; 365 Eina_Cow_Ptr *ref;
366 Eina_Cow_GC *gc;
337 367
338 EINA_COW_MAGIC_CHECK(cow); 368 EINA_COW_MAGIC_CHECK(cow);
339 369
340 ref = (Eina_Cow_Ptr*)(((unsigned char*) data) + cow->struct_size); 370 ref = EINA_COW_PTR_GET(data);
371 if (!ref->writing)
372 ERR("Pointer %p is not in a writable state !", dst);
373 ref->writing = EINA_FALSE;
341 374
342 /* needed if we want to make cow gc safe */ 375 /* needed if we want to make cow gc safe */
343 if (!ref->togc) 376 if (ref->togc) return ;
344 {
345 Eina_Cow_GC *gc;
346 377
347 gc = eina_mempool_malloc(gc_pool, sizeof (Eina_Cow_GC)); 378 gc = eina_mempool_malloc(gc_pool, sizeof (Eina_Cow_GC));
348 if (!gc) return ; /* That one will not get gced this time */ 379 if (!gc) return ; /* That one will not get gced this time */
349 380
350 gc->ref = ref; 381 gc->ref = ref;
351 gc->dst = dst; 382 gc->dst = dst;
352 cow->togc = gc->togc = eina_list_prepend(cow->togc, gc); 383 cow->togc = eina_list_prepend(cow->togc, gc);
353 ref->togc = EINA_TRUE; 384 ref->togc = EINA_TRUE;
354 }
355} 385}
356 386
357EAPI void 387EAPI void
@@ -363,7 +393,7 @@ eina_cow_memcpy(Eina_Cow *cow, const void * const *dst, const void *src)
363 393
364 eina_cow_free(cow, *dst); 394 eina_cow_free(cow, *dst);
365 395
366 ref = (Eina_Cow_Ptr*)(((unsigned char*) src) + cow->struct_size); 396 ref = EINA_COW_PTR_GET(src);
367 ref->refcount++; 397 ref->refcount++;
368 398
369 *((const void**)dst) = src; 399 *((const void**)dst) = src;
@@ -384,7 +414,7 @@ eina_cow_gc(Eina_Cow *cow)
384 /* Do handle hash and all funky merge think here */ 414 /* Do handle hash and all funky merge think here */
385 gc = eina_list_data_get(eina_list_last(cow->togc)); 415 gc = eina_list_data_get(eina_list_last(cow->togc));
386 416
387 data = ((unsigned char*) gc->ref) - cow->struct_size; 417 data = EINA_COW_DATA_GET(gc->ref);
388 418
389 gc->ref->togc = EINA_FALSE; 419 gc->ref->togc = EINA_FALSE;
390 cow->togc = eina_list_remove_list(cow->togc, eina_list_last(cow->togc)); 420 cow->togc = eina_list_remove_list(cow->togc, eina_list_last(cow->togc));
@@ -395,7 +425,7 @@ eina_cow_gc(Eina_Cow *cow)
395 { 425 {
396 eina_cow_free(cow, data); 426 eina_cow_free(cow, data);
397 427
398 ref = (Eina_Cow_Ptr*)(((unsigned char*) match) + cow->struct_size); 428 ref = EINA_COW_PTR_GET(match);
399 *((void**)gc->dst) = match; 429 *((void**)gc->dst) = match;
400 ref->refcount++; 430 ref->refcount++;
401 } 431 }
diff --git a/src/lib/eina/eina_cow.h b/src/lib/eina/eina_cow.h
index 03d3c5c0c8..710c4de8ae 100644
--- a/src/lib/eina/eina_cow.h
+++ b/src/lib/eina/eina_cow.h
@@ -21,14 +21,14 @@
21 21
22typedef struct _Eina_Cow Eina_Cow; 22typedef struct _Eina_Cow Eina_Cow;
23 23
24EAPI Eina_Cow *eina_cow_add(const char *name, unsigned int struct_size, unsigned int step, const void *default_value); 24EAPI Eina_Cow *eina_cow_add(const char *name, unsigned int struct_size, unsigned int step, const void *default_value) EINA_WARN_UNUSED_RESULT;
25EAPI void eina_cow_del(Eina_Cow *cow); 25EAPI void eina_cow_del(Eina_Cow *cow);
26 26
27EAPI const void *eina_cow_alloc(Eina_Cow *cow); 27EAPI const void *eina_cow_alloc(Eina_Cow *cow) EINA_WARN_UNUSED_RESULT;
28EAPI void eina_cow_free(Eina_Cow *cow, const void *data); 28EAPI void eina_cow_free(Eina_Cow *cow, const void *data);
29 29
30EAPI void *eina_cow_write(Eina_Cow *cow, const void * const *src); 30EAPI void *eina_cow_write(Eina_Cow *cow, const void * const *src) EINA_WARN_UNUSED_RESULT;
31EAPI void eina_cow_commit(Eina_Cow *cow, const void * const *dst, const void *data); 31EAPI void eina_cow_done(Eina_Cow *cow, const void * const *dst, const void *data);
32EAPI void eina_cow_memcpy(Eina_Cow *cow, const void * const *dst, const void *src); 32EAPI void eina_cow_memcpy(Eina_Cow *cow, const void * const *dst, const void *src);
33 33
34EAPI Eina_Bool eina_cow_gc(Eina_Cow *cow); 34EAPI Eina_Bool eina_cow_gc(Eina_Cow *cow);
diff --git a/src/tests/eina/eina_test_cow.c b/src/tests/eina/eina_test_cow.c
index 4d4d8e8f8a..cefba43f18 100644
--- a/src/tests/eina/eina_test_cow.c
+++ b/src/tests/eina/eina_test_cow.c
@@ -31,6 +31,55 @@ struct _Eina_Cow_Test
31 void *d; 31 void *d;
32}; 32};
33 33
34static void
35_eina_test_log(const Eina_Log_Domain *d EINA_UNUSED,
36 Eina_Log_Level level EINA_UNUSED, const char *file EINA_UNUSED, const char *fnc EINA_UNUSED, int line EINA_UNUSED,
37 const char *fmt EINA_UNUSED, void *data, va_list args EINA_UNUSED)
38{
39 Eina_Bool *bol = data;
40
41 *bol = EINA_TRUE;
42}
43
44START_TEST(eina_cow_bad)
45{
46 const Eina_Cow_Test *cur;
47 Eina_Cow_Test *write;
48 Eina_Cow *cow;
49 Eina_Bool over_commit = EINA_FALSE;
50 Eina_Bool over_writing = EINA_FALSE;
51 Eina_Cow_Test default_value = { 7, 42, NULL };
52
53 cow = eina_cow_add("COW Test", sizeof (Eina_Cow_Test), 16, &default_value);
54 fail_if(cow == NULL);
55
56 cur = eina_cow_alloc(cow);
57 fail_if(cur == NULL);
58
59 write = eina_cow_write(cow, &cur);
60 fail_if(write == NULL || write == &default_value);
61
62 write->i = 7;
63 eina_cow_done(cow, &cur, write);
64 fail_if(cur->i != 7 || default_value.i != 42);
65
66 eina_log_print_cb_set(_eina_test_log, &over_commit);
67 eina_cow_done(cow, &cur, write); /* Testing over commit */
68 fail_if(!over_commit);
69
70 write = eina_cow_write(cow, &cur);
71 fail_if(write == NULL || write == &default_value);
72
73 eina_log_print_cb_set(_eina_test_log, &over_writing);
74 write = eina_cow_write(cow, &cur); /* Testing over writing */
75 fail_if(write != NULL || !over_writing);
76
77 eina_cow_free(cow, cur);
78
79 eina_cow_del(cow);
80}
81END_TEST
82
34START_TEST(eina_cow) 83START_TEST(eina_cow)
35{ 84{
36 const Eina_Cow_Test *prev; 85 const Eina_Cow_Test *prev;
@@ -50,7 +99,7 @@ START_TEST(eina_cow)
50 fail_if(write == NULL || write == &default_value); 99 fail_if(write == NULL || write == &default_value);
51 100
52 write->i = 7; 101 write->i = 7;
53 eina_cow_commit(cow, &cur, write); 102 eina_cow_done(cow, &cur, write);
54 fail_if(cur->i != 7 || prev->i != 0); 103 fail_if(cur->i != 7 || prev->i != 0);
55 104
56 eina_cow_memcpy(cow, &prev, cur); 105 eina_cow_memcpy(cow, &prev, cur);
@@ -61,7 +110,7 @@ START_TEST(eina_cow)
61 fail_if(write == NULL || write == &default_value); 110 fail_if(write == NULL || write == &default_value);
62 111
63 write->i = 42; write->c = 5; 112 write->i = 42; write->c = 5;
64 eina_cow_commit(cow, &cur, write); 113 eina_cow_done(cow, &cur, write);
65 fail_if(cur->i != 42 || cur->c != 5 || 114 fail_if(cur->i != 42 || cur->c != 5 ||
66 prev->i != 7 || prev->c != 42 || 115 prev->i != 7 || prev->c != 42 ||
67 default_value.c != 42 || default_value.i != 0); 116 default_value.c != 42 || default_value.i != 0);
@@ -71,10 +120,15 @@ START_TEST(eina_cow)
71 120
72 write = eina_cow_write(cow, &cur); 121 write = eina_cow_write(cow, &cur);
73 write->i = 7; write->c = 42; 122 write->i = 7; write->c = 42;
74 eina_cow_commit(cow, &cur, write); 123 eina_cow_done(cow, &cur, write);
75 124
76 fail_if(eina_cow_gc(cow) == EINA_FALSE); 125 fail_if(eina_cow_gc(cow) == EINA_FALSE);
77 fail_if(cur != prev); 126 fail_if(cur != prev);
127
128 eina_cow_free(cow, cur);
129 eina_cow_free(cow, prev);
130
131 eina_cow_del(cow);
78} 132}
79END_TEST 133END_TEST
80 134
@@ -82,4 +136,5 @@ void
82eina_test_cow(TCase *tc) 136eina_test_cow(TCase *tc)
83{ 137{
84 tcase_add_test(tc, eina_cow); 138 tcase_add_test(tc, eina_cow);
139 tcase_add_test(tc, eina_cow_bad);
85} 140}