summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCedric BAIL <cedric.bail@free.fr>2013-01-08 09:17:56 +0000
committerCedric BAIL <cedric.bail@free.fr>2013-01-08 09:17:56 +0000
commit0e50f122e56ece29fd4299cdfcfe337f426e95e7 (patch)
tree342c5b5ffc80e4c231b04a091540da82b5a729af /src
parentf7eac32ad2302c2a8a45021cf6dc0d0d9479ada8 (diff)
efl: Add eina copy on write infrastructure.
SVN revision: 82396
Diffstat (limited to '')
-rw-r--r--src/Makefile_Eina.am9
-rw-r--r--src/lib/eina/Eina.h1
-rw-r--r--src/lib/eina/eina_cow.c412
-rw-r--r--src/lib/eina/eina_cow.h36
-rw-r--r--src/lib/eina/eina_main.c4
-rw-r--r--src/tests/eina/eina_suite.c1
-rw-r--r--src/tests/eina/eina_suite.h1
-rw-r--r--src/tests/eina/eina_test_cow.c85
8 files changed, 545 insertions, 4 deletions
diff --git a/src/Makefile_Eina.am b/src/Makefile_Eina.am
index 9586e2bf6d..eca0bfe429 100644
--- a/src/Makefile_Eina.am
+++ b/src/Makefile_Eina.am
@@ -77,7 +77,8 @@ lib/eina/eina_value.h \
77lib/eina/eina_inline_value.x \ 77lib/eina/eina_inline_value.x \
78lib/eina/eina_inline_lock_barrier.x \ 78lib/eina/eina_inline_lock_barrier.x \
79lib/eina/eina_tmpstr.h \ 79lib/eina/eina_tmpstr.h \
80lib/eina/eina_alloca.h 80lib/eina/eina_alloca.h \
81lib/eina/eina_cow.h
81 82
82# Will be back for developper after 1.2. 83# Will be back for developper after 1.2.
83# lib/eina/eina_model.h 84# lib/eina/eina_model.h
@@ -140,7 +141,8 @@ lib/eina/eina_xattr.c \
140lib/eina/eina_share_common.h \ 141lib/eina/eina_share_common.h \
141lib/eina/eina_private.h \ 142lib/eina/eina_private.h \
142lib/eina/eina_strbuf_common.h \ 143lib/eina/eina_strbuf_common.h \
143lib/eina/eina_tmpstr.c 144lib/eina/eina_tmpstr.c \
145lib/eina/eina_cow.c
144 146
145# Will be back for developper after 1.2 147# Will be back for developper after 1.2
146# lib/eina/eina_model.c \ 148# lib/eina/eina_model.c \
@@ -273,7 +275,8 @@ tests/eina/eina_test_strbuf.c \
273tests/eina/eina_test_str.c \ 275tests/eina/eina_test_str.c \
274tests/eina/eina_test_quadtree.c \ 276tests/eina/eina_test_quadtree.c \
275tests/eina/eina_test_simple_xml_parser.c \ 277tests/eina/eina_test_simple_xml_parser.c \
276tests/eina/eina_test_value.c 278tests/eina/eina_test_value.c \
279tests/eina/eina_test_cow.c
277# tests/eina/eina_test_model.c 280# tests/eina/eina_test_model.c
278 281
279tests_eina_eina_suite_CPPFLAGS = \ 282tests_eina_eina_suite_CPPFLAGS = \
diff --git a/src/lib/eina/Eina.h b/src/lib/eina/Eina.h
index d7d1b86960..962701388f 100644
--- a/src/lib/eina/Eina.h
+++ b/src/lib/eina/Eina.h
@@ -262,6 +262,7 @@ extern "C" {
262#include "eina_mmap.h" 262#include "eina_mmap.h"
263#include "eina_xattr.h" 263#include "eina_xattr.h"
264#include "eina_value.h" 264#include "eina_value.h"
265#include "eina_cow.h"
265 266
266#ifdef __cplusplus 267#ifdef __cplusplus
267} 268}
diff --git a/src/lib/eina/eina_cow.c b/src/lib/eina/eina_cow.c
new file mode 100644
index 0000000000..e891b5c6c2
--- /dev/null
+++ b/src/lib/eina/eina_cow.c
@@ -0,0 +1,412 @@
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#include "eina_config.h"
24#include "eina_mempool.h"
25#include "eina_types.h"
26#include "eina_safety_checks.h"
27#include "eina_list.h"
28#include "eina_hash.h"
29
30#include "eina_cow.h"
31
32#define EINA_COW_MAGIC 0xDEADBEEF
33
34typedef struct _Eina_Cow_Ptr Eina_Cow_Ptr;
35typedef struct _Eina_Cow_GC Eina_Cow_GC;
36
37struct _Eina_Cow_Ptr
38{
39 int refcount;
40
41 Eina_Bool hashed;
42 Eina_Bool togc;
43};
44
45struct _Eina_Cow_GC
46{
47 Eina_List *togc;
48 Eina_Cow_Ptr *ref;
49 const void * const *dst;
50};
51
52struct _Eina_Cow
53{
54 EINA_MAGIC;
55
56 Eina_List *togc;
57 Eina_Hash *match;
58
59 Eina_Mempool *pool;
60 const void *default_value;
61
62 unsigned int struct_size;
63};
64
65typedef int (*Eina_Cow_Hash)(const void *, int);
66
67#define EINA_COW_MAGIC_CHECK(d) \
68 do { \
69 if (!EINA_MAGIC_CHECK((d), EINA_COW_MAGIC)) \
70 EINA_MAGIC_FAIL((d), EINA_COW_MAGIC); \
71 } while (0);
72
73static Eina_Mempool *gc_pool = NULL;
74
75static inline int
76_eina_cow_hash_gen(const void *key, int key_length,
77 Eina_Cow_Hash hash,
78 int size)
79{
80 const unsigned char *walk = key;
81 int r = 0xDEADBEEF;
82
83 while (key_length > 0)
84 {
85 r ^= hash(walk, size);
86
87 walk += size;
88 key_length -= size;
89 }
90
91 return r;
92}
93
94#ifdef __LP64__
95static int
96_eina_cow_hash64(const void *key, int key_length)
97{
98 return _eina_cow_hash_gen(key, key_length,
99 (Eina_Cow_Hash) eina_hash_int64, sizeof (unsigned long int));
100}
101#else
102static int
103_eina_cow_hash32(const void *key, int key_length)
104{
105 return _eina_cow_hash_gen(key, key_length,
106 (Eina_Cow_Hash) eina_hash_int32, sizeof (int));
107}
108#endif
109
110static int current_cow_size = 0;
111
112static unsigned int
113_eina_cow_length(const void *key EINA_UNUSED)
114{
115 /* 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
117 lock will be needed to make multiple gc run at the same safely.
118 */
119 return current_cow_size;
120}
121
122static int
123_eina_cow_cmp(const void *key1, int key1_length,
124 const void *key2, int key2_length EINA_UNUSED)
125{
126 return memcmp(key1, key2, key1_length);
127}
128
129static void
130_eina_cow_hash_del(Eina_Cow *cow,
131 const void *data,
132 Eina_Cow_Ptr *ref)
133{
134 /* if eina_cow_gc is supposed to be thread safe, lock the cow here */
135 if (ref->hashed)
136 {
137 current_cow_size = cow->struct_size;
138 eina_hash_del(cow->match, data, ref);
139 ref->hashed = EINA_FALSE;
140 }
141}
142
143static void
144_eina_cow_togc_del(Eina_Cow *cow, Eina_Cow_Ptr *ref)
145{
146 /* if eina_cow_gc is supposed to be thread safe, lock the cow here */
147 if (ref->togc)
148 {
149 Eina_Cow_GC *gc;
150 Eina_List *l;
151
152 EINA_LIST_FOREACH(cow->togc, l, gc)
153 if (gc->ref == ref)
154 {
155 cow->togc = eina_list_remove_list(cow->togc, l);
156 break;
157 }
158 ref->togc = EINA_FALSE;
159 }
160}
161
162Eina_Bool
163eina_cow_init(void)
164{
165 const char *choice, *tmp;
166
167#ifdef EINA_DEFAULT_MEMPOOL
168 choice = "pass_through";
169#else
170 choice = "chained_mempool";
171#endif
172 tmp = getenv("EINA_MEMPOOL");
173 if (tmp && tmp[0])
174 choice = tmp;
175
176 gc_pool = eina_mempool_add(choice, "gc", NULL, sizeof (Eina_Cow_GC), 32);
177 if (!gc_pool)
178 {
179 /* ERR("ERROR: Mempool for cow '%s' cannot be allocated in eina_cow_new.", name); */
180 return EINA_FALSE;
181 }
182 return EINA_TRUE;
183}
184
185Eina_Bool
186eina_cow_shutdown(void)
187{
188 eina_mempool_del(gc_pool);
189 return EINA_TRUE;
190}
191
192EAPI Eina_Cow *
193eina_cow_add(const char *name, unsigned int struct_size, unsigned int step, const void *default_value)
194{
195 const char *choice, *tmp;
196 Eina_Cow *cow;
197
198 EINA_SAFETY_ON_NULL_RETURN_VAL(default_value, NULL);
199 EINA_SAFETY_ON_FALSE_RETURN_VAL(struct_size, NULL);
200 EINA_SAFETY_ON_FALSE_RETURN_VAL(step, NULL);
201
202 cow = malloc(sizeof (Eina_Cow));
203 if (!cow) return NULL;
204
205#ifdef EINA_DEFAULT_MEMPOOL
206 choice = "pass_through";
207#else
208 choice = "chained_mempool";
209#endif
210 tmp = getenv("EINA_MEMPOOL");
211 if (tmp && tmp[0])
212 choice = tmp;
213
214 cow->pool = eina_mempool_add(choice, name,
215 NULL,
216 struct_size + sizeof (Eina_Cow_Ptr), step);
217 if (!cow->pool)
218 {
219 /* ERR("ERROR: Mempool for cow '%s' cannot be allocated in eina_cow_new.", name); */
220 goto on_error;
221 }
222
223#ifdef __LP64__
224 cow->match = eina_hash_new(_eina_cow_length,
225 _eina_cow_cmp,
226 _eina_cow_hash64,
227 NULL,
228 6);
229#else
230 cow->match = eina_hash_new(_eina_cow_length,
231 _eina_cow_cmp,
232 _eina_cow_hash32,
233 NULL,
234 6);
235#endif
236
237 cow->togc = NULL;
238 cow->default_value = default_value;
239 cow->struct_size = struct_size;
240
241 EINA_MAGIC_SET(cow, EINA_COW_MAGIC);
242
243 return cow;
244
245 on_error:
246 free(cow);
247 return NULL;
248}
249
250EAPI void
251eina_cow_del(Eina_Cow *cow)
252{
253 Eina_Cow_GC *gc;
254
255 EINA_COW_MAGIC_CHECK(cow);
256
257 eina_mempool_del(cow->pool);
258
259 EINA_LIST_FREE(cow->togc, gc)
260 eina_mempool_free(gc_pool, gc);
261 free(cow);
262}
263
264EAPI const void *
265eina_cow_alloc(Eina_Cow *cow)
266{
267 EINA_COW_MAGIC_CHECK(cow);
268
269 return cow->default_value;
270}
271
272EAPI void
273eina_cow_free(Eina_Cow *cow, const void *data)
274{
275 Eina_Cow_Ptr *ref;
276
277 EINA_COW_MAGIC_CHECK(cow);
278
279 if (!data) return ;
280 if (cow->default_value == data) return ;
281
282 ref = (Eina_Cow_Ptr*)(((unsigned char*) data) + cow->struct_size);
283 ref->refcount--;
284
285 if (ref->refcount > 0) return ;
286
287 _eina_cow_hash_del(cow, data, ref);
288 _eina_cow_togc_del(cow, ref);
289 eina_mempool_free(cow->pool, (void*) data);
290}
291
292EAPI void *
293eina_cow_write(Eina_Cow *cow, const void * const *data)
294{
295 Eina_Cow_Ptr *ref;
296 const void *src;
297 void *r;
298
299 EINA_COW_MAGIC_CHECK(cow);
300
301 if (!*data) return NULL; /* cow pointer is always != NULL */
302 if (*data == cow->default_value)
303 {
304 src = cow->default_value;
305 goto allocate;
306 }
307
308 ref = (Eina_Cow_Ptr*)(((unsigned char*) *data) + cow->struct_size);
309
310 if (ref->refcount == 1)
311 {
312 _eina_cow_hash_del(cow, *data, ref);
313 return (void *) *data;
314 }
315
316 src = *data;
317
318 allocate:
319 r = eina_mempool_malloc(cow->pool,
320 cow->struct_size + sizeof (Eina_Cow_Ptr));
321 memcpy(r, src, cow->struct_size);
322
323 ref = (Eina_Cow_Ptr*)(((unsigned char*) r) + cow->struct_size);
324 ref->refcount = 1;
325 ref->hashed = EINA_FALSE;
326 ref->togc = EINA_FALSE;
327
328 *((void**) data) = r;
329
330 return r;
331}
332
333EAPI void
334eina_cow_commit(Eina_Cow *cow, const void * const * dst, const void *data)
335{
336 Eina_Cow_Ptr *ref;
337
338 EINA_COW_MAGIC_CHECK(cow);
339
340 ref = (Eina_Cow_Ptr*)(((unsigned char*) data) + cow->struct_size);
341
342 /* needed if we want to make cow gc safe */
343 if (!ref->togc)
344 {
345 Eina_Cow_GC *gc;
346
347 gc = eina_mempool_malloc(gc_pool, sizeof (Eina_Cow_GC));
348 if (!gc) return ; /* That one will not get gced this time */
349
350 gc->ref = ref;
351 gc->dst = dst;
352 cow->togc = gc->togc = eina_list_prepend(cow->togc, gc);
353 ref->togc = EINA_TRUE;
354 }
355}
356
357EAPI void
358eina_cow_memcpy(Eina_Cow *cow, const void * const *dst, const void *src)
359{
360 Eina_Cow_Ptr *ref;
361
362 EINA_COW_MAGIC_CHECK(cow);
363
364 eina_cow_free(cow, *dst);
365
366 ref = (Eina_Cow_Ptr*)(((unsigned char*) src) + cow->struct_size);
367 ref->refcount++;
368
369 *((const void**)dst) = src;
370}
371
372EAPI Eina_Bool
373eina_cow_gc(Eina_Cow *cow)
374{
375 Eina_Cow_Ptr *ref;
376 Eina_Cow_GC *gc;
377 void *data;
378 void *match;
379
380 EINA_COW_MAGIC_CHECK(cow);
381
382 if (!cow->togc) return EINA_FALSE; /* Nothing more to do */
383
384 /* Do handle hash and all funky merge think here */
385 gc = eina_list_data_get(eina_list_last(cow->togc));
386
387 data = ((unsigned char*) gc->ref) - cow->struct_size;
388
389 gc->ref->togc = EINA_FALSE;
390 cow->togc = eina_list_remove_list(cow->togc, eina_list_last(cow->togc));
391
392 current_cow_size = cow->struct_size;
393 match = eina_hash_find(cow->match, data);
394 if (match)
395 {
396 eina_cow_free(cow, data);
397
398 ref = (Eina_Cow_Ptr*)(((unsigned char*) match) + cow->struct_size);
399 *((void**)gc->dst) = match;
400 ref->refcount++;
401 }
402 else
403 {
404 eina_hash_direct_add(cow->match, data, data);
405 gc->ref->hashed = EINA_TRUE;
406 }
407
408 eina_mempool_free(gc_pool, gc);
409
410 return EINA_TRUE;
411}
412
diff --git a/src/lib/eina/eina_cow.h b/src/lib/eina/eina_cow.h
new file mode 100644
index 0000000000..03d3c5c0c8
--- /dev/null
+++ b/src/lib/eina/eina_cow.h
@@ -0,0 +1,36 @@
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#ifndef EINA_COW_H_
20#define EINA_COW_H_
21
22typedef struct _Eina_Cow Eina_Cow;
23
24EAPI Eina_Cow *eina_cow_add(const char *name, unsigned int struct_size, unsigned int step, const void *default_value);
25EAPI void eina_cow_del(Eina_Cow *cow);
26
27EAPI const void *eina_cow_alloc(Eina_Cow *cow);
28EAPI void eina_cow_free(Eina_Cow *cow, const void *data);
29
30EAPI void *eina_cow_write(Eina_Cow *cow, const void * const *src);
31EAPI void eina_cow_commit(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);
33
34EAPI Eina_Bool eina_cow_gc(Eina_Cow *cow);
35
36#endif
diff --git a/src/lib/eina/eina_main.c b/src/lib/eina/eina_main.c
index fb2bb0df73..893ca8e5d4 100644
--- a/src/lib/eina/eina_main.c
+++ b/src/lib/eina/eina_main.c
@@ -158,6 +158,7 @@ EAPI Eina_Inlist *_eina_tracking = NULL;
158 S(value); 158 S(value);
159 S(tmpstr); 159 S(tmpstr);
160 S(thread); 160 S(thread);
161 S(cow);
161/* no model for now 162/* no model for now
162 S(model); 163 S(model);
163 */ 164 */
@@ -199,7 +200,8 @@ static const struct eina_desc_setup _eina_desc_setup[] = {
199 S(prefix), 200 S(prefix),
200 S(value), 201 S(value),
201 S(tmpstr), 202 S(tmpstr),
202 S(thread) 203 S(thread),
204 S(cow)
203/* no model for now 205/* no model for now
204 S(model) 206 S(model)
205 */ 207 */
diff --git a/src/tests/eina/eina_suite.c b/src/tests/eina/eina_suite.c
index 866db2780a..cceda023ae 100644
--- a/src/tests/eina/eina_suite.c
+++ b/src/tests/eina/eina_suite.c
@@ -68,6 +68,7 @@ static const Eina_Test_Case etc[] = {
68 { "Sched", eina_test_sched }, 68 { "Sched", eina_test_sched },
69 { "Simple Xml Parser", eina_test_simple_xml_parser}, 69 { "Simple Xml Parser", eina_test_simple_xml_parser},
70 { "Value", eina_test_value }, 70 { "Value", eina_test_value },
71 { "COW", eina_test_cow },
71 // Disabling Eina_Model test 72 // Disabling Eina_Model test
72 // { "Model", eina_test_model }, 73 // { "Model", eina_test_model },
73 { NULL, NULL } 74 { NULL, NULL }
diff --git a/src/tests/eina/eina_suite.h b/src/tests/eina/eina_suite.h
index d399298128..b85dad2de3 100644
--- a/src/tests/eina/eina_suite.h
+++ b/src/tests/eina/eina_suite.h
@@ -57,5 +57,6 @@ void eina_test_sched(TCase *tc);
57void eina_test_simple_xml_parser(TCase *tc); 57void eina_test_simple_xml_parser(TCase *tc);
58void eina_test_value(TCase *tc); 58void eina_test_value(TCase *tc);
59void eina_test_model(TCase *tc); 59void eina_test_model(TCase *tc);
60void eina_test_cow(TCase *tc);
60 61
61#endif /* EINA_SUITE_H_ */ 62#endif /* EINA_SUITE_H_ */
diff --git a/src/tests/eina/eina_test_cow.c b/src/tests/eina/eina_test_cow.c
new file mode 100644
index 0000000000..4d4d8e8f8a
--- /dev/null
+++ b/src/tests/eina/eina_test_cow.c
@@ -0,0 +1,85 @@
1/* EINA - EFL data type library
2 * Copyright (C) 2012 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#include "eina_suite.h"
24#include "Eina.h"
25
26typedef struct _Eina_Cow_Test Eina_Cow_Test;
27struct _Eina_Cow_Test
28{
29 unsigned char c;
30 int i;
31 void *d;
32};
33
34START_TEST(eina_cow)
35{
36 const Eina_Cow_Test *prev;
37 const Eina_Cow_Test *cur;
38 Eina_Cow_Test *write;
39 Eina_Cow *cow;
40 Eina_Cow_Test default_value = { 42, 0, NULL };
41
42 cow = eina_cow_add("COW Test", sizeof (Eina_Cow_Test), 16, &default_value);
43 fail_if(cow == NULL);
44
45 prev = eina_cow_alloc(cow);
46 cur = eina_cow_alloc(cow);
47 fail_if(prev == NULL || cur == NULL);
48
49 write = eina_cow_write(cow, &cur);
50 fail_if(write == NULL || write == &default_value);
51
52 write->i = 7;
53 eina_cow_commit(cow, &cur, write);
54 fail_if(cur->i != 7 || prev->i != 0);
55
56 eina_cow_memcpy(cow, &prev, cur);
57 fail_if(cur->i != 7 || prev->i != 7);
58 fail_if(default_value.i != 0);
59
60 write = eina_cow_write(cow, &cur);
61 fail_if(write == NULL || write == &default_value);
62
63 write->i = 42; write->c = 5;
64 eina_cow_commit(cow, &cur, write);
65 fail_if(cur->i != 42 || cur->c != 5 ||
66 prev->i != 7 || prev->c != 42 ||
67 default_value.c != 42 || default_value.i != 0);
68
69 fail_if(eina_cow_gc(cow) == EINA_FALSE);
70 fail_if(eina_cow_gc(cow) == EINA_FALSE);
71
72 write = eina_cow_write(cow, &cur);
73 write->i = 7; write->c = 42;
74 eina_cow_commit(cow, &cur, write);
75
76 fail_if(eina_cow_gc(cow) == EINA_FALSE);
77 fail_if(cur != prev);
78}
79END_TEST
80
81void
82eina_test_cow(TCase *tc)
83{
84 tcase_add_test(tc, eina_cow);
85}