From 692b2c9fc9b347fc0be80b674d4580648e2f6541 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Fri, 10 Jun 2016 13:42:19 -0700 Subject: [PATCH] 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. --- configure.ac | 2 + src/Makefile_Eina.am | 6 +- src/lib/eina/Eina.h | 1 + src/lib/eina/eina_config.h.in | 5 + src/lib/eina/eina_inline_safepointer.x | 188 +++++++++++++ src/lib/eina/eina_main.c | 4 +- src/lib/eina/eina_safepointer.c | 362 +++++++++++++++++++++++++ src/lib/eina/eina_safepointer.h | 115 ++++++++ 8 files changed, 680 insertions(+), 3 deletions(-) create mode 100644 src/lib/eina/eina_inline_safepointer.x create mode 100644 src/lib/eina/eina_safepointer.c create mode 100644 src/lib/eina/eina_safepointer.h diff --git a/configure.ac b/configure.ac index 676624fe38..f37f3a5429 100644 --- a/configure.ac +++ b/configure.ac @@ -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], [], [], [[ diff --git a/src/Makefile_Eina.am b/src/Makefile_Eina.am index 1b6bdff7bd..602c3dbf2a 100644 --- a/src/Makefile_Eina.am +++ b/src/Makefile_Eina.am @@ -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 diff --git a/src/lib/eina/Eina.h b/src/lib/eina/Eina.h index fe0a4ec108..e57660e233 100644 --- a/src/lib/eina/Eina.h +++ b/src/lib/eina/Eina.h @@ -270,6 +270,7 @@ extern "C" { #include #include #include +#include #undef EAPI #define EAPI diff --git a/src/lib/eina/eina_config.h.in b/src/lib/eina/eina_config.h.in index 9ec3b29372..ab970ef0fa 100644 --- a/src/lib/eina/eina_config.h.in +++ b/src/lib/eina/eina_config.h.in @@ -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 diff --git a/src/lib/eina/eina_inline_safepointer.x b/src/lib/eina/eina_inline_safepointer.x new file mode 100644 index 0000000000..7c4cd5505c --- /dev/null +++ b/src/lib/eina/eina_inline_safepointer.x @@ -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 . + */ + +#ifndef EINA_SAFEPOINTER_INLINE_H_ +#define EINA_SAFEPOINTER_INLINE_H_ + +#include +#include + +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 diff --git a/src/lib/eina/eina_main.c b/src/lib/eina/eina_main.c index 52c548e5e5..4cdb4068a3 100644 --- a/src/lib/eina/eina_main.c +++ b/src/lib/eina/eina_main.c @@ -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) */ diff --git a/src/lib/eina/eina_safepointer.c b/src/lib/eina/eina_safepointer.c new file mode 100644 index 0000000000..8336ea0780 --- /dev/null +++ b/src/lib/eina/eina_safepointer.c @@ -0,0 +1,362 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/lib/eina/eina_safepointer.h b/src/lib/eina/eina_safepointer.h new file mode 100644 index 0000000000..02ee2763e7 --- /dev/null +++ b/src/lib/eina/eina_safepointer.h @@ -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 . + */ + +#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