From 0e50f122e56ece29fd4299cdfcfe337f426e95e7 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Tue, 8 Jan 2013 09:17:56 +0000 Subject: [PATCH] efl: Add eina copy on write infrastructure. SVN revision: 82396 --- src/Makefile_Eina.am | 9 +- src/lib/eina/Eina.h | 1 + src/lib/eina/eina_cow.c | 412 +++++++++++++++++++++++++++++++++ src/lib/eina/eina_cow.h | 36 +++ src/lib/eina/eina_main.c | 4 +- src/tests/eina/eina_suite.c | 1 + src/tests/eina/eina_suite.h | 1 + src/tests/eina/eina_test_cow.c | 85 +++++++ 8 files changed, 545 insertions(+), 4 deletions(-) create mode 100644 src/lib/eina/eina_cow.c create mode 100644 src/lib/eina/eina_cow.h create mode 100644 src/tests/eina/eina_test_cow.c 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 \ lib/eina/eina_inline_value.x \ lib/eina/eina_inline_lock_barrier.x \ lib/eina/eina_tmpstr.h \ -lib/eina/eina_alloca.h +lib/eina/eina_alloca.h \ +lib/eina/eina_cow.h # Will be back for developper after 1.2. # lib/eina/eina_model.h @@ -140,7 +141,8 @@ lib/eina/eina_xattr.c \ lib/eina/eina_share_common.h \ lib/eina/eina_private.h \ lib/eina/eina_strbuf_common.h \ -lib/eina/eina_tmpstr.c +lib/eina/eina_tmpstr.c \ +lib/eina/eina_cow.c # Will be back for developper after 1.2 # lib/eina/eina_model.c \ @@ -273,7 +275,8 @@ tests/eina/eina_test_strbuf.c \ tests/eina/eina_test_str.c \ tests/eina/eina_test_quadtree.c \ tests/eina/eina_test_simple_xml_parser.c \ -tests/eina/eina_test_value.c +tests/eina/eina_test_value.c \ +tests/eina/eina_test_cow.c # tests/eina/eina_test_model.c tests_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" { #include "eina_mmap.h" #include "eina_xattr.h" #include "eina_value.h" +#include "eina_cow.h" #ifdef __cplusplus } 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 @@ +/* EINA - EFL data type library + * Copyright (C) 2013 Cedric Bail + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; + * if not, see . + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "eina_config.h" +#include "eina_mempool.h" +#include "eina_types.h" +#include "eina_safety_checks.h" +#include "eina_list.h" +#include "eina_hash.h" + +#include "eina_cow.h" + +#define EINA_COW_MAGIC 0xDEADBEEF + +typedef struct _Eina_Cow_Ptr Eina_Cow_Ptr; +typedef struct _Eina_Cow_GC Eina_Cow_GC; + +struct _Eina_Cow_Ptr +{ + int refcount; + + Eina_Bool hashed; + Eina_Bool togc; +}; + +struct _Eina_Cow_GC +{ + Eina_List *togc; + Eina_Cow_Ptr *ref; + const void * const *dst; +}; + +struct _Eina_Cow +{ + EINA_MAGIC; + + Eina_List *togc; + Eina_Hash *match; + + Eina_Mempool *pool; + const void *default_value; + + unsigned int struct_size; +}; + +typedef int (*Eina_Cow_Hash)(const void *, int); + +#define EINA_COW_MAGIC_CHECK(d) \ + do { \ + if (!EINA_MAGIC_CHECK((d), EINA_COW_MAGIC)) \ + EINA_MAGIC_FAIL((d), EINA_COW_MAGIC); \ + } while (0); + +static Eina_Mempool *gc_pool = NULL; + +static inline int +_eina_cow_hash_gen(const void *key, int key_length, + Eina_Cow_Hash hash, + int size) +{ + const unsigned char *walk = key; + int r = 0xDEADBEEF; + + while (key_length > 0) + { + r ^= hash(walk, size); + + walk += size; + key_length -= size; + } + + return r; +} + +#ifdef __LP64__ +static int +_eina_cow_hash64(const void *key, int key_length) +{ + return _eina_cow_hash_gen(key, key_length, + (Eina_Cow_Hash) eina_hash_int64, sizeof (unsigned long int)); +} +#else +static int +_eina_cow_hash32(const void *key, int key_length) +{ + return _eina_cow_hash_gen(key, key_length, + (Eina_Cow_Hash) eina_hash_int32, sizeof (int)); +} +#endif + +static int current_cow_size = 0; + +static unsigned int +_eina_cow_length(const void *key EINA_UNUSED) +{ + /* nasty hack, has only gc need to access the hash, he will be in charge + of that global. access to the hash should be considered global. so a + lock will be needed to make multiple gc run at the same safely. + */ + return current_cow_size; +} + +static int +_eina_cow_cmp(const void *key1, int key1_length, + const void *key2, int key2_length EINA_UNUSED) +{ + return memcmp(key1, key2, key1_length); +} + +static void +_eina_cow_hash_del(Eina_Cow *cow, + const void *data, + Eina_Cow_Ptr *ref) +{ + /* if eina_cow_gc is supposed to be thread safe, lock the cow here */ + if (ref->hashed) + { + current_cow_size = cow->struct_size; + eina_hash_del(cow->match, data, ref); + ref->hashed = EINA_FALSE; + } +} + +static void +_eina_cow_togc_del(Eina_Cow *cow, Eina_Cow_Ptr *ref) +{ + /* if eina_cow_gc is supposed to be thread safe, lock the cow here */ + if (ref->togc) + { + Eina_Cow_GC *gc; + Eina_List *l; + + EINA_LIST_FOREACH(cow->togc, l, gc) + if (gc->ref == ref) + { + cow->togc = eina_list_remove_list(cow->togc, l); + break; + } + ref->togc = EINA_FALSE; + } +} + +Eina_Bool +eina_cow_init(void) +{ + const char *choice, *tmp; + +#ifdef EINA_DEFAULT_MEMPOOL + choice = "pass_through"; +#else + choice = "chained_mempool"; +#endif + tmp = getenv("EINA_MEMPOOL"); + if (tmp && tmp[0]) + choice = tmp; + + gc_pool = eina_mempool_add(choice, "gc", NULL, sizeof (Eina_Cow_GC), 32); + if (!gc_pool) + { + /* ERR("ERROR: Mempool for cow '%s' cannot be allocated in eina_cow_new.", name); */ + return EINA_FALSE; + } + return EINA_TRUE; +} + +Eina_Bool +eina_cow_shutdown(void) +{ + eina_mempool_del(gc_pool); + return EINA_TRUE; +} + +EAPI Eina_Cow * +eina_cow_add(const char *name, unsigned int struct_size, unsigned int step, const void *default_value) +{ + const char *choice, *tmp; + Eina_Cow *cow; + + EINA_SAFETY_ON_NULL_RETURN_VAL(default_value, NULL); + EINA_SAFETY_ON_FALSE_RETURN_VAL(struct_size, NULL); + EINA_SAFETY_ON_FALSE_RETURN_VAL(step, NULL); + + cow = malloc(sizeof (Eina_Cow)); + if (!cow) return NULL; + +#ifdef EINA_DEFAULT_MEMPOOL + choice = "pass_through"; +#else + choice = "chained_mempool"; +#endif + tmp = getenv("EINA_MEMPOOL"); + if (tmp && tmp[0]) + choice = tmp; + + cow->pool = eina_mempool_add(choice, name, + NULL, + struct_size + sizeof (Eina_Cow_Ptr), step); + if (!cow->pool) + { + /* ERR("ERROR: Mempool for cow '%s' cannot be allocated in eina_cow_new.", name); */ + goto on_error; + } + +#ifdef __LP64__ + cow->match = eina_hash_new(_eina_cow_length, + _eina_cow_cmp, + _eina_cow_hash64, + NULL, + 6); +#else + cow->match = eina_hash_new(_eina_cow_length, + _eina_cow_cmp, + _eina_cow_hash32, + NULL, + 6); +#endif + + cow->togc = NULL; + cow->default_value = default_value; + cow->struct_size = struct_size; + + EINA_MAGIC_SET(cow, EINA_COW_MAGIC); + + return cow; + + on_error: + free(cow); + return NULL; +} + +EAPI void +eina_cow_del(Eina_Cow *cow) +{ + Eina_Cow_GC *gc; + + EINA_COW_MAGIC_CHECK(cow); + + eina_mempool_del(cow->pool); + + EINA_LIST_FREE(cow->togc, gc) + eina_mempool_free(gc_pool, gc); + free(cow); +} + +EAPI const void * +eina_cow_alloc(Eina_Cow *cow) +{ + EINA_COW_MAGIC_CHECK(cow); + + return cow->default_value; +} + +EAPI void +eina_cow_free(Eina_Cow *cow, const void *data) +{ + Eina_Cow_Ptr *ref; + + EINA_COW_MAGIC_CHECK(cow); + + if (!data) return ; + if (cow->default_value == data) return ; + + ref = (Eina_Cow_Ptr*)(((unsigned char*) data) + cow->struct_size); + ref->refcount--; + + if (ref->refcount > 0) return ; + + _eina_cow_hash_del(cow, data, ref); + _eina_cow_togc_del(cow, ref); + eina_mempool_free(cow->pool, (void*) data); +} + +EAPI void * +eina_cow_write(Eina_Cow *cow, const void * const *data) +{ + Eina_Cow_Ptr *ref; + const void *src; + void *r; + + EINA_COW_MAGIC_CHECK(cow); + + if (!*data) return NULL; /* cow pointer is always != NULL */ + if (*data == cow->default_value) + { + src = cow->default_value; + goto allocate; + } + + ref = (Eina_Cow_Ptr*)(((unsigned char*) *data) + cow->struct_size); + + if (ref->refcount == 1) + { + _eina_cow_hash_del(cow, *data, ref); + return (void *) *data; + } + + src = *data; + + allocate: + r = eina_mempool_malloc(cow->pool, + cow->struct_size + sizeof (Eina_Cow_Ptr)); + memcpy(r, src, cow->struct_size); + + ref = (Eina_Cow_Ptr*)(((unsigned char*) r) + cow->struct_size); + ref->refcount = 1; + ref->hashed = EINA_FALSE; + ref->togc = EINA_FALSE; + + *((void**) data) = r; + + return r; +} + +EAPI void +eina_cow_commit(Eina_Cow *cow, const void * const * dst, const void *data) +{ + Eina_Cow_Ptr *ref; + + EINA_COW_MAGIC_CHECK(cow); + + ref = (Eina_Cow_Ptr*)(((unsigned char*) data) + cow->struct_size); + + /* needed if we want to make cow gc safe */ + if (!ref->togc) + { + Eina_Cow_GC *gc; + + gc = eina_mempool_malloc(gc_pool, sizeof (Eina_Cow_GC)); + if (!gc) return ; /* That one will not get gced this time */ + + gc->ref = ref; + gc->dst = dst; + cow->togc = gc->togc = eina_list_prepend(cow->togc, gc); + ref->togc = EINA_TRUE; + } +} + +EAPI void +eina_cow_memcpy(Eina_Cow *cow, const void * const *dst, const void *src) +{ + Eina_Cow_Ptr *ref; + + EINA_COW_MAGIC_CHECK(cow); + + eina_cow_free(cow, *dst); + + ref = (Eina_Cow_Ptr*)(((unsigned char*) src) + cow->struct_size); + ref->refcount++; + + *((const void**)dst) = src; +} + +EAPI Eina_Bool +eina_cow_gc(Eina_Cow *cow) +{ + Eina_Cow_Ptr *ref; + Eina_Cow_GC *gc; + void *data; + void *match; + + EINA_COW_MAGIC_CHECK(cow); + + if (!cow->togc) return EINA_FALSE; /* Nothing more to do */ + + /* Do handle hash and all funky merge think here */ + gc = eina_list_data_get(eina_list_last(cow->togc)); + + data = ((unsigned char*) gc->ref) - cow->struct_size; + + gc->ref->togc = EINA_FALSE; + cow->togc = eina_list_remove_list(cow->togc, eina_list_last(cow->togc)); + + current_cow_size = cow->struct_size; + match = eina_hash_find(cow->match, data); + if (match) + { + eina_cow_free(cow, data); + + ref = (Eina_Cow_Ptr*)(((unsigned char*) match) + cow->struct_size); + *((void**)gc->dst) = match; + ref->refcount++; + } + else + { + eina_hash_direct_add(cow->match, data, data); + gc->ref->hashed = EINA_TRUE; + } + + eina_mempool_free(gc_pool, gc); + + return EINA_TRUE; +} + 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 @@ +/* EINA - EFL data type library + * Copyright (C) 2013 Cedric Bail + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; + * if not, see . + */ + +#ifndef EINA_COW_H_ +#define EINA_COW_H_ + +typedef struct _Eina_Cow Eina_Cow; + +EAPI Eina_Cow *eina_cow_add(const char *name, unsigned int struct_size, unsigned int step, const void *default_value); +EAPI void eina_cow_del(Eina_Cow *cow); + +EAPI const void *eina_cow_alloc(Eina_Cow *cow); +EAPI void eina_cow_free(Eina_Cow *cow, const void *data); + +EAPI void *eina_cow_write(Eina_Cow *cow, const void * const *src); +EAPI void eina_cow_commit(Eina_Cow *cow, const void * const *dst, const void *data); +EAPI void eina_cow_memcpy(Eina_Cow *cow, const void * const *dst, const void *src); + +EAPI Eina_Bool eina_cow_gc(Eina_Cow *cow); + +#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; S(value); S(tmpstr); S(thread); + S(cow); /* no model for now S(model); */ @@ -199,7 +200,8 @@ static const struct eina_desc_setup _eina_desc_setup[] = { S(prefix), S(value), S(tmpstr), - S(thread) + S(thread), + S(cow) /* no model for now S(model) */ 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[] = { { "Sched", eina_test_sched }, { "Simple Xml Parser", eina_test_simple_xml_parser}, { "Value", eina_test_value }, + { "COW", eina_test_cow }, // Disabling Eina_Model test // { "Model", eina_test_model }, { 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); void eina_test_simple_xml_parser(TCase *tc); void eina_test_value(TCase *tc); void eina_test_model(TCase *tc); +void eina_test_cow(TCase *tc); #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 @@ +/* EINA - EFL data type library + * Copyright (C) 2012 Cedric Bail + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; + * if not, see . + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "eina_suite.h" +#include "Eina.h" + +typedef struct _Eina_Cow_Test Eina_Cow_Test; +struct _Eina_Cow_Test +{ + unsigned char c; + int i; + void *d; +}; + +START_TEST(eina_cow) +{ + const Eina_Cow_Test *prev; + const Eina_Cow_Test *cur; + Eina_Cow_Test *write; + Eina_Cow *cow; + Eina_Cow_Test default_value = { 42, 0, NULL }; + + cow = eina_cow_add("COW Test", sizeof (Eina_Cow_Test), 16, &default_value); + fail_if(cow == NULL); + + prev = eina_cow_alloc(cow); + cur = eina_cow_alloc(cow); + fail_if(prev == NULL || cur == NULL); + + write = eina_cow_write(cow, &cur); + fail_if(write == NULL || write == &default_value); + + write->i = 7; + eina_cow_commit(cow, &cur, write); + fail_if(cur->i != 7 || prev->i != 0); + + eina_cow_memcpy(cow, &prev, cur); + fail_if(cur->i != 7 || prev->i != 7); + fail_if(default_value.i != 0); + + write = eina_cow_write(cow, &cur); + fail_if(write == NULL || write == &default_value); + + write->i = 42; write->c = 5; + eina_cow_commit(cow, &cur, write); + fail_if(cur->i != 42 || cur->c != 5 || + prev->i != 7 || prev->c != 42 || + default_value.c != 42 || default_value.i != 0); + + fail_if(eina_cow_gc(cow) == EINA_FALSE); + fail_if(eina_cow_gc(cow) == EINA_FALSE); + + write = eina_cow_write(cow, &cur); + write->i = 7; write->c = 42; + eina_cow_commit(cow, &cur, write); + + fail_if(eina_cow_gc(cow) == EINA_FALSE); + fail_if(cur != prev); +} +END_TEST + +void +eina_test_cow(TCase *tc) +{ + tcase_add_test(tc, eina_cow); +}