forked from enlightenment/efl
eina: add generic infrastructure for a Eina_Safepointer
This is heavily inspired from Eo_Id infrastructure. Main change are that the lower bit are always guaranteed to be zero and ignored by all function. Also it may be a little bit less efficient in some case, but we will tune it once we have real life usage of it. Eo won't be migrated for 1.18 to it as Eo_Id is deeply integrated and it is quite risky to touch it so close from a freeze. This can wait.
This commit is contained in:
parent
5964008946
commit
692b2c9fc9
|
@ -463,6 +463,8 @@ AC_CHECK_SIZEOF(int, 4)
|
|||
AC_CHECK_SIZEOF(long, 4)
|
||||
|
||||
AC_CHECK_SIZEOF([uintptr_t])
|
||||
EINA_SIZEOF_UINTPTR_T=$ac_cv_sizeof_uintptr_t
|
||||
AC_SUBST([EINA_SIZEOF_UINTPTR_T])
|
||||
|
||||
AC_CHECK_TYPES([siginfo_t], [], [],
|
||||
[[
|
||||
|
|
|
@ -97,7 +97,8 @@ lib/eina/eina_quaternion.h \
|
|||
lib/eina/eina_vector.h \
|
||||
lib/eina/eina_inline_vector.x \
|
||||
lib/eina/eina_promise.h \
|
||||
lib/eina/eina_bezier.h
|
||||
lib/eina/eina_bezier.h \
|
||||
lib/eina/eina_safepointer.h
|
||||
|
||||
lib_eina_libeina_la_SOURCES = \
|
||||
lib/eina/eina_abi.c \
|
||||
|
@ -168,7 +169,8 @@ lib/eina/eina_share_common.h \
|
|||
lib/eina/eina_strbuf_common.h \
|
||||
lib/eina/eina_quaternion.c \
|
||||
lib/eina/eina_promise.c \
|
||||
lib/eina/eina_bezier.c
|
||||
lib/eina/eina_bezier.c \
|
||||
lib/eina/eina_safepointer.c
|
||||
|
||||
if HAVE_WIN32
|
||||
lib_eina_libeina_la_SOURCES += lib/eina/eina_file_win32.c
|
||||
|
|
|
@ -270,6 +270,7 @@ extern "C" {
|
|||
#include <eina_quaternion.h>
|
||||
#include <eina_promise.h>
|
||||
#include <eina_bezier.h>
|
||||
#include <eina_safepointer.h>
|
||||
|
||||
#undef EAPI
|
||||
#define EAPI
|
||||
|
|
|
@ -67,6 +67,11 @@
|
|||
#endif
|
||||
#define EINA_SIZEOF_WCHAR_T @EINA_SIZEOF_WCHAR_T@
|
||||
|
||||
#ifdef EINA_SIZEOF_UINTPTR_T
|
||||
# undef EINA_SIZEOF_UINTPTR_T
|
||||
#endif
|
||||
#define EINA_SIZEOF_UINTPTR_T @EINA_SIZEOF_UINTPTR_T@
|
||||
|
||||
#ifdef EINA_CONFIGURE_HAVE_DIRENT_H
|
||||
# undef EINA_CONFIGURE_HAVE_DIRENT_H
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
/* EINA - EFL data type library
|
||||
* Copyright (C) 2015-2016 Carsten Haitzler, 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_SAFEPOINTER_INLINE_H_
|
||||
#define EINA_SAFEPOINTER_INLINE_H_
|
||||
|
||||
#include <eina_trash.h>
|
||||
#include <eina_log.h>
|
||||
|
||||
typedef struct _Eina_Memory_Table Eina_Memory_Table;
|
||||
typedef struct _Eina_Memory_Entry Eina_Memory_Entry;
|
||||
typedef uintptr_t Eina_Sp_Id;
|
||||
|
||||
#if EINA_SIZEOF_UINTPTR_T == 4
|
||||
/* 32 bits */
|
||||
# define EINA_BITS_MID_TABLE_ID 5
|
||||
# define EINA_BITS_TABLE_ID 5
|
||||
# define EINA_BITS_ENTRY_ID 12
|
||||
# define EINA_BITS_GENERATION_COUNTER 8
|
||||
# define EINA_BITS_FREE_COUNTER 2
|
||||
# define EINA_DROPPED_TABLES 0
|
||||
# define EINA_DROPPED_ENTRIES 3
|
||||
typedef int16_t Eina_Table_Index;
|
||||
typedef uint16_t Eina_Generation_Counter;
|
||||
#else
|
||||
/* 64 bits */
|
||||
# define EINA_BITS_MID_TABLE_ID 11
|
||||
# define EINA_BITS_TABLE_ID 11
|
||||
# define EINA_BITS_ENTRY_ID 12
|
||||
# define EINA_BITS_GENERATION_COUNTER 28
|
||||
# define EINA_BITS_FREE_COUNTER 2
|
||||
# define EINA_DROPPED_TABLES 2
|
||||
# define EINA_DROPPED_ENTRIES 2
|
||||
typedef int16_t Eina_Table_Index;
|
||||
typedef uint32_t Eina_Generation_Counter;
|
||||
#endif
|
||||
|
||||
/* Shifts macros to manipulate the SP id */
|
||||
#define EINA_SHIFT_GENERATION (EINA_BITS_FREE_COUNTER)
|
||||
#define EINA_SHIFT_ENTRY_ID (EINA_SHIFT_GENERATION + \
|
||||
EINA_BITS_GENERATION_COUNTER)
|
||||
#define EINA_SHIFT_TABLE_ID (EINA_SHIFT_ENTRY_ID + \
|
||||
EINA_BITS_ENTRY_ID)
|
||||
#define EINA_SHIFT_MID_TABLE_ID (EINA_SHIFT_TABLE_ID + \
|
||||
EINA_BITS_TABLE_ID)
|
||||
|
||||
/* Maximum ranges - a few tables and entries are dropped to minimize the amount
|
||||
* of wasted bytes, see _eina_safepointer_calloc */
|
||||
#define EINA_MAX_MID_TABLE_ID (1 << EINA_BITS_MID_TABLE_ID)
|
||||
#define EINA_MAX_TABLE_ID ((1 << EINA_BITS_TABLE_ID) - EINA_DROPPED_TABLES )
|
||||
#define EINA_MAX_ENTRY_ID ((1 << EINA_BITS_ENTRY_ID) - EINA_DROPPED_ENTRIES)
|
||||
#define EINA_MAX_GENERATIONS (1 << EINA_BITS_GENERATION_COUNTER)
|
||||
|
||||
/* Masks */
|
||||
#define EINA_MASK_MID_TABLE_ID (EINA_MAX_MID_TABLE_ID - 1)
|
||||
#define EINA_MASK_TABLE_ID ((1 << EINA_BITS_TABLE_ID) - 1)
|
||||
#define EINA_MASK_ENTRY_ID ((1 << EINA_BITS_ENTRY_ID) - 1)
|
||||
#define EINA_MASK_GENERATIONS (EINA_MAX_GENERATIONS - 1)
|
||||
|
||||
|
||||
/* Macro to extract from an Eo id the indexes of the tables */
|
||||
#define EINA_SP_DECOMPOSE_ID(ID, MID_TABLE, TABLE, ENTRY, GENERATION) \
|
||||
MID_TABLE = (ID >> EINA_SHIFT_MID_TABLE_ID) & EINA_MASK_MID_TABLE_ID; \
|
||||
TABLE = (ID >> EINA_SHIFT_TABLE_ID) & EINA_MASK_TABLE_ID; \
|
||||
ENTRY = (ID >> EINA_SHIFT_ENTRY_ID) & EINA_MASK_ENTRY_ID; \
|
||||
GENERATION = (ID >> EINA_SHIFT_GENERATION) & EINA_MASK_GENERATIONS;
|
||||
|
||||
struct _Eina_Memory_Entry
|
||||
{
|
||||
/* Pointer to the object or
|
||||
Eina_Trash entry if not active */
|
||||
void *ptr;
|
||||
|
||||
unsigned int active : 1;
|
||||
/* Valid generation for this entry */
|
||||
unsigned int generation : EINA_BITS_GENERATION_COUNTER;
|
||||
};
|
||||
|
||||
struct _Eina_Memory_Table
|
||||
{
|
||||
/* Pointer to the first recycled entry */
|
||||
Eina_Trash *trash;
|
||||
|
||||
/* Packed mid table and table indexes */
|
||||
Eina_Sp_Id partial_id;
|
||||
|
||||
/* Indicates where start the "never used" entries */
|
||||
Eina_Table_Index start;
|
||||
|
||||
/* Entries of the table holding real pointers and generations */
|
||||
Eina_Memory_Entry entries[EINA_MAX_ENTRY_ID];
|
||||
};
|
||||
|
||||
EAPI extern Eina_Memory_Table **_eina_sp_ids_tables[EINA_MAX_MID_TABLE_ID];
|
||||
EAPI extern int _eina_sp_log_dom;
|
||||
|
||||
#ifdef _EINA_SP_ERR
|
||||
#undef _EINA_SP_ERR
|
||||
#endif
|
||||
#define _EINA_SP_ERR(...) EINA_LOG_DOM_ERR(_eina_sp_log_dom, __VA_ARGS__)
|
||||
|
||||
static inline Eina_Memory_Entry *
|
||||
_eina_safepointer_entry_get(const Eina_Safepointer *safe,
|
||||
Eina_Memory_Table **rtable)
|
||||
{
|
||||
Eina_Table_Index mid_table_id, table_id, entry_id;
|
||||
Eina_Generation_Counter generation;
|
||||
Eina_Sp_Id id = (Eina_Sp_Id) safe;
|
||||
|
||||
EINA_SP_DECOMPOSE_ID(id, mid_table_id, table_id, entry_id, generation);
|
||||
|
||||
if (_eina_sp_ids_tables[mid_table_id] &&
|
||||
_eina_sp_ids_tables[mid_table_id][table_id] &&
|
||||
entry_id < EINA_MAX_ENTRY_ID)
|
||||
{
|
||||
Eina_Memory_Table *table;
|
||||
Eina_Memory_Entry *entry;
|
||||
|
||||
table = _eina_sp_ids_tables[mid_table_id][table_id];
|
||||
entry = &(table->entries[entry_id]);
|
||||
|
||||
if (entry->active &&
|
||||
entry->generation == generation)
|
||||
{
|
||||
if (rtable) *rtable = table;
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
_EINA_SP_ERR("Pointer %p is not a pointer to a valid object.", (void *) safe);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void *
|
||||
eina_safepointer_get(const Eina_Safepointer *safe)
|
||||
{
|
||||
Eina_Memory_Entry *entry;
|
||||
|
||||
if (!safe) return NULL;
|
||||
|
||||
entry = _eina_safepointer_entry_get(safe, NULL);
|
||||
if (!entry) return NULL;
|
||||
|
||||
return entry->ptr;
|
||||
}
|
||||
|
||||
#undef _EINA_SP_ERR
|
||||
|
||||
#ifndef _EINA_INTERNAL_SAFEPOINTER
|
||||
|
||||
#undef EINA_BITS_MID_TABLE_ID
|
||||
#undef EINA_BITS_TABLE_ID
|
||||
#undef EINA_BITS_ENTRY_ID
|
||||
#undef EINA_BITS_GENERATION_COUNTER
|
||||
#undef EINA_DROPPED_TABLES
|
||||
#undef EINA_DROPPED_ENTRIES
|
||||
#undef EINA_SHIFT_MID_TABLE_ID
|
||||
#undef EINA_SHIFT_TABLE_ID
|
||||
#undef EINA_SHIFT_ENTRY_ID
|
||||
#undef EINA_MAX_MID_TABLE_ID
|
||||
#undef EINA_MAX_TABLE_ID
|
||||
#undef EINA_MAX_ENTRY_ID
|
||||
#undef EINA_MAX_GENERATIONS
|
||||
#undef EINA_MASK_MID_TABLE_ID
|
||||
#undef EINA_MASK_TABLE_ID
|
||||
#undef EINA_MASK_ENTRY_ID
|
||||
#undef EINA_MASK_GENERATIONS
|
||||
#undef EINA_SP_DECOMPOSE_ID
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -155,6 +155,7 @@ EAPI Eina_Inlist *_eina_tracking = NULL;
|
|||
S(thread_queue);
|
||||
S(rbtree);
|
||||
S(promise);
|
||||
S(safepointer);
|
||||
/* no model for now
|
||||
S(model);
|
||||
*/
|
||||
|
@ -202,7 +203,8 @@ static const struct eina_desc_setup _eina_desc_setup[] = {
|
|||
S(cpu),
|
||||
S(thread_queue),
|
||||
S(rbtree),
|
||||
S(promise)
|
||||
S(promise),
|
||||
S(safepointer)
|
||||
/* no model for now
|
||||
S(model)
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,362 @@
|
|||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "eina_config.h"
|
||||
#include "eina_private.h"
|
||||
|
||||
#define _EINA_INTERNAL_SAFEPOINTER
|
||||
#include "eina_safepointer.h"
|
||||
#include "eina_mempool.h"
|
||||
#include "eina_trash.h"
|
||||
#include "eina_log.h"
|
||||
#include "eina_lock.h"
|
||||
|
||||
typedef struct _Eina_Memory_Header Eina_Memory_Header;
|
||||
|
||||
#ifdef ERR
|
||||
#undef ERR
|
||||
#endif
|
||||
#define ERR(...) EINA_LOG_DOM_ERR(_eina_sp_log_dom, __VA_ARGS__)
|
||||
|
||||
#ifdef DBG
|
||||
#undef DBG
|
||||
#endif
|
||||
#define DBG(...) EINA_LOG_DOM_DBG(_eina_sp_log_dom, __VA_ARGS__)
|
||||
|
||||
/* Macro used to compose an Eo id */
|
||||
#define SP_COMPOSE_PARTIAL_ID(MID_TABLE, TABLE) \
|
||||
( \
|
||||
((Eina_Sp_Id)(MID_TABLE & EINA_MASK_MID_TABLE_ID) << EINA_SHIFT_MID_TABLE_ID) | \
|
||||
((Eina_Sp_Id)(TABLE & EINA_MASK_TABLE_ID) << EINA_SHIFT_TABLE_ID) \
|
||||
)
|
||||
|
||||
#define SP_COMPOSE_FINAL_ID(PARTIAL_ID, ENTRY, GENERATION) \
|
||||
(PARTIAL_ID | \
|
||||
((ENTRY & EINA_MASK_ENTRY_ID) << EINA_SHIFT_ENTRY_ID) | \
|
||||
((GENERATION & EINA_MASK_GENERATIONS) << EINA_SHIFT_GENERATION))
|
||||
|
||||
struct _Eina_Memory_Header
|
||||
{
|
||||
EINA_MAGIC;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
EAPI Eina_Memory_Table **_eina_sp_ids_tables[EINA_MAX_MID_TABLE_ID] = { NULL };
|
||||
EAPI int _eina_sp_log_dom = -1;
|
||||
|
||||
/* Spare empty table */
|
||||
static Eina_Memory_Table *empty_table = NULL;
|
||||
|
||||
// We are using a Spinlock even with the amount of syscall we do as it shouldn't
|
||||
// take that long anyway.
|
||||
static Eina_Spinlock sl;
|
||||
|
||||
#define MEM_PAGE_SIZE 4096
|
||||
#define SAFEPOINTER_MAGIC 0x7DEADC03
|
||||
|
||||
static void *
|
||||
_eina_safepointer_calloc(int number, size_t size)
|
||||
{
|
||||
#ifdef HAVE_MMAP
|
||||
Eina_Memory_Header *header;
|
||||
size_t newsize;
|
||||
|
||||
size = size * number + sizeof (Eina_Memory_Header);
|
||||
newsize = ((size / MEM_PAGE_SIZE) +
|
||||
(size % MEM_PAGE_SIZE ? 1 : 0))
|
||||
* MEM_PAGE_SIZE;
|
||||
|
||||
header = mmap(NULL, newsize, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (header == MAP_FAILED)
|
||||
{
|
||||
ERR("mmap of Eina_Safepointer table region failed.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
header->size = newsize;
|
||||
EINA_MAGIC_SET(header, SAFEPOINTER_MAGIC);
|
||||
|
||||
return (void*)(header + 1);
|
||||
#else
|
||||
return calloc(number, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
_eina_safepointer_free(void *pointer)
|
||||
{
|
||||
#ifdef HAVE_MMAP
|
||||
Eina_Memory_Header *header;
|
||||
|
||||
if (!pointer) return ;
|
||||
|
||||
header = (Eina_Memory_Header*)(pointer) - 1;
|
||||
if (!EINA_MAGIC_CHECK(header, SAFEPOINTER_MAGIC))
|
||||
EINA_MAGIC_FAIL(header, SAFEPOINTER_MAGIC);
|
||||
|
||||
EINA_MAGIC_SET(header, 0);
|
||||
munmap(header, header->size);
|
||||
#else
|
||||
free((void*) ((uintptr_t) pointer & ~0x3));
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef EINA_DEBUG_MALLOC
|
||||
static void
|
||||
_eina_safepointer_protect(void *pointer, Eina_Bool may_not_write)
|
||||
{
|
||||
#ifdef HAVE_MMAP
|
||||
Eina_Memory_Header *header;
|
||||
|
||||
if (!pointer) return ;
|
||||
|
||||
header = (Eina_Memory_Header*)(pointer) - 1;
|
||||
if (!EINA_MAGIC_CHECK(header, SAFEPOINTER_MAGIC))
|
||||
EINA_MAGIC_FAIL(header, SAFEPOINTER_MAGIC);
|
||||
|
||||
mprotect(header, header->size, PROT_READ | ( may_not_write ? 0 : PROT_WRITE));
|
||||
#else
|
||||
(void) pointer;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define PROTECT(Ptr) _eina_safepointer_protect(Ptr, EINA_TRUE)
|
||||
#define UNPROTECT(Ptr) _eina_safepointer_protect(Ptr, EINA_FALSE)
|
||||
|
||||
#else
|
||||
|
||||
#define PROTECT(Ptr)
|
||||
#define UNPROTECT(Ptr)
|
||||
|
||||
#endif
|
||||
|
||||
static Eina_Memory_Table *
|
||||
_eina_safepointer_table_new(Eina_Table_Index mid_table_id,
|
||||
Eina_Table_Index table_id)
|
||||
{
|
||||
Eina_Memory_Table *table;
|
||||
|
||||
if (empty_table)
|
||||
{
|
||||
/* Recycle the available empty table */
|
||||
table = empty_table;
|
||||
empty_table = NULL;
|
||||
UNPROTECT(table);
|
||||
}
|
||||
else
|
||||
{
|
||||
table = _eina_safepointer_calloc(1, sizeof (Eina_Memory_Table));
|
||||
if (!table)
|
||||
{
|
||||
ERR("Failed to allocate leaf table at [%i][%i]", mid_table_id, table_id);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
table->partial_id = SP_COMPOSE_PARTIAL_ID(mid_table_id,
|
||||
table_id);
|
||||
PROTECT(table);
|
||||
UNPROTECT(_eina_sp_ids_tables[mid_table_id]);
|
||||
_eina_sp_ids_tables[mid_table_id][table_id] = table;
|
||||
PROTECT(_eina_sp_ids_tables[mid_table_id]);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
static Eina_Memory_Table *
|
||||
_eina_safepointer_table_find(void)
|
||||
{
|
||||
Eina_Table_Index mid_table_id;
|
||||
|
||||
for (mid_table_id = 0; mid_table_id < EINA_MAX_MID_TABLE_ID; mid_table_id++)
|
||||
{
|
||||
Eina_Table_Index table_id;
|
||||
|
||||
if (!_eina_sp_ids_tables[mid_table_id])
|
||||
{
|
||||
_eina_sp_ids_tables[mid_table_id] = _eina_safepointer_calloc(EINA_MAX_TABLE_ID, sizeof (Eina_Memory_Table*));
|
||||
}
|
||||
if (!_eina_sp_ids_tables[mid_table_id])
|
||||
{
|
||||
ERR("Failed to allocate mid table at [%i]", mid_table_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (table_id = 0; table_id < EINA_MAX_TABLE_ID; table_id++)
|
||||
{
|
||||
Eina_Memory_Table *table;
|
||||
|
||||
table = _eina_sp_ids_tables[mid_table_id][table_id];
|
||||
|
||||
if (!table)
|
||||
table = _eina_safepointer_table_new(mid_table_id, table_id);
|
||||
|
||||
if (!table) return NULL;
|
||||
|
||||
if (table->trash ||
|
||||
table->start < EINA_MAX_ENTRY_ID)
|
||||
return table;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Eina_Memory_Entry *
|
||||
_eina_safepointer_entry_find(Eina_Memory_Table *table)
|
||||
{
|
||||
Eina_Memory_Entry *entry = NULL;
|
||||
|
||||
if (table->trash)
|
||||
{
|
||||
entry = eina_trash_pop(&table->trash);
|
||||
}
|
||||
else if (table->start < EINA_MAX_ENTRY_ID)
|
||||
{
|
||||
entry = &(table->entries[table->start]);
|
||||
table->start++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Impossible to find an entry in %" PRIxPTR ".", table->partial_id);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
EAPI const Eina_Safepointer *
|
||||
eina_safepointer_register(const void *target)
|
||||
{
|
||||
Eina_Memory_Table *table;
|
||||
Eina_Memory_Entry *entry = NULL;
|
||||
Eina_Sp_Id id = 0;
|
||||
|
||||
// We silently handle NULL
|
||||
if (!target) return NULL;
|
||||
|
||||
eina_spinlock_take(&sl);
|
||||
|
||||
table = _eina_safepointer_table_find();
|
||||
if (!table) goto no_table;
|
||||
|
||||
UNPROTECT(table);
|
||||
entry = _eina_safepointer_entry_find(table);
|
||||
if (!entry) goto on_error;
|
||||
|
||||
entry->ptr = (void*) target;
|
||||
entry->active = 1;
|
||||
entry->generation++;
|
||||
if (entry->generation == EINA_MAX_GENERATIONS)
|
||||
entry->generation = 1;
|
||||
|
||||
id = SP_COMPOSE_FINAL_ID(table->partial_id,
|
||||
(entry - table->entries),
|
||||
entry->generation);
|
||||
|
||||
on_error:
|
||||
PROTECT(table);
|
||||
no_table:
|
||||
eina_spinlock_release(&sl);
|
||||
|
||||
return (void*) id;
|
||||
}
|
||||
|
||||
EAPI void
|
||||
eina_safepointer_unregister(const Eina_Safepointer *safe)
|
||||
{
|
||||
Eina_Memory_Table *table;
|
||||
Eina_Memory_Entry *entry;
|
||||
Eina_Table_Index entry_id;
|
||||
|
||||
// We silently handle NULL
|
||||
if (!safe) return ;
|
||||
|
||||
entry = _eina_safepointer_entry_get(safe, &table);
|
||||
if (!entry) return ;
|
||||
|
||||
eina_spinlock_take(&sl);
|
||||
|
||||
// In case of a race condition during a double free attempt
|
||||
// The entry could have been unactivated since we did found it
|
||||
// So check again.
|
||||
if (!entry->active) goto on_error;
|
||||
|
||||
UNPROTECT(table);
|
||||
entry->active = 0;
|
||||
eina_trash_push(&table->trash, entry);
|
||||
PROTECT(table);
|
||||
|
||||
entry_id = entry - table->entries;
|
||||
if (entry_id == EINA_MAX_ENTRY_ID - 1)
|
||||
{
|
||||
Eina_Table_Index i;
|
||||
|
||||
for (i = entry_id; i >= 0; i--)
|
||||
{
|
||||
if (table->entries[i].active)
|
||||
break ;
|
||||
}
|
||||
|
||||
// No more active entry
|
||||
// Could be speed up by tracking the
|
||||
// number of allocated entries, but
|
||||
// with all the syscall around, not sure
|
||||
// it is worth it.
|
||||
if (i == -1)
|
||||
{
|
||||
Eina_Table_Index mid_table_id, table_id;
|
||||
|
||||
mid_table_id = (table->partial_id >> EINA_SHIFT_MID_TABLE_ID) & EINA_MASK_MID_TABLE_ID;
|
||||
table_id = (table->partial_id >> EINA_SHIFT_TABLE_ID) & EINA_MASK_TABLE_ID;
|
||||
UNPROTECT(_eina_sp_ids_tables[mid_table_id]);
|
||||
_eina_sp_ids_tables[mid_table_id][table_id] = NULL;
|
||||
PROTECT(_eina_sp_ids_tables[mid_table_id]);
|
||||
if (!empty_table)
|
||||
empty_table = table;
|
||||
else
|
||||
_eina_safepointer_free(table);
|
||||
}
|
||||
}
|
||||
|
||||
on_error:
|
||||
eina_spinlock_release(&sl);
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
eina_safepointer_init(void)
|
||||
{
|
||||
eina_magic_string_set(SAFEPOINTER_MAGIC, "Safepointer");
|
||||
_eina_sp_log_dom = eina_log_domain_register("eina_safepointer",
|
||||
EINA_LOG_COLOR_DEFAULT);
|
||||
if (_eina_sp_log_dom < 0)
|
||||
{
|
||||
EINA_LOG_ERR("Could not register log domain: eina_safepointer.");
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
eina_spinlock_new(&sl);
|
||||
|
||||
DBG("entry[Size, Align] = { %zu, %u }",
|
||||
sizeof (Eina_Memory_Entry), eina_mempool_alignof(sizeof (Eina_Memory_Entry)));
|
||||
DBG("table[Size, Align] = { %zu, %u }\n",
|
||||
sizeof (Eina_Memory_Table), eina_mempool_alignof(sizeof (Eina_Memory_Table)));
|
||||
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
eina_safepointer_shutdown(void)
|
||||
{
|
||||
eina_spinlock_free(&sl);
|
||||
|
||||
return EINA_TRUE;
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/* EINA - EFL data type library
|
||||
* Copyright (C) 2015-2016 Carsten Haitzler, 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_SAFEPOINTER_H__
|
||||
#define EINA_SAFEPOINTER_H__
|
||||
|
||||
/**
|
||||
* @addtogroup Eina_Safepointer_Group Safe Pointer
|
||||
*
|
||||
* @brief These functions provide a wrapper that protect access to pointers
|
||||
*
|
||||
* Eina_Safepointer is an pointer to index converter that allow an increased
|
||||
* level of safety by forbidding direct access to the pointer. The protection
|
||||
* work by using a set of indirection table that are mmapped and mprotected
|
||||
* against write access. This the pointer they store and that map to a specific
|
||||
* index is always correct. Also once a pointer is unregistered the index
|
||||
* won't be served back for 2^8 on 32 bits system and 2^28 on 64 bits system
|
||||
* for that specific slot. Finally we do guarantee that the lower 2 bits of the
|
||||
* returned index are actually never used and completly ignored by our API.
|
||||
* So you can safely store whatever information you want in it, we will ignore
|
||||
* it and threat as if it wasn't there.
|
||||
*
|
||||
* @note The use of Eina_Safepointer is thread safe.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup Eina_Data_Types_Group Data Types
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup Eina_Containers_Group Containers
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup Eina_Safepointer_Group Safe Pointer
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef Eina_Safepointer
|
||||
* Type of the protected index.
|
||||
*/
|
||||
typedef struct _Eina_Safepointer Eina_Safepointer;
|
||||
|
||||
/**
|
||||
* @brief Register a pointer and get an Eina_Safepointer that map to it.
|
||||
*
|
||||
* @param target The pointer to register.
|
||||
* @return A valid pointer that is an index to the mapped pointer.
|
||||
*
|
||||
* @note It will return @c NULL on error or if @p target is @c NULL.
|
||||
*
|
||||
* @note The lower 2 bits of the returned pointer will always be 0.
|
||||
*
|
||||
* @note The returned pointer can be used like a pointer, but can not
|
||||
* be touched except with Eina_Safepointer functions.
|
||||
*/
|
||||
EAPI const Eina_Safepointer *eina_safepointer_register(const void *target);
|
||||
|
||||
/**
|
||||
* @brief Unregister an Eina_Safepointer and the pointer that map to it.
|
||||
*
|
||||
* @param safe The index to unregister from the mapping.
|
||||
*
|
||||
* @note This function will ignore the lower 2 bits of the given pointer.
|
||||
*/
|
||||
EAPI void eina_safepointer_unregister(const Eina_Safepointer *safe);
|
||||
|
||||
/**
|
||||
* @brief Get the associated pointer from an Eina_Safepointer mapping.
|
||||
*
|
||||
* @param safe The Eina_Safepointer index to lookup at.
|
||||
* @return The pointer registered with that index or @c NULL in any other case.
|
||||
*
|
||||
* @note It is always safe to ask for a pointer for any value of the mapping.
|
||||
* If the pointer is invalid or @c NULL, we will return @c NULL and not crash.
|
||||
*/
|
||||
static inline void *eina_safepointer_get(const Eina_Safepointer *safe);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
# include "eina_inline_safepointer.x"
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue