summaryrefslogtreecommitdiff
path: root/src/lib/eina
diff options
context:
space:
mode:
authorJean-Philippe Andre <jp.andre@samsung.com>2017-01-11 14:48:57 +0900
committerJean-Philippe Andre <jp.andre@samsung.com>2017-01-17 14:05:16 +0900
commit4f5e64fdea88ba7362d77a6d42ea22cf13d3323f (patch)
tree42d0e98027484e564e56a10cb748154cec554742 /src/lib/eina
parentb1105da1dae316081f34c71df0d5fdd4afabb9f8 (diff)
eina_freeq: Add mode for postponed deletion
While this reuses the existing (but new) infrastructure of eina_freeq, the mode of operation and objective is very different from the default freeq. By default, any object added to the freeq is basically already freed from the user point of view, and the freeq itself only adds a tiny layer of memory safety by deferring the actual call to free and optionally filling the memory blob with a pattern ('wwwww...'). This is mostly thread-safe (requires thread-safe free functions). This new type I called postponed is intended to store objects that will be short lived. This is not thread safe as the life of the objects added to this queue depends on the thread that adds to the queue. The main intent is to introduce a new API for short-lived strings. @feature
Diffstat (limited to 'src/lib/eina')
-rw-r--r--src/lib/eina/eina_freeq.c133
-rw-r--r--src/lib/eina/eina_freeq.h70
-rw-r--r--src/lib/eina/eina_main.c2
3 files changed, 161 insertions, 44 deletions
diff --git a/src/lib/eina/eina_freeq.c b/src/lib/eina/eina_freeq.c
index 17459dc5ee..0485501c03 100644
--- a/src/lib/eina/eina_freeq.c
+++ b/src/lib/eina/eina_freeq.c
@@ -45,14 +45,16 @@ struct _Eina_FreeQ_Block
45 45
46struct _Eina_FreeQ 46struct _Eina_FreeQ
47{ 47{
48 Eina_Lock lock; 48 Eina_Lock lock; // recursive lock, unused for postponed queues (thread-local)
49 int count; // number of item slots used 49 int count; // number of item slots used
50 int count_max; // the maximum number of slots allowed to be used 50 int count_max; // maximum number of slots allowed to be used or -1
51 size_t mem_max; // the maximum amount of memory allowed to be used 51 size_t mem_max; // the maximum amount of memory allowed to be used
52 size_t mem_total; // current total memory known about in the queue 52 size_t mem_total; // current total memory known about in the queue
53 Eina_FreeQ_Block *blocks; // the list of blocks of free items 53 Eina_FreeQ_Block *blocks; // the list of blocks of free items
54 Eina_FreeQ_Block *block_last; // the last block to append items to 54 Eina_FreeQ_Block *block_last; // the last block to append items to
55 int bypass; // 0 if not to bypass, 1 if we should bypass 55 Eina_Bool bypass; // 0 if not to bypass, 1 if we should bypass
56 Eina_Bool postponed; // 1 if postponed type of freeq (eg. temp strings)
57 Eina_Bool unlocked; // 0 by default, 1 if thread-local (lock not used)
56}; 58};
57 59
58// ========================================================================= // 60// ========================================================================= //
@@ -67,6 +69,13 @@ static size_t _eina_freeq_mem_max = ITEM_MEM_MAX;
67 69
68// ========================================================================= // 70// ========================================================================= //
69 71
72#define LOCK_FQ(fq); do { \
73 if (!fq->unlocked) eina_lock_take(&(fq->lock)); } while(0)
74#define UNLOCK_FQ(fq); do { \
75 if (!fq->unlocked) eina_lock_release(&(fq->lock)); } while(0)
76
77// ========================================================================= //
78
70static inline void 79static inline void
71_eina_freeq_fill_do(void *ptr, size_t size) 80_eina_freeq_fill_do(void *ptr, size_t size)
72{ 81{
@@ -146,18 +155,20 @@ _eina_freeq_process(Eina_FreeQ *fq)
146static void 155static void
147_eina_freeq_flush_nolock(Eina_FreeQ *fq) 156_eina_freeq_flush_nolock(Eina_FreeQ *fq)
148{ 157{
158 if (fq->postponed) return;
159
149 while ((fq->count > fq->count_max) || (fq->mem_total > fq->mem_max)) 160 while ((fq->count > fq->count_max) || (fq->mem_total > fq->mem_max))
150 _eina_freeq_process(fq); 161 _eina_freeq_process(fq);
151} 162}
152 163
153// ========================================================================= // 164// ========================================================================= //
154 165
155EAPI Eina_FreeQ * 166static Eina_FreeQ *
156eina_freeq_new(void) 167_eina_freeq_new_default(void)
157{ 168{
158 Eina_FreeQ *fq; 169 Eina_FreeQ *fq;
159 170
160 if (_eina_freeq_bypass == -1) 171 if (EINA_UNLIKELY(_eina_freeq_bypass == -1))
161 { 172 {
162 const char *s; 173 const char *s;
163 int v; 174 int v;
@@ -197,16 +208,53 @@ eina_freeq_new(void)
197 return fq; 208 return fq;
198} 209}
199 210
211static Eina_FreeQ *
212_eina_freeq_new_postponed(void)
213{
214 Eina_FreeQ *fq;
215
216 fq= calloc(1, sizeof(*fq));
217 if (!fq) return NULL;
218 fq->mem_max = 0;
219 fq->count_max = -1;
220 fq->postponed = EINA_TRUE;
221 fq->unlocked = EINA_TRUE;
222 return fq;
223}
224
225EAPI Eina_FreeQ *
226eina_freeq_new(Eina_FreeQ_Type type)
227{
228 switch (type)
229 {
230 case EINA_FREEQ_DEFAULT:
231 return _eina_freeq_new_default();
232 case EINA_FREEQ_POSTPONED:
233 return _eina_freeq_new_postponed();
234 default:
235 return NULL;
236 }
237}
238
200EAPI void 239EAPI void
201eina_freeq_free(Eina_FreeQ *fq) 240eina_freeq_free(Eina_FreeQ *fq)
202{ 241{
203 if (!fq) return; 242 if (!fq) return;
204 if (fq == _eina_freeq_main) _eina_freeq_main = NULL; 243 if (fq == _eina_freeq_main) _eina_freeq_main = NULL;
205 eina_freeq_clear(fq); 244 eina_freeq_clear(fq);
206 eina_lock_free(&(fq->lock)); 245 if (!fq->unlocked) eina_lock_free(&(fq->lock));
207 free(fq); 246 free(fq);
208} 247}
209 248
249EAPI Eina_FreeQ_Type
250eina_freeq_type_get(Eina_FreeQ *fq)
251{
252 if (fq && fq->postponed)
253 return EINA_FREEQ_POSTPONED;
254 return EINA_FREEQ_DEFAULT;
255}
256
257/* FIXME This function should not exist as an EAPI */
210EAPI void 258EAPI void
211eina_freeq_main_set(Eina_FreeQ *fq) 259eina_freeq_main_set(Eina_FreeQ *fq)
212{ 260{
@@ -224,12 +272,13 @@ EAPI void
224eina_freeq_count_max_set(Eina_FreeQ *fq, int count) 272eina_freeq_count_max_set(Eina_FreeQ *fq, int count)
225{ 273{
226 if (!fq) return; 274 if (!fq) return;
227 if (count < 0) count = 0; 275 if (fq->postponed) return;
228 eina_lock_take(&(fq->lock)); 276 if (count < 0) count = -1;
277 LOCK_FQ(fq);
229 fq->bypass = 0; 278 fq->bypass = 0;
230 fq->count_max = count; 279 fq->count_max = count;
231 _eina_freeq_flush_nolock(fq); 280 _eina_freeq_flush_nolock(fq);
232 eina_lock_release(&(fq->lock)); 281 UNLOCK_FQ(fq);
233} 282}
234 283
235EAPI int 284EAPI int
@@ -238,10 +287,10 @@ eina_freeq_count_max_get(Eina_FreeQ *fq)
238 int count; 287 int count;
239 288
240 if (!fq) return 0; 289 if (!fq) return 0;
241 eina_lock_take(&(fq->lock)); 290 LOCK_FQ(fq);
242 if (fq->bypass) count = 0; 291 if (fq->bypass) count = 0;
243 else count = fq->count_max; 292 else count = fq->count_max;
244 eina_lock_release(&(fq->lock)); 293 UNLOCK_FQ(fq);
245 return count; 294 return count;
246} 295}
247 296
@@ -249,11 +298,12 @@ EAPI void
249eina_freeq_mem_max_set(Eina_FreeQ *fq, size_t mem) 298eina_freeq_mem_max_set(Eina_FreeQ *fq, size_t mem)
250{ 299{
251 if (!fq) return; 300 if (!fq) return;
252 eina_lock_take(&(fq->lock)); 301 if (fq->postponed) return;
302 LOCK_FQ(fq);
253 fq->bypass = 0; 303 fq->bypass = 0;
254 fq->mem_max = mem; 304 fq->mem_max = mem;
255 _eina_freeq_flush_nolock(fq); 305 _eina_freeq_flush_nolock(fq);
256 eina_lock_release(&(fq->lock)); 306 UNLOCK_FQ(fq);
257} 307}
258 308
259EAPI size_t 309EAPI size_t
@@ -262,10 +312,10 @@ eina_freeq_mem_max_get(Eina_FreeQ *fq)
262 size_t mem; 312 size_t mem;
263 313
264 if (!fq) return 0; 314 if (!fq) return 0;
265 eina_lock_take(&(fq->lock)); 315 LOCK_FQ(fq);
266 if (fq->bypass) mem = 0; 316 if (fq->bypass) mem = 0;
267 else mem = fq->mem_max; 317 else mem = fq->mem_max;
268 eina_lock_release(&(fq->lock)); 318 UNLOCK_FQ(fq);
269 return mem; 319 return mem;
270} 320}
271 321
@@ -273,22 +323,22 @@ EAPI void
273eina_freeq_clear(Eina_FreeQ *fq) 323eina_freeq_clear(Eina_FreeQ *fq)
274{ 324{
275 if (!fq) return; 325 if (!fq) return;
276 eina_lock_take(&(fq->lock)); 326 LOCK_FQ(fq);
277 while (fq->count > 0) _eina_freeq_process(fq); 327 while (fq->count > 0) _eina_freeq_process(fq);
278 eina_lock_release(&(fq->lock)); 328 UNLOCK_FQ(fq);
279} 329}
280 330
281EAPI void 331EAPI void
282eina_freeq_reduce(Eina_FreeQ *fq, int count) 332eina_freeq_reduce(Eina_FreeQ *fq, int count)
283{ 333{
284 if (!fq) return; 334 if (!fq) return;
285 eina_lock_take(&(fq->lock)); 335 LOCK_FQ(fq);
286 while ((fq->count > 0) && (count > 0)) 336 while ((fq->count > 0) && (count > 0))
287 { 337 {
288 _eina_freeq_process(fq); 338 _eina_freeq_process(fq);
289 count--; 339 count--;
290 } 340 }
291 eina_lock_release(&(fq->lock)); 341 UNLOCK_FQ(fq);
292} 342}
293 343
294EAPI Eina_Bool 344EAPI Eina_Bool
@@ -297,10 +347,10 @@ eina_freeq_ptr_pending(Eina_FreeQ *fq)
297 Eina_Bool pending; 347 Eina_Bool pending;
298 348
299 if (!fq) return EINA_FALSE; 349 if (!fq) return EINA_FALSE;
300 eina_lock_take(&(fq->lock)); 350 LOCK_FQ(fq);
301 if (fq->blocks) pending = EINA_TRUE; 351 if (fq->blocks) pending = EINA_TRUE;
302 else pending = EINA_FALSE; 352 else pending = EINA_FALSE;
303 eina_lock_release(&(fq->lock)); 353 UNLOCK_FQ(fq);
304 return pending; 354 return pending;
305} 355}
306 356
@@ -314,13 +364,30 @@ eina_freeq_ptr_add(Eina_FreeQ *fq,
314 364
315 if (!ptr) return; 365 if (!ptr) return;
316 if (!free_func) free_func = free; 366 if (!free_func) free_func = free;
317 if ((size < _eina_freeq_fillpat_max) && (size > 0)) 367 if (!fq->postponed && (size < _eina_freeq_fillpat_max) && (size > 0))
318 _eina_freeq_fill_do(ptr, size); 368 _eina_freeq_fill_do(ptr, size);
319 if ((!fq) || (fq->bypass)) goto insta_free; 369
320 eina_lock_take(&(fq->lock)); 370 if (!fq || fq->bypass)
371 {
372 _eina_freeq_free_do(ptr, free_func, size);
373 return;
374 }
375
376 LOCK_FQ(fq);
321 if ((!fq->block_last) || (fq->block_last->end == ITEM_BLOCK_COUNT)) 377 if ((!fq->block_last) || (fq->block_last->end == ITEM_BLOCK_COUNT))
322 goto newblock; 378 {
323newblock_done: 379 if (!_eina_freeq_block_append(fq))
380 {
381 UNLOCK_FQ(fq);
382 if (!fq->postponed)
383 _eina_freeq_free_do(ptr, free_func, size);
384 else
385 EINA_LOG_ERR("Could not add a pointer to the free queue! This "
386 "program will leak resources!");
387 return;
388 }
389 }
390
324 fb = fq->block_last; 391 fb = fq->block_last;
325 fb->items[fb->end].ptr = ptr; 392 fb->items[fb->end].ptr = ptr;
326 fb->items[fb->end].free_func = free_func; 393 fb->items[fb->end].free_func = free_func;
@@ -329,15 +396,5 @@ newblock_done:
329 fq->count++; 396 fq->count++;
330 fq->mem_total += size; 397 fq->mem_total += size;
331 _eina_freeq_flush_nolock(fq); 398 _eina_freeq_flush_nolock(fq);
332 eina_lock_release(&(fq->lock)); 399 UNLOCK_FQ(fq);
333 return;
334newblock:
335 if (!_eina_freeq_block_append(fq))
336 {
337 eina_lock_release(&(fq->lock));
338insta_free:
339 _eina_freeq_free_do(ptr, free_func, size);
340 return;
341 }
342 goto newblock_done;
343} 400}
diff --git a/src/lib/eina/eina_freeq.h b/src/lib/eina/eina_freeq.h
index eacd3a0ca9..643aa1e5ce 100644
--- a/src/lib/eina/eina_freeq.h
+++ b/src/lib/eina/eina_freeq.h
@@ -15,7 +15,8 @@
15 * data at some time in the future. The main free queue will be driven 15 * data at some time in the future. The main free queue will be driven
16 * by the EFL main loop and ensure data is eventually freed. 16 * by the EFL main loop and ensure data is eventually freed.
17 * 17 *
18 * For debugging and tuning you may set the following envrionment variables: 18 * For debugging and tuning you may set the following environment variables,
19 * applicable only to free queues of the default type:
19 * 20 *
20 * EINA_FREEQ_BYPASS=1/0 21 * EINA_FREEQ_BYPASS=1/0
21 * 22 *
@@ -76,6 +77,48 @@
76 */ 77 */
77typedef struct _Eina_FreeQ Eina_FreeQ; 78typedef struct _Eina_FreeQ Eina_FreeQ;
78 79
80/** @brief Type of free queues
81 *
82 * @since 1.19
83 */
84typedef enum _Eina_FreeQ_Type
85{
86 /** @brief Default type of free queue.
87 *
88 * Default free queue, any object added to it should be considered freed
89 * immediately. Use this kind of freeq for debugging and additional memory
90 * safety purposes only.
91 *
92 * This type of free queue is thread-safe.
93 *
94 * @since 1.19
95 */
96 EINA_FREEQ_DEFAULT,
97
98 /** @brief Postponed type of free queue.
99 *
100 * Postponed free queues behave differently in that objects added to it
101 * are not to be considered freed immediately, but rather they are
102 * short-lived. Use this to return temporary objects that may be used only
103 * in the local scope. The queued objects lifetime ends as soon as the
104 * execution comes back to the loop. Objects added to this kind of free
105 * queue should be accessed exclusively from the same thread that adds them.
106 *
107 * If a thread does not have a loop attached, the application may leak all
108 * those objects. At the moment of writing this means only the main loop
109 * should use such a free queue.
110 *
111 * By default those queues have no memory limit, and will be entirely
112 * flushed when the execution comes back to the loop.
113 *
114 * This type of free queue is not thread-safe and should be considered local
115 * to a single thread.
116 *
117 * @since 1.19
118 */
119 EINA_FREEQ_POSTPONED,
120} Eina_FreeQ_Type;
121
79/** 122/**
80 * @brief Create a new free queue to defer freeing of data with 123 * @brief Create a new free queue to defer freeing of data with
81 * 124 *
@@ -83,7 +126,7 @@ typedef struct _Eina_FreeQ Eina_FreeQ;
83 * @since 1.19 126 * @since 1.19
84 */ 127 */
85EAPI Eina_FreeQ * 128EAPI Eina_FreeQ *
86eina_freeq_new(void); 129eina_freeq_new(Eina_FreeQ_Type type);
87 130
88/** 131/**
89 * @brief Free a free queue and anything that is queued in it. 132 * @brief Free a free queue and anything that is queued in it.
@@ -96,6 +139,16 @@ EAPI void
96eina_freeq_free(Eina_FreeQ *fq); 139eina_freeq_free(Eina_FreeQ *fq);
97 140
98/** 141/**
142 * @brief Query the type of a free queue.
143 *
144 * @param fq The free queue to inspect.
145 *
146 * @since 1.19
147 */
148EAPI Eina_FreeQ_Type
149eina_freeq_type_get(Eina_FreeQ *fq);
150
151/**
99 * @brief Set the main free queue driven by the EFL mainloop. 152 * @brief Set the main free queue driven by the EFL mainloop.
100 * 153 *
101 * @param fq The free queue to set as the main loop one. 154 * @param fq The free queue to set as the main loop one.
@@ -119,14 +172,18 @@ eina_freeq_main_get(void);
119 * @brief Set the maximum number of free pointers this queue is allowed 172 * @brief Set the maximum number of free pointers this queue is allowed
120 * 173 *
121 * @param fq The free queue to alter 174 * @param fq The free queue to alter
122 * @param count The maximum number of items allowed (must be >= 0) 175 * @param count The maximum number of items allowed, negative values mean
176 * no limit
123 * 177 *
124 * This will alter the maximum number of pointers allowed in the given free 178 * This will alter the maximum number of pointers allowed in the given free
125 * queue. If more items are added to the free queue than are allowed, 179 * queue. If more items are added to the free queue than are allowed,
126 * excess items will be freed to make room for the new items. If count is 180 * excess items will be freed to make room for the new items. If count is
127 * changed, then excess items may be cleaned out at the time this API is 181 * changed, then excess items may be cleaned out at the time this API is
128 * called. 182 * called.
129 * 183 *
184 * @note Setting a maximum count on a postponed free queue leads to undefined
185 * behaviour.
186 *
130 * @since 1.19 187 * @since 1.19
131 */ 188 */
132EAPI void 189EAPI void
@@ -136,7 +193,7 @@ eina_freeq_count_max_set(Eina_FreeQ *fq, int count);
136 * @brief Get the maximum number of free pointers this queue is allowed 193 * @brief Get the maximum number of free pointers this queue is allowed
137 * 194 *
138 * @param fq The free queue to query 195 * @param fq The free queue to query
139 * @return The maximum number of free items allowed 196 * @return The maximum number of free items allowed or -1 for infinity
140 * 197 *
141 * @since 1.19 198 * @since 1.19
142 */ 199 */
@@ -156,6 +213,9 @@ eina_freeq_count_max_get(Eina_FreeQ *fq);
156 * until the total is below this limit. Changing the limit may involve 213 * until the total is below this limit. Changing the limit may involve
157 * cleaning out excess items from the free queue until the total amount of 214 * cleaning out excess items from the free queue until the total amount of
158 * memory used by items in the queue is below or at the limit. 215 * memory used by items in the queue is below or at the limit.
216 *
217 * @note Setting a memory limit on a postponed free queue leads to undefined
218 * behaviour.
159 * 219 *
160 * @since 1.19 220 * @since 1.19
161 */ 221 */
diff --git a/src/lib/eina/eina_main.c b/src/lib/eina/eina_main.c
index 2f754e5360..ab0aec90b1 100644
--- a/src/lib/eina/eina_main.c
+++ b/src/lib/eina/eina_main.c
@@ -255,7 +255,7 @@ eina_init(void)
255 mtrace(); 255 mtrace();
256 } 256 }
257#endif 257#endif
258 eina_freeq_main_set(eina_freeq_new()); 258 eina_freeq_main_set(eina_freeq_new(EINA_FREEQ_DEFAULT));
259 259
260 if (!eina_log_init()) 260 if (!eina_log_init())
261 { 261 {