efl: Add eina copy on write infrastructure.

SVN revision: 82396
This commit is contained in:
Cedric BAIL 2013-01-08 09:17:56 +00:00
parent f7eac32ad2
commit 0e50f122e5
8 changed files with 545 additions and 4 deletions

View File

@ -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 = \

View File

@ -262,6 +262,7 @@ extern "C" {
#include "eina_mmap.h"
#include "eina_xattr.h"
#include "eina_value.h"
#include "eina_cow.h"
#ifdef __cplusplus
}

412
src/lib/eina/eina_cow.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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;
}

36
src/lib/eina/eina_cow.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View File

@ -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)
*/

View File

@ -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 }

View File

@ -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_ */

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
}