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
This commit is contained in:
Cedric BAIL 2013-01-14 03:34:19 +00:00
parent c42971cd27
commit c7aead6fdc
3 changed files with 152 additions and 67 deletions

View File

@ -1,4 +1,4 @@
/* EINA - EFL data type library
/* Eina - EFL data type library
* Copyright (C) 2013 Cedric Bail
*
* This library is free software; you can redistribute it and/or
@ -21,6 +21,8 @@
#endif
#include "eina_config.h"
#include "eina_private.h"
#include "eina_log.h"
#include "eina_mempool.h"
#include "eina_types.h"
#include "eina_safety_checks.h"
@ -38,13 +40,13 @@ struct _Eina_Cow_Ptr
{
int refcount;
Eina_Bool hashed;
Eina_Bool togc;
Eina_Bool hashed : 1;
Eina_Bool togc : 1;
Eina_Bool writing : 1;
};
struct _Eina_Cow_GC
{
Eina_List *togc;
Eina_Cow_Ptr *ref;
const void * const *dst;
};
@ -70,6 +72,27 @@ typedef int (*Eina_Cow_Hash)(const void *, int);
EINA_MAGIC_FAIL((d), EINA_COW_MAGIC); \
} while (0);
#define EINA_COW_PTR_SIZE \
(sizeof (Eina_Cow_Ptr) < sizeof (void*) ? sizeof (void*) : sizeof (Eina_Cow_Ptr))
#define EINA_COW_PTR_GET(d) \
(Eina_Cow_Ptr*) (((unsigned char *)d) - EINA_COW_PTR_SIZE)
#define EINA_COW_DATA_GET(d) \
(((unsigned char *)d) + EINA_COW_PTR_SIZE)
static int _eina_cow_log_dom = -1;
#ifdef ERR
#undef ERR
#endif
#define ERR(...) EINA_LOG_DOM_ERR(_eina_cow_log_dom, __VA_ARGS__)
#ifdef DBG
#undef DBG
#endif
#define DBG(...) EINA_LOG_DOM_DBG(_eina_cow_log_dom, __VA_ARGS__)
static Eina_Mempool *gc_pool = NULL;
static inline int
@ -113,8 +136,7 @@ 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.
of that global. access to the hash should be considered global.
*/
return current_cow_size;
}
@ -131,32 +153,31 @@ _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;
}
/* eina_cow_gc is not supposed to be thread safe */
if (!ref->hashed) return ;
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_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_cow_gc is not supposed to be thread safe */
if (!ref->togc) return ;
EINA_LIST_FOREACH(cow->togc, l, gc)
if (gc->ref == ref)
{
cow->togc = eina_list_remove_list(cow->togc, l);
eina_mempool_free(gc_pool, gc);
break;
}
ref->togc = EINA_FALSE;
}
Eina_Bool
@ -164,6 +185,13 @@ eina_cow_init(void)
{
const char *choice, *tmp;
_eina_cow_log_dom = eina_log_domain_register("eina_cow", EINA_LOG_COLOR_DEFAULT);
if (_eina_cow_log_dom < 0)
{
EINA_LOG_ERR("Could not register log domain: eina_cow");
return EINA_FALSE;
}
#ifdef EINA_DEFAULT_MEMPOOL
choice = "pass_through";
#else
@ -176,7 +204,7 @@ eina_cow_init(void)
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); */
ERR("Mempool for cow gc cannot be allocated.");
return EINA_FALSE;
}
return EINA_TRUE;
@ -213,10 +241,10 @@ eina_cow_add(const char *name, unsigned int struct_size, unsigned int step, cons
cow->pool = eina_mempool_add(choice, name,
NULL,
struct_size + sizeof (Eina_Cow_Ptr), step);
struct_size + EINA_COW_PTR_SIZE, step);
if (!cow->pool)
{
/* ERR("ERROR: Mempool for cow '%s' cannot be allocated in eina_cow_new.", name); */
ERR("Mempool for cow '%s' cannot be allocated.", name);
goto on_error;
}
@ -279,7 +307,7 @@ eina_cow_free(Eina_Cow *cow, const void *data)
if (!data) return ;
if (cow->default_value == data) return ;
ref = (Eina_Cow_Ptr*)(((unsigned char*) data) + cow->struct_size);
ref = EINA_COW_PTR_GET(data);
ref->refcount--;
if (ref->refcount > 0) return ;
@ -293,65 +321,67 @@ 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;
}
goto allocate;
ref = (Eina_Cow_Ptr*)(((unsigned char*) *data) + cow->struct_size);
ref = EINA_COW_PTR_GET(*data);
if (ref->refcount == 1)
{
if (ref->writing)
{
ERR("Request writing on an pointer that is already in a writing process %p\n", data);
return NULL;
}
_eina_cow_hash_del(cow, *data, ref);
return (void *) *data;
goto end;
}
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 = eina_mempool_malloc(cow->pool,
cow->struct_size + EINA_COW_PTR_SIZE);
ref->refcount = 1;
ref->hashed = EINA_FALSE;
ref->togc = EINA_FALSE;
r = EINA_COW_DATA_GET(ref);
memcpy(r, *data, cow->struct_size);
*((void**) data) = r;
return r;
end:
ref->writing = EINA_TRUE;
return (void *) *data;
}
EAPI void
eina_cow_commit(Eina_Cow *cow, const void * const * dst, const void *data)
eina_cow_done(Eina_Cow *cow, const void * const * dst, const void *data)
{
Eina_Cow_Ptr *ref;
Eina_Cow_GC *gc;
EINA_COW_MAGIC_CHECK(cow);
ref = (Eina_Cow_Ptr*)(((unsigned char*) data) + cow->struct_size);
ref = EINA_COW_PTR_GET(data);
if (!ref->writing)
ERR("Pointer %p is not in a writable state !", dst);
ref->writing = EINA_FALSE;
/* needed if we want to make cow gc safe */
if (!ref->togc)
{
Eina_Cow_GC *gc;
if (ref->togc) return ;
gc = eina_mempool_malloc(gc_pool, sizeof (Eina_Cow_GC));
if (!gc) return ; /* That one will not get gced this time */
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;
}
gc->ref = ref;
gc->dst = dst;
cow->togc = eina_list_prepend(cow->togc, gc);
ref->togc = EINA_TRUE;
}
EAPI void
@ -363,7 +393,7 @@ eina_cow_memcpy(Eina_Cow *cow, const void * const *dst, const void *src)
eina_cow_free(cow, *dst);
ref = (Eina_Cow_Ptr*)(((unsigned char*) src) + cow->struct_size);
ref = EINA_COW_PTR_GET(src);
ref->refcount++;
*((const void**)dst) = src;
@ -384,7 +414,7 @@ eina_cow_gc(Eina_Cow *cow)
/* 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;
data = EINA_COW_DATA_GET(gc->ref);
gc->ref->togc = EINA_FALSE;
cow->togc = eina_list_remove_list(cow->togc, eina_list_last(cow->togc));
@ -395,7 +425,7 @@ eina_cow_gc(Eina_Cow *cow)
{
eina_cow_free(cow, data);
ref = (Eina_Cow_Ptr*)(((unsigned char*) match) + cow->struct_size);
ref = EINA_COW_PTR_GET(match);
*((void**)gc->dst) = match;
ref->refcount++;
}

View File

@ -21,14 +21,14 @@
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 Eina_Cow *eina_cow_add(const char *name, unsigned int struct_size, unsigned int step, const void *default_value) EINA_WARN_UNUSED_RESULT;
EAPI void eina_cow_del(Eina_Cow *cow);
EAPI const void *eina_cow_alloc(Eina_Cow *cow);
EAPI const void *eina_cow_alloc(Eina_Cow *cow) EINA_WARN_UNUSED_RESULT;
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_write(Eina_Cow *cow, const void * const *src) EINA_WARN_UNUSED_RESULT;
EAPI void eina_cow_done(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);

View File

@ -31,6 +31,55 @@ struct _Eina_Cow_Test
void *d;
};
static void
_eina_test_log(const Eina_Log_Domain *d EINA_UNUSED,
Eina_Log_Level level EINA_UNUSED, const char *file EINA_UNUSED, const char *fnc EINA_UNUSED, int line EINA_UNUSED,
const char *fmt EINA_UNUSED, void *data, va_list args EINA_UNUSED)
{
Eina_Bool *bol = data;
*bol = EINA_TRUE;
}
START_TEST(eina_cow_bad)
{
const Eina_Cow_Test *cur;
Eina_Cow_Test *write;
Eina_Cow *cow;
Eina_Bool over_commit = EINA_FALSE;
Eina_Bool over_writing = EINA_FALSE;
Eina_Cow_Test default_value = { 7, 42, NULL };
cow = eina_cow_add("COW Test", sizeof (Eina_Cow_Test), 16, &default_value);
fail_if(cow == NULL);
cur = eina_cow_alloc(cow);
fail_if(cur == NULL);
write = eina_cow_write(cow, &cur);
fail_if(write == NULL || write == &default_value);
write->i = 7;
eina_cow_done(cow, &cur, write);
fail_if(cur->i != 7 || default_value.i != 42);
eina_log_print_cb_set(_eina_test_log, &over_commit);
eina_cow_done(cow, &cur, write); /* Testing over commit */
fail_if(!over_commit);
write = eina_cow_write(cow, &cur);
fail_if(write == NULL || write == &default_value);
eina_log_print_cb_set(_eina_test_log, &over_writing);
write = eina_cow_write(cow, &cur); /* Testing over writing */
fail_if(write != NULL || !over_writing);
eina_cow_free(cow, cur);
eina_cow_del(cow);
}
END_TEST
START_TEST(eina_cow)
{
const Eina_Cow_Test *prev;
@ -50,7 +99,7 @@ START_TEST(eina_cow)
fail_if(write == NULL || write == &default_value);
write->i = 7;
eina_cow_commit(cow, &cur, write);
eina_cow_done(cow, &cur, write);
fail_if(cur->i != 7 || prev->i != 0);
eina_cow_memcpy(cow, &prev, cur);
@ -61,7 +110,7 @@ START_TEST(eina_cow)
fail_if(write == NULL || write == &default_value);
write->i = 42; write->c = 5;
eina_cow_commit(cow, &cur, write);
eina_cow_done(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);
@ -71,10 +120,15 @@ START_TEST(eina_cow)
write = eina_cow_write(cow, &cur);
write->i = 7; write->c = 42;
eina_cow_commit(cow, &cur, write);
eina_cow_done(cow, &cur, write);
fail_if(eina_cow_gc(cow) == EINA_FALSE);
fail_if(cur != prev);
eina_cow_free(cow, cur);
eina_cow_free(cow, prev);
eina_cow_del(cow);
}
END_TEST
@ -82,4 +136,5 @@ void
eina_test_cow(TCase *tc)
{
tcase_add_test(tc, eina_cow);
tcase_add_test(tc, eina_cow_bad);
}