1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4
5 #ifdef HAVE_VALGRIND
6 # include <valgrind.h>
7 # include <memcheck.h>
8 #endif
9
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <fcntl.h>
14 #include <inttypes.h>
15
16 #ifdef _WIN32
17 # include <evil_private.h> /* mmap */
18 #else
19 # include <sys/mman.h>
20 #endif
21
22 #include "eina_config.h"
23 #include "eina_private.h"
24
25 #define _EINA_INTERNAL_SAFEPOINTER
26 #include "eina_safepointer.h"
27 #include "eina_mempool.h"
28 #include "eina_trash.h"
29 #include "eina_log.h"
30 #include "eina_lock.h"
31
32 typedef struct _Eina_Memory_Header Eina_Memory_Header;
33
34 #ifdef ERR
35 #undef ERR
36 #endif
37 #define ERR(...) EINA_LOG_DOM_ERR(_eina_sp_log_dom, __VA_ARGS__)
38
39 #ifdef DBG
40 #undef DBG
41 #endif
42 #define DBG(...) EINA_LOG_DOM_DBG(_eina_sp_log_dom, __VA_ARGS__)
43
44 /* Macro used to compose an Eo id */
45 #define SP_COMPOSE_PARTIAL_ID(MID_TABLE, TABLE) \
46 ( \
47 ((Eina_Sp_Id)(MID_TABLE & EINA_MASK_MID_TABLE_ID) << EINA_SHIFT_MID_TABLE_ID) | \
48 ((Eina_Sp_Id)(TABLE & EINA_MASK_TABLE_ID) << EINA_SHIFT_TABLE_ID) \
49 )
50
51 #define SP_COMPOSE_FINAL_ID(PARTIAL_ID, ENTRY, GENERATION) \
52 (PARTIAL_ID | \
53 ((ENTRY & EINA_MASK_ENTRY_ID) << EINA_SHIFT_ENTRY_ID) | \
54 ((GENERATION & EINA_MASK_GENERATIONS) << EINA_SHIFT_GENERATION))
55
56 struct _Eina_Memory_Header
57 {
58 EINA_MAGIC;
59 size_t size;
60 };
61
62 EAPI Eina_Memory_Table **_eina_sp_ids_tables[EINA_MAX_MID_TABLE_ID] = { NULL };
63 EAPI int _eina_sp_log_dom = -1;
64
65 /* Spare empty table */
66 static Eina_Memory_Table *empty_table = NULL;
67
68 // We are using a Spinlock even with the amount of syscall we do as it shouldn't
69 // take that long anyway.
70 static Eina_Spinlock sl;
71
72 #define MEM_PAGE_SIZE 4096
73 #define SAFEPOINTER_MAGIC 0x7DEADC03
74
75 static void *
_eina_safepointer_calloc(int number,size_t size)76 _eina_safepointer_calloc(int number, size_t size)
77 {
78 #ifdef HAVE_MMAP
79 # ifdef HAVE_VALGRIND
80 if (RUNNING_ON_VALGRIND) return calloc(number, size);
81 else
82 # endif
83 {
84 Eina_Memory_Header *header;
85 size_t newsize;
86
87 size = size * number + sizeof (Eina_Memory_Header);
88 newsize = ((size / MEM_PAGE_SIZE) +
89 (size % MEM_PAGE_SIZE ? 1 : 0))
90 * MEM_PAGE_SIZE;
91
92 header = mmap(NULL, newsize, PROT_READ | PROT_WRITE,
93 MAP_PRIVATE | MAP_ANON, -1, 0);
94 if (header == MAP_FAILED)
95 {
96 ERR("mmap of Eina_Safepointer table region failed.");
97 return NULL;
98 }
99
100 header->size = newsize;
101 EINA_MAGIC_SET(header, SAFEPOINTER_MAGIC);
102
103 return (void *)(header + 1);
104 }
105 #else
106 return calloc(number, size);
107 #endif
108 }
109
110 static void
_eina_safepointer_free(void * pointer)111 _eina_safepointer_free(void *pointer)
112 {
113 #ifdef HAVE_MMAP
114 # ifdef HAVE_VALGRIND
115 if (RUNNING_ON_VALGRIND) free((void *)((uintptr_t) pointer & ~0x3));
116 else
117 # endif
118 {
119 Eina_Memory_Header *header;
120
121 if (!pointer) return;
122
123 header = (Eina_Memory_Header*)(pointer) - 1;
124 if (!EINA_MAGIC_CHECK(header, SAFEPOINTER_MAGIC))
125 EINA_MAGIC_FAIL(header, SAFEPOINTER_MAGIC);
126
127 EINA_MAGIC_SET(header, 0);
128 munmap(header, header->size);
129 }
130 #else
131 free((void *)((uintptr_t) pointer & ~0x3));
132 #endif
133 }
134
135 #ifdef EINA_DEBUG_MALLOC
136 static void
_eina_safepointer_protect(void * pointer,Eina_Bool may_not_write)137 _eina_safepointer_protect(void *pointer, Eina_Bool may_not_write)
138 {
139 #ifdef HAVE_MMAP
140 # ifdef HAVE_VALGRIND
141 if (RUNNING_ON_VALGRIND) { (void) pointer; }
142 else
143 # endif
144 {
145 Eina_Memory_Header *header;
146
147 if (!pointer) return;
148
149 header = (Eina_Memory_Header*)(pointer) - 1;
150 if (!EINA_MAGIC_CHECK(header, SAFEPOINTER_MAGIC))
151 EINA_MAGIC_FAIL(header, SAFEPOINTER_MAGIC);
152
153 mprotect(header, header->size, PROT_READ | ( may_not_write ? 0 : PROT_WRITE));
154 }
155 #else
156 (void) pointer;
157 #endif
158 }
159
160 #define PROTECT(Ptr) _eina_safepointer_protect(Ptr, EINA_TRUE)
161 #define UNPROTECT(Ptr) _eina_safepointer_protect(Ptr, EINA_FALSE)
162
163 #else
164
165 #define PROTECT(Ptr)
166 #define UNPROTECT(Ptr)
167
168 #endif
169
170 static Eina_Memory_Table *
_eina_safepointer_table_new(Eina_Table_Index mid_table_id,Eina_Table_Index table_id)171 _eina_safepointer_table_new(Eina_Table_Index mid_table_id,
172 Eina_Table_Index table_id)
173 {
174 Eina_Memory_Table *table;
175
176 if (empty_table)
177 {
178 /* Recycle the available empty table */
179 table = empty_table;
180 empty_table = NULL;
181 UNPROTECT(table);
182 }
183 else
184 {
185 table = _eina_safepointer_calloc(1, sizeof (Eina_Memory_Table));
186 if (!table)
187 {
188 ERR("Failed to allocate leaf table at [%i][%i]", mid_table_id, table_id);
189 return NULL;
190 }
191 }
192
193 table->partial_id = SP_COMPOSE_PARTIAL_ID(mid_table_id,
194 table_id);
195 PROTECT(table);
196 UNPROTECT(_eina_sp_ids_tables[mid_table_id]);
197 _eina_sp_ids_tables[mid_table_id][table_id] = table;
198 PROTECT(_eina_sp_ids_tables[mid_table_id]);
199
200 return table;
201 }
202
203 static Eina_Memory_Table *
_eina_safepointer_table_find(void)204 _eina_safepointer_table_find(void)
205 {
206 Eina_Table_Index mid_table_id;
207
208 for (mid_table_id = 0; mid_table_id < EINA_MAX_MID_TABLE_ID; mid_table_id++)
209 {
210 Eina_Table_Index table_id;
211
212 if (!_eina_sp_ids_tables[mid_table_id])
213 {
214 _eina_sp_ids_tables[mid_table_id] = _eina_safepointer_calloc(EINA_MAX_TABLE_ID, sizeof (Eina_Memory_Table*));
215 }
216 if (!_eina_sp_ids_tables[mid_table_id])
217 {
218 ERR("Failed to allocate mid table at [%i]", mid_table_id);
219 return NULL;
220 }
221
222 for (table_id = 0; table_id < EINA_MAX_TABLE_ID; table_id++)
223 {
224 Eina_Memory_Table *table;
225
226 table = _eina_sp_ids_tables[mid_table_id][table_id];
227
228 if (!table)
229 table = _eina_safepointer_table_new(mid_table_id, table_id);
230
231 if (!table) return NULL;
232
233 if (table->trash ||
234 table->start < EINA_MAX_ENTRY_ID)
235 return table;
236 }
237 }
238
239 return NULL;
240 }
241
242 static Eina_Memory_Entry *
_eina_safepointer_entry_find(Eina_Memory_Table * table)243 _eina_safepointer_entry_find(Eina_Memory_Table *table)
244 {
245 Eina_Memory_Entry *entry = NULL;
246
247 if (table->trash)
248 {
249 entry = eina_trash_pop(&table->trash);
250 }
251 else if (table->start < EINA_MAX_ENTRY_ID)
252 {
253 entry = &(table->entries[table->start]);
254 table->start++;
255 }
256 else
257 {
258 ERR("Impossible to find an entry in %" PRIxPTR ".", table->partial_id);
259 }
260
261 return entry;
262 }
263
264 EAPI const Eina_Safepointer *
eina_safepointer_register(const void * target)265 eina_safepointer_register(const void *target)
266 {
267 Eina_Memory_Table *table;
268 Eina_Memory_Entry *entry = NULL;
269 Eina_Sp_Id id = 0;
270 unsigned int gen;
271
272 // We silently handle NULL
273 if (!target) return NULL;
274
275 eina_spinlock_take(&sl);
276
277 table = _eina_safepointer_table_find();
278 if (!table) goto no_table;
279
280 UNPROTECT(table);
281 entry = _eina_safepointer_entry_find(table);
282 if (!entry) goto on_error;
283
284 entry->ptr = (void*) target;
285 entry->active = 1;
286 gen = entry->generation + 1;
287 entry->generation = (gen == EINA_MAX_GENERATIONS) ? 1 : gen;
288
289 id = SP_COMPOSE_FINAL_ID(table->partial_id,
290 (entry - table->entries),
291 entry->generation);
292
293 on_error:
294 PROTECT(table);
295 no_table:
296 eina_spinlock_release(&sl);
297
298 return (void*) id;
299 }
300
301 EAPI void
eina_safepointer_unregister(const Eina_Safepointer * safe)302 eina_safepointer_unregister(const Eina_Safepointer *safe)
303 {
304 Eina_Memory_Table *table;
305 Eina_Memory_Entry *entry;
306 Eina_Table_Index entry_id;
307
308 // We silently handle NULL
309 if (!safe) return ;
310
311 entry = _eina_safepointer_entry_get(safe, &table);
312 if (!entry) return ;
313
314 eina_spinlock_take(&sl);
315
316 // In case of a race condition during a double free attempt
317 // The entry could have been unactivated since we did found it
318 // So check again.
319 if (!entry->active) goto on_error;
320
321 UNPROTECT(table);
322 entry->active = 0;
323 eina_trash_push(&table->trash, entry);
324 PROTECT(table);
325
326 entry_id = entry - table->entries;
327 if (entry_id == EINA_MAX_ENTRY_ID - 1)
328 {
329 Eina_Table_Index i;
330
331 for (i = entry_id; i >= 0; i--)
332 {
333 if (table->entries[i].active)
334 break ;
335 }
336
337 // No more active entry
338 // Could be speed up by tracking the
339 // number of allocated entries, but
340 // with all the syscall around, not sure
341 // it is worth it.
342 if (i == -1)
343 {
344 Eina_Table_Index mid_table_id, table_id;
345
346 mid_table_id = (table->partial_id >> EINA_SHIFT_MID_TABLE_ID) & EINA_MASK_MID_TABLE_ID;
347 table_id = (table->partial_id >> EINA_SHIFT_TABLE_ID) & EINA_MASK_TABLE_ID;
348 UNPROTECT(_eina_sp_ids_tables[mid_table_id]);
349 _eina_sp_ids_tables[mid_table_id][table_id] = NULL;
350 PROTECT(_eina_sp_ids_tables[mid_table_id]);
351 if (!empty_table)
352 empty_table = table;
353 else
354 _eina_safepointer_free(table);
355 }
356 }
357
358 on_error:
359 eina_spinlock_release(&sl);
360 }
361
362 Eina_Bool
eina_safepointer_init(void)363 eina_safepointer_init(void)
364 {
365 eina_magic_string_set(SAFEPOINTER_MAGIC, "Safepointer");
366 _eina_sp_log_dom = eina_log_domain_register("eina_safepointer",
367 EINA_LOG_COLOR_DEFAULT);
368 if (_eina_sp_log_dom < 0)
369 {
370 EINA_LOG_ERR("Could not register log domain: eina_safepointer.");
371 return EINA_FALSE;
372 }
373
374 eina_spinlock_new(&sl);
375
376 DBG("entry[Size, Align] = { %zu, %u }",
377 sizeof (Eina_Memory_Entry), eina_mempool_alignof(sizeof (Eina_Memory_Entry)));
378 DBG("table[Size, Align] = { %zu, %u }",
379 sizeof (Eina_Memory_Table), eina_mempool_alignof(sizeof (Eina_Memory_Table)));
380
381 return EINA_TRUE;
382 }
383
384 Eina_Bool
eina_safepointer_shutdown(void)385 eina_safepointer_shutdown(void)
386 {
387 eina_spinlock_free(&sl);
388
389 return EINA_TRUE;
390 }
391