2013-01-13 19:34:19 -08:00
|
|
|
/* Eina - EFL data type library
|
2013-01-08 01:17:56 -08:00
|
|
|
* 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
|
|
|
|
|
2013-03-19 05:15:44 -07:00
|
|
|
#ifdef HAVE_BACKTRACE
|
|
|
|
# include <execinfo.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef NVALGRIND
|
|
|
|
# include <memcheck.h>
|
|
|
|
#endif
|
|
|
|
|
2013-01-08 01:17:56 -08:00
|
|
|
#include "eina_config.h"
|
2013-01-13 19:34:19 -08:00
|
|
|
#include "eina_private.h"
|
|
|
|
#include "eina_log.h"
|
2013-01-08 01:17:56 -08:00
|
|
|
#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"
|
|
|
|
|
2013-03-18 23:11:50 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
2013-01-08 01:17:56 -08:00
|
|
|
#define EINA_COW_MAGIC 0xDEADBEEF
|
|
|
|
|
2013-01-23 17:42:20 -08:00
|
|
|
# define EINA_COW_PTR_MAGIC 0xBEEFE00
|
|
|
|
#endif
|
|
|
|
|
2013-01-08 01:17:56 -08:00
|
|
|
typedef struct _Eina_Cow_Ptr Eina_Cow_Ptr;
|
|
|
|
typedef struct _Eina_Cow_GC Eina_Cow_GC;
|
|
|
|
|
2013-03-03 18:08:21 -08:00
|
|
|
#ifdef HAVE_BACKTRACE
|
|
|
|
#define EINA_DEBUG_BT_NUM 64
|
|
|
|
typedef void (*Eina_Bt_Func) ();
|
|
|
|
#endif
|
|
|
|
|
2013-01-08 01:17:56 -08:00
|
|
|
struct _Eina_Cow_Ptr
|
|
|
|
{
|
2013-03-18 23:11:50 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
2013-01-23 17:42:20 -08:00
|
|
|
EINA_MAGIC;
|
2013-03-03 18:08:21 -08:00
|
|
|
# ifdef HAVE_BACKTRACE
|
|
|
|
Eina_Bt_Func writer_bt[EINA_DEBUG_BT_NUM];
|
|
|
|
int writer_bt_num;
|
|
|
|
# endif
|
2013-01-23 17:42:20 -08:00
|
|
|
#endif
|
2013-01-08 01:17:56 -08:00
|
|
|
int refcount;
|
|
|
|
|
2013-03-18 23:11:50 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
2018-06-15 08:39:53 -07:00
|
|
|
unsigned int writing;
|
2013-03-18 23:11:50 -07:00
|
|
|
#endif
|
2018-06-15 08:39:53 -07:00
|
|
|
|
|
|
|
Eina_Bool hashed : 1;
|
|
|
|
Eina_Bool togc : 1;
|
2013-01-08 01:17:56 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct _Eina_Cow_GC
|
|
|
|
{
|
2013-03-18 23:11:50 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
2013-01-23 17:42:20 -08:00
|
|
|
EINA_MAGIC;
|
2013-03-18 23:11:50 -07:00
|
|
|
#endif
|
2013-01-23 17:42:20 -08:00
|
|
|
|
2013-01-08 01:17:56 -08:00
|
|
|
Eina_Cow_Ptr *ref;
|
2013-11-03 19:45:25 -08:00
|
|
|
const void **dst;
|
2013-01-08 01:17:56 -08:00
|
|
|
};
|
|
|
|
struct _Eina_Cow
|
|
|
|
{
|
2013-03-18 23:11:50 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
2013-01-08 01:17:56 -08:00
|
|
|
EINA_MAGIC;
|
2013-03-18 23:11:50 -07:00
|
|
|
#endif
|
2013-01-08 01:17:56 -08:00
|
|
|
|
2013-03-18 23:11:50 -07:00
|
|
|
Eina_Hash *togc;
|
2013-01-08 01:17:56 -08:00
|
|
|
Eina_Hash *match;
|
|
|
|
|
|
|
|
Eina_Mempool *pool;
|
2013-11-25 14:33:51 -08:00
|
|
|
const Eina_Cow_Data *default_value;
|
2013-01-08 01:17:56 -08:00
|
|
|
|
|
|
|
unsigned int struct_size;
|
2013-01-23 17:42:20 -08:00
|
|
|
unsigned int total_size;
|
2013-01-08 01:17:56 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef int (*Eina_Cow_Hash)(const void *, int);
|
|
|
|
|
2013-03-18 23:11:50 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
|
|
|
# define EINA_COW_MAGIC_CHECK(d) \
|
2013-01-08 01:17:56 -08:00
|
|
|
do { \
|
|
|
|
if (!EINA_MAGIC_CHECK((d), EINA_COW_MAGIC)) \
|
|
|
|
EINA_MAGIC_FAIL((d), EINA_COW_MAGIC); \
|
|
|
|
} while (0);
|
|
|
|
|
2013-01-23 17:42:20 -08:00
|
|
|
# define EINA_COW_PTR_MAGIC_CHECK(d) \
|
|
|
|
do { \
|
|
|
|
if (!EINA_MAGIC_CHECK((d), EINA_COW_PTR_MAGIC)) \
|
|
|
|
EINA_MAGIC_FAIL((d), EINA_COW_PTR_MAGIC); \
|
|
|
|
} while (0);
|
|
|
|
#else
|
2013-03-18 23:11:50 -07:00
|
|
|
# define EINA_COW_MAGIC_CHECK(d)
|
2013-01-23 17:42:20 -08:00
|
|
|
# define EINA_COW_PTR_MAGIC_CHECK(d)
|
|
|
|
#endif
|
|
|
|
|
2013-01-13 19:34:19 -08:00
|
|
|
#define EINA_COW_PTR_SIZE \
|
2013-01-21 19:52:55 -08:00
|
|
|
eina_mempool_alignof(sizeof (Eina_Cow_Ptr))
|
2013-01-13 19:34:19 -08:00
|
|
|
|
|
|
|
#define EINA_COW_PTR_GET(d) \
|
2013-01-21 19:52:55 -08:00
|
|
|
(((Eina_Cow_Ptr *)d) - 1)
|
2013-01-13 19:34:19 -08:00
|
|
|
|
|
|
|
#define EINA_COW_DATA_GET(d) \
|
2013-11-25 14:33:51 -08:00
|
|
|
(((Eina_Cow_Ptr *)d) + 1)
|
2013-01-13 19:34:19 -08:00
|
|
|
|
|
|
|
static int _eina_cow_log_dom = -1;
|
|
|
|
|
|
|
|
#ifdef ERR
|
|
|
|
#undef ERR
|
|
|
|
#endif
|
|
|
|
#define ERR(...) EINA_LOG_DOM_ERR(_eina_cow_log_dom, __VA_ARGS__)
|
|
|
|
|
2013-04-04 07:43:23 -07:00
|
|
|
#ifdef INF
|
|
|
|
#undef INF
|
|
|
|
#endif
|
|
|
|
#define INF(...) EINA_LOG_DOM_INFO(_eina_cow_log_dom, __VA_ARGS__)
|
|
|
|
|
2013-01-13 19:34:19 -08:00
|
|
|
#ifdef DBG
|
|
|
|
#undef DBG
|
|
|
|
#endif
|
|
|
|
#define DBG(...) EINA_LOG_DOM_DBG(_eina_cow_log_dom, __VA_ARGS__)
|
|
|
|
|
2013-11-25 14:33:51 -08:00
|
|
|
|
2013-01-08 01:17:56 -08:00
|
|
|
static Eina_Mempool *gc_pool = NULL;
|
2013-11-25 14:33:51 -08:00
|
|
|
|
2013-01-08 01:17:56 -08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-11-09 02:48:31 -08:00
|
|
|
#ifdef EFL64
|
2013-01-08 01:17:56 -08:00
|
|
|
static int
|
|
|
|
_eina_cow_hash64(const void *key, int key_length)
|
|
|
|
{
|
|
|
|
return _eina_cow_hash_gen(key, key_length,
|
2013-11-09 03:43:20 -08:00
|
|
|
(Eina_Cow_Hash) eina_hash_int64, sizeof (unsigned long long int));
|
2013-01-08 01:17:56 -08:00
|
|
|
}
|
|
|
|
#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)
|
|
|
|
{
|
2013-11-25 14:33:51 -08:00
|
|
|
/* nasty hack, since only gc needs to access the hash, it will be in charge
|
2013-01-13 19:34:19 -08:00
|
|
|
of that global. access to the hash should be considered global.
|
2013-01-08 01:17:56 -08:00
|
|
|
*/
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-04-10 19:57:57 -07:00
|
|
|
static inline void
|
2013-01-08 01:17:56 -08:00
|
|
|
_eina_cow_hash_del(Eina_Cow *cow,
|
|
|
|
const void *data,
|
|
|
|
Eina_Cow_Ptr *ref)
|
|
|
|
{
|
2013-01-13 19:34:19 -08:00
|
|
|
/* eina_cow_gc is not supposed to be thread safe */
|
2013-06-20 04:28:18 -07:00
|
|
|
if (!ref->hashed) return;
|
2013-01-13 19:34:19 -08:00
|
|
|
|
|
|
|
current_cow_size = cow->struct_size;
|
2013-04-01 02:38:24 -07:00
|
|
|
eina_hash_del(cow->match, data, data);
|
2013-01-13 19:34:19 -08:00
|
|
|
ref->hashed = EINA_FALSE;
|
2013-01-08 01:17:56 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-03-18 23:11:50 -07:00
|
|
|
_eina_cow_gc_free(void *data)
|
2013-01-08 01:17:56 -08:00
|
|
|
{
|
2013-03-18 23:11:50 -07:00
|
|
|
eina_mempool_free(gc_pool, data);
|
|
|
|
}
|
2013-01-13 19:34:19 -08:00
|
|
|
|
2013-04-10 19:57:57 -07:00
|
|
|
static inline void
|
2013-03-18 23:11:50 -07:00
|
|
|
_eina_cow_togc_del(Eina_Cow *cow, Eina_Cow_Ptr *ref)
|
|
|
|
{
|
2013-01-13 19:34:19 -08:00
|
|
|
/* eina_cow_gc is not supposed to be thread safe */
|
2013-06-20 04:28:18 -07:00
|
|
|
if (!ref->togc) return;
|
2013-03-18 23:11:50 -07:00
|
|
|
eina_hash_del(cow->togc, &ref, NULL);
|
2013-01-13 19:34:19 -08:00
|
|
|
ref->togc = EINA_FALSE;
|
2013-01-08 01:17:56 -08:00
|
|
|
}
|
|
|
|
|
2013-04-01 21:41:47 -07:00
|
|
|
static void
|
|
|
|
_eina_cow_togc_add(Eina_Cow *cow,
|
|
|
|
Eina_Cow_Ptr *ref,
|
2013-11-03 19:45:25 -08:00
|
|
|
const Eina_Cow_Data ** dst)
|
2013-04-01 21:41:47 -07:00
|
|
|
{
|
|
|
|
Eina_Cow_GC *gc;
|
|
|
|
|
|
|
|
/* needed if we want to make cow gc safe */
|
2013-06-20 04:28:18 -07:00
|
|
|
if (ref->togc) return;
|
2013-04-01 21:41:47 -07:00
|
|
|
#ifndef NVALGRIND
|
|
|
|
VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
gc = eina_mempool_malloc(gc_pool, sizeof (Eina_Cow_GC));
|
2013-06-20 04:28:18 -07:00
|
|
|
if (!gc) return; /* That one will not get gced this time */
|
2013-04-01 21:41:47 -07:00
|
|
|
|
|
|
|
gc->ref = ref;
|
|
|
|
gc->dst = dst;
|
|
|
|
eina_hash_direct_add(cow->togc, &gc->ref, gc);
|
|
|
|
#ifndef NVALGRIND
|
|
|
|
VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
|
|
|
|
#endif
|
|
|
|
ref->togc = EINA_TRUE;
|
|
|
|
#ifndef NVALGRIND
|
|
|
|
VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-11-25 14:33:51 -08:00
|
|
|
_eina_cow_gc(Eina_Cow *cow, Eina_Cow_GC *gc)
|
2013-04-01 21:41:47 -07:00
|
|
|
{
|
2013-11-25 14:33:51 -08:00
|
|
|
Eina_Cow_Data *data;
|
|
|
|
Eina_Cow_Data *match;
|
2013-04-01 21:41:47 -07:00
|
|
|
|
2013-11-25 14:33:51 -08:00
|
|
|
data = EINA_COW_DATA_GET(gc->ref);
|
2013-04-01 21:41:47 -07:00
|
|
|
|
|
|
|
current_cow_size = cow->struct_size;
|
|
|
|
match = eina_hash_find(cow->match, data);
|
|
|
|
if (match)
|
|
|
|
{
|
2013-11-25 14:33:51 -08:00
|
|
|
Eina_Cow_Ptr *ref = EINA_COW_PTR_GET(match);
|
2013-04-01 21:41:47 -07:00
|
|
|
#ifndef NVALGRIND
|
|
|
|
VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
|
|
|
|
#endif
|
2013-11-25 14:33:51 -08:00
|
|
|
ref->refcount += gc->ref->refcount;
|
2013-04-01 21:41:47 -07:00
|
|
|
|
2013-11-25 14:33:51 -08:00
|
|
|
*gc->dst = match;
|
|
|
|
eina_cow_free(cow, (const Eina_Cow_Data**) &data);
|
2013-04-01 21:41:47 -07:00
|
|
|
|
|
|
|
#ifndef NVALGRIND
|
|
|
|
VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
eina_hash_direct_add(cow->match, data, data);
|
2013-11-25 14:33:51 -08:00
|
|
|
gc->ref->hashed = EINA_TRUE;
|
|
|
|
gc->ref->togc = EINA_FALSE;
|
|
|
|
eina_hash_del(cow->togc, &gc->ref, gc);
|
2013-04-01 21:41:47 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-08 01:17:56 -08:00
|
|
|
Eina_Bool
|
|
|
|
eina_cow_init(void)
|
|
|
|
{
|
|
|
|
const char *choice, *tmp;
|
|
|
|
|
2013-01-13 19:34:19 -08:00
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2013-01-08 01:17:56 -08:00
|
|
|
#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)
|
|
|
|
{
|
2013-01-13 19:34:19 -08:00
|
|
|
ERR("Mempool for cow gc cannot be allocated.");
|
2013-01-08 01:17:56 -08:00
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
Eina_Bool
|
|
|
|
eina_cow_shutdown(void)
|
|
|
|
{
|
eina: remove usless newline
Summary:
ecore_evas: remove debug
eina: unregister log level when done with
Fixes a constant memory leak.
eina: introduce EINA_HOT and EINA_COLD
These attributes respectivelly expand to __attribute__ ((hot)) and
__attribute__ ((cold)) when available. They allow to mark functions are
being hot/cold (frequently used or not) as well as to qualify labels
within a function (likely/unlikely branches).
eo: speed-up generated calls by removing call cache
The call cache needed to by thread-local, to avoid concurrency issues.
Problem with TLS is that is adds an extra overhead, which appears to be
greater than the optimization the cache provides.
Op is naturally atomic, because it is an unsigned integer. As such, it
cannot be tempered with while another thread is reading it. When
entering the generated function, the first operation done is reading
'op'. If we have concurrency, we will have access sequences returning
either EFL_NOOP or a VALID op, because 'op' is not set until the very
end of the function, when everything has been computed. As such, we now
use the 'op' atomic integer to instore a lock-free/wait-free mechanism,
which allows to drop the TLS nature of the cache, speeding up the access
to the cache, and therefore making functions execute faster.
We don't test anymore the generation count. This can be put as a
limitation. If means that if you call efl_object_shutdown() and
re-initialize it later with different data, opcodes will be invalid.
I am not sure there is any usecase for this to ever happen.
We could move all the caches in a dedicated section, that can be
overwritten after a call to efl_object_shutdown(), but I am not sure it
will be very portable.
Benchmark: mean over 3 executions of
ELM_TEST_AUTOBOUNCE=100 time elementary_test -to genlist
```
BEFORE AFTER
------------------------------------------------------------
time (ns) 11114111647.0 9147676220.0
frames 2872.3333333333335 2904.6666666666665
time per frame (ns) 3869364.6666666665 3149535.3333333335
user time (s) 11.096666666666666 9.22
cpu (%) 22.666666666666668 18.333333333333332
```
Ref T6580
Reviewers: raster, cedric
Subscribers: cedric, jpeg
Maniphest Tasks: T6580
Differential Revision: https://phab.enlightenment.org/D5738
2018-01-15 21:58:38 -08:00
|
|
|
eina_log_domain_unregister(_eina_cow_log_dom);
|
2013-01-08 01:17:56 -08:00
|
|
|
eina_mempool_del(gc_pool);
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI Eina_Cow *
|
2013-04-02 01:10:35 -07:00
|
|
|
eina_cow_add(const char *name, unsigned int struct_size, unsigned int step, const void *default_value, Eina_Bool gc)
|
2013-01-08 01:17:56 -08:00
|
|
|
{
|
|
|
|
const char *choice, *tmp;
|
|
|
|
Eina_Cow *cow;
|
2013-01-23 17:42:20 -08:00
|
|
|
unsigned int total_size;
|
2013-01-08 01:17:56 -08:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2013-04-04 07:43:23 -07:00
|
|
|
INF("Creating Cow '%s' with mempool of type '%s'", name, choice);
|
2013-01-23 17:42:20 -08:00
|
|
|
total_size = eina_mempool_alignof(struct_size + EINA_COW_PTR_SIZE);
|
|
|
|
cow->pool = eina_mempool_add(choice, name, NULL, total_size, step);
|
2013-01-08 01:17:56 -08:00
|
|
|
if (!cow->pool)
|
|
|
|
{
|
2013-01-13 19:34:19 -08:00
|
|
|
ERR("Mempool for cow '%s' cannot be allocated.", name);
|
2013-01-08 01:17:56 -08:00
|
|
|
goto on_error;
|
|
|
|
}
|
|
|
|
|
2013-11-09 02:48:31 -08:00
|
|
|
#ifdef EFL64
|
2013-01-08 01:17:56 -08:00
|
|
|
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,
|
2013-11-25 14:33:51 -08:00
|
|
|
6);
|
2013-01-08 01:17:56 -08:00
|
|
|
#endif
|
2013-04-02 01:10:35 -07:00
|
|
|
if (gc)
|
|
|
|
cow->togc = eina_hash_pointer_new(_eina_cow_gc_free);
|
|
|
|
else
|
|
|
|
cow->togc = NULL;
|
2013-01-08 01:17:56 -08:00
|
|
|
cow->default_value = default_value;
|
|
|
|
cow->struct_size = struct_size;
|
2013-01-23 17:42:20 -08:00
|
|
|
cow->total_size = total_size;
|
2013-01-08 01:17:56 -08:00
|
|
|
|
2013-03-18 23:11:50 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
2013-01-08 01:17:56 -08:00
|
|
|
EINA_MAGIC_SET(cow, EINA_COW_MAGIC);
|
2013-03-18 23:11:50 -07:00
|
|
|
#endif
|
2013-01-08 01:17:56 -08:00
|
|
|
|
|
|
|
return cow;
|
|
|
|
|
|
|
|
on_error:
|
|
|
|
free(cow);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI void
|
|
|
|
eina_cow_del(Eina_Cow *cow)
|
|
|
|
{
|
2013-06-20 04:28:18 -07:00
|
|
|
if (!cow) return;
|
2013-01-17 02:29:31 -08:00
|
|
|
|
2013-03-18 23:11:50 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
2013-01-08 01:17:56 -08:00
|
|
|
EINA_COW_MAGIC_CHECK(cow);
|
2013-03-18 23:11:50 -07:00
|
|
|
#endif
|
2013-01-08 01:17:56 -08:00
|
|
|
|
|
|
|
eina_mempool_del(cow->pool);
|
2013-01-20 03:02:48 -08:00
|
|
|
eina_hash_free(cow->match);
|
2013-04-02 01:10:35 -07:00
|
|
|
if (cow->togc) eina_hash_free(cow->togc);
|
2013-01-08 01:17:56 -08:00
|
|
|
free(cow);
|
|
|
|
}
|
|
|
|
|
2013-01-16 22:51:35 -08:00
|
|
|
EAPI const Eina_Cow_Data *
|
2013-01-08 01:17:56 -08:00
|
|
|
eina_cow_alloc(Eina_Cow *cow)
|
|
|
|
{
|
2013-03-18 23:11:50 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
2013-01-08 01:17:56 -08:00
|
|
|
EINA_COW_MAGIC_CHECK(cow);
|
2013-03-18 23:11:50 -07:00
|
|
|
#endif
|
2013-01-08 01:17:56 -08:00
|
|
|
|
|
|
|
return cow->default_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI void
|
2013-11-03 19:45:25 -08:00
|
|
|
eina_cow_free(Eina_Cow *cow, const Eina_Cow_Data **data)
|
2013-01-08 01:17:56 -08:00
|
|
|
{
|
|
|
|
Eina_Cow_Ptr *ref;
|
|
|
|
|
2013-03-18 23:11:50 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
2013-01-08 01:17:56 -08:00
|
|
|
EINA_COW_MAGIC_CHECK(cow);
|
2013-03-18 23:11:50 -07:00
|
|
|
#endif
|
2013-01-08 01:17:56 -08:00
|
|
|
|
2013-11-03 19:45:25 -08:00
|
|
|
if (!data || !*data) return;
|
|
|
|
if (cow->default_value == *data) return;
|
2013-01-08 01:17:56 -08:00
|
|
|
|
2013-11-03 19:45:25 -08:00
|
|
|
ref = EINA_COW_PTR_GET(*data);
|
2013-01-23 17:42:20 -08:00
|
|
|
#ifndef NVALGRIND
|
2013-03-22 04:11:43 -07:00
|
|
|
VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
|
2013-01-23 17:42:20 -08:00
|
|
|
#endif
|
2013-01-08 01:17:56 -08:00
|
|
|
ref->refcount--;
|
2013-01-24 13:42:18 -08:00
|
|
|
|
2013-11-17 00:11:56 -08:00
|
|
|
if (ref->refcount == 0) _eina_cow_hash_del(cow, *data, ref);
|
2013-11-03 19:45:25 -08:00
|
|
|
*data = (Eina_Cow_Data*) cow->default_value;
|
|
|
|
|
2013-03-22 04:11:43 -07:00
|
|
|
if (ref->refcount > 0)
|
|
|
|
{
|
2013-01-23 17:42:20 -08:00
|
|
|
#ifndef NVALGRIND
|
2013-03-22 04:11:43 -07:00
|
|
|
VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
|
2013-01-23 17:42:20 -08:00
|
|
|
#endif
|
2013-06-20 04:28:18 -07:00
|
|
|
return;
|
2013-03-22 04:11:43 -07:00
|
|
|
}
|
2013-01-08 01:17:56 -08:00
|
|
|
|
2013-03-18 23:11:50 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
2013-01-23 17:42:20 -08:00
|
|
|
EINA_MAGIC_SET(ref, EINA_MAGIC_NONE);
|
|
|
|
#endif
|
2013-01-08 01:17:56 -08:00
|
|
|
_eina_cow_togc_del(cow, ref);
|
2013-01-23 17:42:20 -08:00
|
|
|
eina_mempool_free(cow->pool, (void*) ref);
|
2013-01-08 01:17:56 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
EAPI void *
|
2013-01-16 22:51:35 -08:00
|
|
|
eina_cow_write(Eina_Cow *cow,
|
|
|
|
const Eina_Cow_Data * const *data)
|
2013-01-08 01:17:56 -08:00
|
|
|
{
|
|
|
|
Eina_Cow_Ptr *ref;
|
2013-11-25 14:33:51 -08:00
|
|
|
Eina_Cow_Data *r;
|
2013-01-08 01:17:56 -08:00
|
|
|
|
2013-03-18 23:11:50 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
2013-01-08 01:17:56 -08:00
|
|
|
EINA_COW_MAGIC_CHECK(cow);
|
2013-03-18 23:11:50 -07:00
|
|
|
#endif
|
2013-01-08 01:17:56 -08:00
|
|
|
|
|
|
|
if (!*data) return NULL; /* cow pointer is always != NULL */
|
|
|
|
if (*data == cow->default_value)
|
2013-01-13 19:34:19 -08:00
|
|
|
goto allocate;
|
2013-01-08 01:17:56 -08:00
|
|
|
|
2013-01-13 19:34:19 -08:00
|
|
|
ref = EINA_COW_PTR_GET(*data);
|
2013-01-08 01:17:56 -08:00
|
|
|
|
2013-01-24 13:06:41 -08:00
|
|
|
#ifndef NVALGRIND
|
2013-03-19 18:59:22 -07:00
|
|
|
VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
|
2013-01-24 13:06:41 -08:00
|
|
|
#endif
|
2013-01-08 01:17:56 -08:00
|
|
|
if (ref->refcount == 1)
|
|
|
|
{
|
2013-03-18 23:11:50 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
2013-01-23 17:42:20 -08:00
|
|
|
EINA_COW_PTR_MAGIC_CHECK(ref);
|
|
|
|
|
2018-06-15 08:39:53 -07:00
|
|
|
if (ref->writing && ref->togc && ref->hashed)
|
2013-01-13 19:34:19 -08:00
|
|
|
{
|
2018-06-15 08:39:53 -07:00
|
|
|
ERR("Request writing on a GC-ed pointer that is already in a writing process %p\n", data);
|
2013-03-18 23:11:50 -07:00
|
|
|
#ifdef HAVE_BACKTRACE
|
2013-03-03 18:08:21 -08:00
|
|
|
backtrace_symbols_fd((void **) ref->writer_bt,
|
|
|
|
ref->writer_bt_num, 1);
|
|
|
|
#endif
|
2013-01-13 19:34:19 -08:00
|
|
|
return NULL;
|
|
|
|
}
|
2013-03-18 23:11:50 -07:00
|
|
|
#endif
|
2013-01-13 19:34:19 -08:00
|
|
|
|
2013-11-25 14:33:51 -08:00
|
|
|
if (cow->togc)
|
|
|
|
_eina_cow_hash_del(cow, *data, ref);
|
2013-04-10 19:57:57 -07:00
|
|
|
|
2013-01-23 17:42:20 -08:00
|
|
|
#ifndef NVALGRIND
|
2013-03-19 18:59:22 -07:00
|
|
|
VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
|
2013-01-23 17:42:20 -08:00
|
|
|
#endif
|
2013-01-13 19:34:19 -08:00
|
|
|
goto end;
|
2013-01-08 01:17:56 -08:00
|
|
|
}
|
2013-01-27 16:22:21 -08:00
|
|
|
ref->refcount--;
|
2013-01-08 01:17:56 -08:00
|
|
|
|
|
|
|
allocate:
|
2013-01-23 17:42:20 -08:00
|
|
|
ref = eina_mempool_malloc(cow->pool, cow->total_size);
|
2013-01-08 01:17:56 -08:00
|
|
|
ref->refcount = 1;
|
2018-06-15 08:39:53 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
|
|
|
ref->writing = 0;
|
|
|
|
#endif
|
2013-01-08 01:17:56 -08:00
|
|
|
ref->hashed = EINA_FALSE;
|
|
|
|
ref->togc = EINA_FALSE;
|
2013-03-19 18:59:22 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
|
|
|
EINA_MAGIC_SET(ref, EINA_COW_PTR_MAGIC);
|
|
|
|
#endif
|
|
|
|
|
2013-01-23 17:42:20 -08:00
|
|
|
#ifndef NVALGRIND
|
2013-03-19 18:59:22 -07:00
|
|
|
VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
|
2013-01-23 17:42:20 -08:00
|
|
|
#endif
|
2013-01-08 01:17:56 -08:00
|
|
|
|
2013-01-13 19:34:19 -08:00
|
|
|
r = EINA_COW_DATA_GET(ref);
|
|
|
|
memcpy(r, *data, cow->struct_size);
|
2013-11-25 14:33:51 -08:00
|
|
|
*((Eina_Cow_Data**) data) = r;
|
2013-01-08 01:17:56 -08:00
|
|
|
|
2013-01-13 19:34:19 -08:00
|
|
|
end:
|
2013-01-23 17:42:20 -08:00
|
|
|
#ifndef NVALGRIND
|
2013-03-19 18:59:22 -07:00
|
|
|
VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
|
2013-03-03 18:08:21 -08:00
|
|
|
#endif
|
2013-03-18 23:11:50 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
|
|
|
# ifdef HAVE_BACKTRACE
|
2013-03-03 18:08:21 -08:00
|
|
|
ref->writer_bt_num = backtrace((void **)(ref->writer_bt),
|
|
|
|
EINA_DEBUG_BT_NUM);
|
2013-03-18 23:11:50 -07:00
|
|
|
# endif
|
2018-06-15 08:39:53 -07:00
|
|
|
ref->writing++;
|
2013-03-18 23:11:50 -07:00
|
|
|
#endif
|
2013-01-23 17:42:20 -08:00
|
|
|
#ifndef NVALGRIND
|
2013-03-19 18:59:22 -07:00
|
|
|
VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
|
2013-01-23 17:42:20 -08:00
|
|
|
#endif
|
|
|
|
|
2013-01-13 19:34:19 -08:00
|
|
|
return (void *) *data;
|
2013-01-08 01:17:56 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
EAPI void
|
2013-01-16 22:51:35 -08:00
|
|
|
eina_cow_done(Eina_Cow *cow,
|
|
|
|
const Eina_Cow_Data * const * dst,
|
2013-03-19 05:15:44 -07:00
|
|
|
const void *data,
|
|
|
|
Eina_Bool needed_gc)
|
2013-01-08 01:17:56 -08:00
|
|
|
{
|
|
|
|
Eina_Cow_Ptr *ref;
|
|
|
|
|
|
|
|
EINA_COW_MAGIC_CHECK(cow);
|
|
|
|
|
2013-01-13 19:34:19 -08:00
|
|
|
ref = EINA_COW_PTR_GET(data);
|
2013-01-23 17:42:20 -08:00
|
|
|
#ifndef NVALGRIND
|
2013-03-19 18:59:22 -07:00
|
|
|
VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
|
2013-01-23 17:42:20 -08:00
|
|
|
#endif
|
2013-03-19 18:59:22 -07:00
|
|
|
EINA_COW_PTR_MAGIC_CHECK(ref);
|
2013-03-18 23:11:50 -07:00
|
|
|
#ifdef EINA_COW_MAGIC_ON
|
2013-01-24 13:06:41 -08:00
|
|
|
if (!ref->writing)
|
|
|
|
ERR("Pointer %p is not in a writable state !", dst);
|
|
|
|
|
2018-06-15 08:39:53 -07:00
|
|
|
ref->writing--;
|
2013-03-18 23:11:50 -07:00
|
|
|
#endif
|
2013-03-19 18:59:22 -07:00
|
|
|
#ifndef NVALGRIND
|
|
|
|
VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
|
|
|
|
#endif
|
2013-01-08 01:17:56 -08:00
|
|
|
|
2013-06-20 04:28:18 -07:00
|
|
|
if (!cow->togc || !needed_gc) return;
|
2013-03-19 05:15:44 -07:00
|
|
|
|
2013-03-20 13:18:49 -07:00
|
|
|
#ifndef NVALGRIND
|
|
|
|
VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
|
|
|
|
#endif
|
2013-01-08 01:17:56 -08:00
|
|
|
|
2013-11-03 19:45:25 -08:00
|
|
|
_eina_cow_togc_add(cow, ref, (const Eina_Cow_Data **) dst);
|
2013-01-08 01:17:56 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
EAPI void
|
2013-01-16 22:51:35 -08:00
|
|
|
eina_cow_memcpy(Eina_Cow *cow,
|
|
|
|
const Eina_Cow_Data * const *dst,
|
|
|
|
const Eina_Cow_Data *src)
|
2013-01-08 01:17:56 -08:00
|
|
|
{
|
|
|
|
Eina_Cow_Ptr *ref;
|
|
|
|
|
|
|
|
EINA_COW_MAGIC_CHECK(cow);
|
|
|
|
|
2013-06-20 04:28:18 -07:00
|
|
|
if (*dst == src) return;
|
2013-01-27 16:22:21 -08:00
|
|
|
|
|
|
|
if (src != cow->default_value)
|
|
|
|
{
|
|
|
|
ref = EINA_COW_PTR_GET(src);
|
2013-01-23 17:42:20 -08:00
|
|
|
#ifndef NVALGRIND
|
2013-03-19 18:59:22 -07:00
|
|
|
VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
|
2013-01-23 17:42:20 -08:00
|
|
|
#endif
|
2013-04-01 21:41:47 -07:00
|
|
|
|
|
|
|
EINA_COW_PTR_MAGIC_CHECK(ref);
|
2013-01-27 16:22:21 -08:00
|
|
|
ref->refcount++;
|
2013-04-01 21:41:47 -07:00
|
|
|
|
2013-04-02 01:10:35 -07:00
|
|
|
if (cow->togc)
|
|
|
|
_eina_cow_togc_del(cow, ref);
|
2013-04-01 21:41:47 -07:00
|
|
|
|
2013-01-23 17:42:20 -08:00
|
|
|
#ifndef NVALGRIND
|
2013-03-19 18:59:22 -07:00
|
|
|
VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
|
2013-01-23 17:42:20 -08:00
|
|
|
#endif
|
2013-01-27 16:22:21 -08:00
|
|
|
}
|
2013-01-08 01:17:56 -08:00
|
|
|
|
2013-11-03 19:45:25 -08:00
|
|
|
eina_cow_free(cow, (const Eina_Cow_Data**) dst);
|
2013-04-01 21:41:47 -07:00
|
|
|
|
2013-01-08 01:17:56 -08:00
|
|
|
*((const void**)dst) = src;
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI Eina_Bool
|
|
|
|
eina_cow_gc(Eina_Cow *cow)
|
|
|
|
{
|
|
|
|
Eina_Cow_GC *gc;
|
2013-03-18 23:11:50 -07:00
|
|
|
Eina_Iterator *it;
|
|
|
|
Eina_Bool r;
|
2013-11-30 20:52:53 -08:00
|
|
|
#ifndef NVALGRIND
|
2013-11-25 14:33:51 -08:00
|
|
|
Eina_Cow_Ptr *ref;
|
2013-11-30 20:52:53 -08:00
|
|
|
#endif
|
2013-01-08 01:17:56 -08:00
|
|
|
|
|
|
|
EINA_COW_MAGIC_CHECK(cow);
|
|
|
|
|
2013-04-02 01:10:35 -07:00
|
|
|
if (!cow->togc || !eina_hash_population(cow->togc))
|
|
|
|
return EINA_FALSE;
|
2013-01-08 01:17:56 -08:00
|
|
|
|
2013-03-18 23:11:50 -07:00
|
|
|
it = eina_hash_iterator_data_new(cow->togc);
|
|
|
|
r = eina_iterator_next(it, (void**) &gc);
|
|
|
|
eina_iterator_free(it);
|
2013-11-25 14:33:51 -08:00
|
|
|
|
2013-03-18 23:11:50 -07:00
|
|
|
if (!r) return EINA_FALSE; /* Something did go wrong here */
|
2013-01-08 01:17:56 -08:00
|
|
|
|
2013-11-30 20:52:53 -08:00
|
|
|
#ifndef NVALGRIND
|
2013-11-25 14:33:51 -08:00
|
|
|
/* Do handle hash and all funky merge thing here */
|
|
|
|
ref = gc->ref;
|
2013-01-08 01:17:56 -08:00
|
|
|
|
2013-11-25 14:33:51 -08:00
|
|
|
VALGRIND_MAKE_MEM_DEFINED(ref, sizeof (*ref));
|
2013-01-23 17:42:20 -08:00
|
|
|
#endif
|
2013-11-25 14:33:51 -08:00
|
|
|
_eina_cow_gc(cow, gc);
|
2013-04-01 02:38:24 -07:00
|
|
|
#ifndef NVALGRIND
|
2013-11-25 14:33:51 -08:00
|
|
|
VALGRIND_MAKE_MEM_NOACCESS(ref, sizeof (*ref));
|
2013-04-01 02:38:24 -07:00
|
|
|
#endif
|
2013-01-08 01:17:56 -08:00
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|