summaryrefslogtreecommitdiff
path: root/src/lib/eina/eina_promise.c
diff options
context:
space:
mode:
authorFelipe Magno de Almeida <felipe@expertisesolutions.com.br>2016-04-01 14:49:58 -0300
committerCedric Bail <cedric@osg.samsung.com>2016-04-05 11:35:12 -0700
commit09eea7bc01dd1381589eeab35e705cbd45820f1e (patch)
tree99fe671e347803af2052aede2d4f58f37768c416 /src/lib/eina/eina_promise.c
parentaba46af602c8b45815974ed25303874e9a368c86 (diff)
eina: add promise
Add a promise object that will allows Eolian interface to include promises as a way to have asynchronous value return and composibility. To understand better, let see the coming usage in a .eo file: class Foo { methods { bar { params { @inout promise: Promise<int>; } } } } Which will create the following API interface: void foo_bar(Eo* obj, Eina_Promise** promise); and the equivalent declaration for implementation. However, the API function will instantiate the Promise for the user and the implementer of the class automatically. So the user of this function will treat it as a @out parameter, while the developer of the function will treat it like a @inout parameter. So, the user will use this function like this: Eina_Promise* promise; // No need to instantiate foo_bar(obj, &promise); eina_promise_then(promise, callback); Signed-off-by: Cedric Bail <cedric@osg.samsung.com>
Diffstat (limited to '')
-rw-r--r--src/lib/eina/eina_promise.c640
1 files changed, 640 insertions, 0 deletions
diff --git a/src/lib/eina/eina_promise.c b/src/lib/eina/eina_promise.c
new file mode 100644
index 0000000000..9a7c259474
--- /dev/null
+++ b/src/lib/eina/eina_promise.c
@@ -0,0 +1,640 @@
1#ifdef HAVE_CONFIG_H
2#include <config.h>
3#endif
4
5#include <Eina.h>
6
7#include <assert.h>
8
9typedef struct _Eina_Promise_Then_Cb _Eina_Promise_Then_Cb;
10typedef struct _Eina_Promise_Progress_Cb _Eina_Promise_Progress_Cb;
11typedef struct _Eina_Promise_Cancel_Cb _Eina_Promise_Cancel_Cb;
12typedef struct _Eina_Promise_Default _Eina_Promise_Default;
13typedef struct _Eina_Promise_Default_Owner _Eina_Promise_Default_Owner;
14typedef struct _Eina_Promise_Iterator _Eina_Promise_Iterator;
15typedef struct _Eina_Promise_Success_Iterator _Eina_Promise_Success_Iterator;
16
17struct _Eina_Promise_Then_Cb
18{
19 EINA_INLIST;
20
21 Eina_Promise_Cb callback;
22 Eina_Promise_Error_Cb error_cb;
23 void* data;
24};
25
26struct _Eina_Promise_Progress_Cb
27{
28 EINA_INLIST;
29
30 Eina_Promise_Progress_Cb callback;
31 void* data;
32};
33
34struct _Eina_Promise_Cancel_Cb
35{
36 EINA_INLIST;
37
38 Eina_Promise_Default_Cancel_Cb callback;
39 Eina_Promise_Free_Cb free;
40 void* data;
41};
42
43struct _Eina_Promise_Default
44{
45 Eina_Promise vtable;
46 Eina_Error error;
47 size_t value_size;
48
49 Eina_Inlist *then_callbacks;
50 Eina_Inlist *progress_callbacks;
51 Eina_Inlist *cancel_callbacks;
52 Eina_Promise_Free_Cb value_free_cb;
53
54 int ref;
55
56 Eina_Bool has_finished : 1;
57 Eina_Bool has_errored : 1;
58 Eina_Bool is_cancelled : 1;
59 Eina_Bool is_manual_then : 1;
60 Eina_Bool is_first_then : 1;
61};
62
63struct _Eina_Promise_Default_Owner
64{
65 Eina_Promise_Owner owner_vtable;
66 _Eina_Promise_Default promise;
67
68 char value[];
69};
70
71#define EINA_PROMISE_GET_OWNER(p) (_Eina_Promise_Default_Owner*)((unsigned char*)p - offsetof(struct _Eina_Promise_Default_Owner, promise))
72
73struct _Eina_Promise_Iterator
74{
75 Eina_Iterator* success_iterator;
76 struct _Eina_Promise_Success_Iterator
77 {
78 Eina_Iterator success_iterator_impl;
79 unsigned int promise_index;
80 unsigned int num_promises;
81 unsigned int promises_finished;
82 Eina_Promise* promises[];
83 } data;
84};
85
86static void _eina_promise_finish(_Eina_Promise_Default_Owner* promise);
87static void _eina_promise_ref(_Eina_Promise_Default* promise);
88static void _eina_promise_unref(_Eina_Promise_Default* promise);
89
90static void _eina_promise_iterator_setup(_Eina_Promise_Iterator* iterator, Eina_Array* promises);
91
92static void
93_eina_promise_then_calls(_Eina_Promise_Default_Owner* promise)
94{
95 _Eina_Promise_Then_Cb* callback;
96 Eina_Inlist* list2;
97 Eina_Bool error;
98
99 _eina_promise_ref(&promise->promise);
100 error = promise->promise.has_errored;
101
102 EINA_INLIST_FOREACH_SAFE(promise->promise.then_callbacks, list2, callback)
103 {
104 if (error)
105 {
106 if (callback->error_cb)
107 (*callback->error_cb)(callback->data, &promise->promise.error);
108 }
109 else if (callback->callback)
110 {
111 fprintf(stderr, "then callback\n");
112 (*callback->callback)(callback->data, &promise->value[0]);
113 }
114 _eina_promise_unref(&promise->promise);
115 }
116 _eina_promise_unref(&promise->promise);
117}
118
119static void
120_eina_promise_cancel_calls(_Eina_Promise_Default_Owner* promise, Eina_Bool call_cancel EINA_UNUSED)
121{
122 _Eina_Promise_Cancel_Cb* callback;
123 Eina_Inlist* list2;
124
125 EINA_INLIST_FOREACH_SAFE(promise->promise.cancel_callbacks, list2, callback)
126 {
127 if (callback->callback)
128 {
129 (*callback->callback)(callback->data, (Eina_Promise_Owner*)promise);
130 }
131 }
132
133 if (!promise->promise.is_manual_then)
134 {
135 _eina_promise_then_calls(promise);
136 }
137}
138
139static void
140_eina_promise_del(_Eina_Promise_Default_Owner* promise)
141{
142 if (promise->promise.has_finished)
143 {
144 if (promise->promise.value_free_cb)
145 promise->promise.value_free_cb((void*)&promise->value[0]);
146 }
147 else
148 {
149 _eina_promise_cancel_calls(promise, EINA_TRUE);
150 }
151}
152
153static void *
154_eina_promise_buffer_get(_Eina_Promise_Default_Owner* promise)
155{
156 return &promise->value[0];
157}
158
159static void *
160_eina_promise_value_get(_Eina_Promise_Default_Owner const* promise)
161{
162 if (promise->promise.has_finished)
163 {
164 return (void*)(promise->promise.value_size && !promise->promise.has_errored ?
165 &promise->value[0] : NULL);
166 }
167 else
168 {
169 return NULL;
170 }
171}
172
173static void
174_eina_promise_value_set(_Eina_Promise_Default_Owner* promise, void* data, Eina_Promise_Free_Cb free)
175{
176 if (data && promise->promise.value_size)
177 {
178 memcpy(&promise->value[0], data, promise->promise.value_size);
179 }
180
181 promise->promise.value_free_cb = free;
182
183 _eina_promise_finish(promise);
184}
185
186static void
187_eina_promise_then(_Eina_Promise_Default* p, Eina_Promise_Cb callback,
188 Eina_Promise_Error_Cb error_cb, void* data)
189{
190 _Eina_Promise_Default_Owner* promise;
191 _Eina_Promise_Then_Cb* cb;
192
193 promise = EINA_PROMISE_GET_OWNER(p);
194
195 cb = malloc(sizeof(struct _Eina_Promise_Then_Cb));
196
197 cb->callback = callback;
198 cb->error_cb = error_cb;
199 cb->data = data;
200 promise->promise.then_callbacks = eina_inlist_append(promise->promise.then_callbacks, EINA_INLIST_GET(cb));
201 fprintf(stderr, "appending then callback\n");
202
203 if (!promise->promise.is_first_then)
204 {
205 _eina_promise_ref(p);
206 promise->promise.is_first_then = EINA_FALSE;
207 }
208 if (promise->promise.has_finished)
209 {
210 _eina_promise_then_calls(promise);
211 }
212}
213
214static void
215_eina_promise_error_set(_Eina_Promise_Default_Owner* promise, Eina_Error error)
216{
217 promise->promise.error = error;
218 promise->promise.has_errored = EINA_TRUE;
219
220 _eina_promise_finish(promise);
221}
222
223static void
224_eina_promise_finish(_Eina_Promise_Default_Owner* promise)
225{
226 promise->promise.has_finished = EINA_TRUE;
227 if (!promise->promise.is_manual_then)
228 {
229 _eina_promise_then_calls(promise);
230 }
231}
232
233static Eina_Error
234_eina_promise_error_get(_Eina_Promise_Default_Owner const* promise)
235{
236 if (promise->promise.has_errored)
237 {
238 return promise->promise.error;
239 }
240 else
241 {
242 return 0;
243 }
244}
245
246static Eina_Bool
247_eina_promise_pending_is(_Eina_Promise_Default const* promise)
248{
249 return !promise->has_finished;
250}
251
252static Eina_Bool
253_eina_promise_owner_pending_is(_Eina_Promise_Default_Owner const* promise)
254{
255 return !promise->promise.has_finished;
256}
257
258static Eina_Bool
259_eina_promise_owner_cancelled_is(_Eina_Promise_Default_Owner const* promise)
260{
261 return promise->promise.is_cancelled;
262}
263
264static void
265_eina_promise_progress_cb_add(_Eina_Promise_Default* promise, Eina_Promise_Progress_Cb callback, void* data)
266{
267 _Eina_Promise_Progress_Cb* cb;
268
269 cb = malloc(sizeof(struct _Eina_Promise_Progress_Cb));
270 cb->callback = callback;
271 cb->data = data;
272 promise->progress_callbacks = eina_inlist_append(promise->progress_callbacks, EINA_INLIST_GET(cb));
273}
274
275static void
276_eina_promise_cancel(_Eina_Promise_Default* promise)
277{
278 _Eina_Promise_Default_Owner* owner = EINA_PROMISE_GET_OWNER(promise);
279
280 if (!owner->promise.is_cancelled)
281 {
282 owner->promise.is_cancelled = EINA_TRUE;
283 owner->promise.has_finished = EINA_TRUE;
284 owner->promise.has_errored = EINA_TRUE;
285 _eina_promise_cancel_calls(owner, EINA_TRUE);
286 }
287}
288
289static size_t
290_eina_promise_value_size_get(_Eina_Promise_Default_Owner const* promise)
291{
292 return promise->promise.value_size;
293}
294
295static void
296_eina_promise_ref(_Eina_Promise_Default* p)
297{
298 ++p->ref;
299}
300
301static void
302_eina_promise_unref(_Eina_Promise_Default* p)
303{
304 if (p->ref == 1 && p->has_finished)
305 {
306 _eina_promise_del(EINA_PROMISE_GET_OWNER(p));
307 }
308 else
309 {
310 --p->ref;
311 }
312}
313
314static Eina_Promise *
315_eina_promise_owner_promise_get(_Eina_Promise_Default_Owner* p)
316{
317 return &p->promise.vtable;
318}
319
320void
321eina_promise_owner_default_cancel_cb_add(Eina_Promise_Owner* p,
322 Eina_Promise_Default_Cancel_Cb callback, void* data,
323 Eina_Promise_Free_Cb free_cb)
324{
325 _Eina_Promise_Default_Owner *promise;
326 _Eina_Promise_Cancel_Cb *cb;
327
328 promise = (_Eina_Promise_Default_Owner *)p;
329
330 cb = malloc(sizeof(struct _Eina_Promise_Cancel_Cb));
331
332 cb->callback = callback;
333 cb->free = free_cb;
334 cb->data = data;
335 promise->promise.cancel_callbacks = eina_inlist_append(promise->promise.cancel_callbacks, EINA_INLIST_GET(cb));
336
337 if (promise->promise.is_cancelled)
338 {
339 _eina_promise_cancel_calls(promise, EINA_TRUE);
340 }
341}
342
343static void
344_eina_promise_owner_progress(_Eina_Promise_Default_Owner* promise, void* data)
345{
346 _Eina_Promise_Progress_Cb* callback;
347 Eina_Inlist* list2;
348
349 EINA_INLIST_FOREACH_SAFE(promise->promise.progress_callbacks, list2, callback)
350 {
351 (*callback->callback)(callback->data, data);
352 }
353}
354
355Eina_Promise_Owner *
356eina_promise_default_add(int value_size)
357{
358 _Eina_Promise_Default_Owner* p;
359
360 p = malloc(sizeof(_Eina_Promise_Default_Owner) + value_size);
361 p->promise.vtable.version = EINA_PROMISE_VERSION;
362 p->promise.vtable.then = EINA_FUNC_PROMISE_THEN(_eina_promise_then);
363 p->promise.vtable.value_get = EINA_FUNC_PROMISE_VALUE_GET(_eina_promise_value_get);
364 p->promise.vtable.error_get = EINA_FUNC_PROMISE_ERROR_GET(_eina_promise_error_get);
365 p->promise.vtable.pending_is = EINA_FUNC_PROMISE_PENDING_IS(_eina_promise_pending_is);
366 p->promise.vtable.progress_cb_add = EINA_FUNC_PROMISE_PROGRESS_CB_ADD(_eina_promise_progress_cb_add);
367 p->promise.vtable.cancel = EINA_FUNC_PROMISE_CANCEL(_eina_promise_cancel);
368 p->promise.vtable.ref = EINA_FUNC_PROMISE_REF(_eina_promise_ref);
369 p->promise.vtable.unref = EINA_FUNC_PROMISE_UNREF(_eina_promise_unref);
370 p->promise.vtable.value_size_get = EINA_FUNC_PROMISE_VALUE_SIZE_GET(_eina_promise_value_size_get);
371 p->promise.has_finished = p->promise.has_errored =
372 p->promise.is_cancelled = p->promise.is_manual_then = EINA_FALSE;
373 p->promise.is_first_then = EINA_TRUE;
374 p->promise.ref = 1;
375 memset(&p->promise.then_callbacks, 0, sizeof(p->promise.then_callbacks));
376 memset(&p->promise.progress_callbacks, 0, sizeof(p->promise.progress_callbacks));
377 memset(&p->promise.cancel_callbacks, 0, sizeof(p->promise.cancel_callbacks));
378 p->promise.value_size = value_size;
379 p->promise.value_free_cb = NULL;
380
381 p->owner_vtable.version = EINA_PROMISE_VERSION;
382 p->owner_vtable.value_set = EINA_FUNC_PROMISE_OWNER_VALUE_SET(_eina_promise_value_set);
383 p->owner_vtable.error_set = EINA_FUNC_PROMISE_OWNER_ERROR_SET(_eina_promise_error_set);
384 p->owner_vtable.buffer_get = EINA_FUNC_PROMISE_OWNER_BUFFER_GET(_eina_promise_buffer_get);
385 p->owner_vtable.value_size_get = EINA_FUNC_PROMISE_OWNER_VALUE_SIZE_GET(_eina_promise_value_size_get);
386 p->owner_vtable.promise_get = EINA_FUNC_PROMISE_OWNER_PROMISE_GET(_eina_promise_owner_promise_get);
387 p->owner_vtable.pending_is = EINA_FUNC_PROMISE_OWNER_PENDING_IS(_eina_promise_owner_pending_is);
388 p->owner_vtable.cancelled_is = EINA_FUNC_PROMISE_OWNER_CANCELLED_IS(_eina_promise_owner_cancelled_is);
389 p->owner_vtable.progress = EINA_FUNC_PROMISE_OWNER_PROGRESS(_eina_promise_owner_progress);
390
391 return &p->owner_vtable;
392}
393
394void
395eina_promise_owner_default_manual_then_set(Eina_Promise_Owner* owner, Eina_Bool is_manual)
396{
397 _Eina_Promise_Default_Owner* p = (_Eina_Promise_Default_Owner*)owner;
398
399 p->promise.is_manual_then = is_manual;
400}
401
402void
403eina_promise_owner_default_call_then(Eina_Promise_Owner* promise)
404{
405 _Eina_Promise_Default_Owner* owner = (_Eina_Promise_Default_Owner*)promise;
406
407 _eina_promise_then_calls(owner);
408}
409
410static void
411_eina_promise_all_compose_then_cb(_Eina_Promise_Default_Owner* promise, void* value EINA_UNUSED)
412{
413 _Eina_Promise_Iterator* iterator;
414
415 if (!promise->promise.has_finished)
416 {
417 iterator = (_Eina_Promise_Iterator*)promise->value;
418 if (++iterator->data.promises_finished == iterator->data.num_promises)
419 {
420 _eina_promise_finish(promise);
421 }
422 }
423}
424
425static void
426_eina_promise_all_compose_error_then_cb(_Eina_Promise_Default_Owner* promise, Eina_Error const* error)
427{
428 if (!promise->promise.has_finished)
429 {
430 promise->promise.has_finished = promise->promise.has_errored = EINA_TRUE;
431 promise->promise.error = *error;
432 _eina_promise_finish(promise);
433 }
434}
435
436static void
437_eina_promise_all_free(_Eina_Promise_Iterator* value)
438{
439 unsigned i = 0;
440
441 eina_iterator_free(value->success_iterator);
442
443 for (;i != value->data.num_promises; ++i)
444 {
445 eina_promise_unref(value->data.promises[i]);
446 }
447}
448
449Eina_Promise *
450eina_promise_all(Eina_Iterator* it)
451{
452 _Eina_Promise_Default_Owner *promise;
453 Eina_Promise* current;
454 Eina_Array* promises;
455 Eina_Promise **cur_promise, **last;
456 _Eina_Promise_Iterator* internal_it;
457
458 promises = eina_array_new(20);
459
460 EINA_ITERATOR_FOREACH(it, current)
461 {
462 eina_array_push(promises, current);
463 }
464
465 eina_iterator_free(it);
466
467 promise = (_Eina_Promise_Default_Owner*)
468 eina_promise_default_add(sizeof(_Eina_Promise_Iterator) +
469 sizeof(_Eina_Promise_Default_Owner*)*eina_array_count_get(promises));
470 internal_it = (_Eina_Promise_Iterator*)&promise->value[0];
471 _eina_promise_iterator_setup(internal_it, promises);
472 eina_array_free(promises);
473
474 promise->promise.value_free_cb = (Eina_Promise_Free_Cb)&_eina_promise_all_free;
475
476 cur_promise = internal_it->data.promises;
477 last = internal_it->data.promises + internal_it->data.num_promises;
478 for (;cur_promise != last; ++cur_promise)
479 {
480 eina_promise_then(*cur_promise, (Eina_Promise_Cb)&_eina_promise_all_compose_then_cb,
481 (Eina_Promise_Error_Cb)&_eina_promise_all_compose_error_then_cb, promise);
482 eina_promise_ref(*cur_promise); // We need to keep the value alive until this promise is freed
483 }
484
485 return &promise->promise.vtable;
486}
487
488static Eina_Bool
489_eina_promise_iterator_next(_Eina_Promise_Success_Iterator *it, void **data)
490{
491 if (it->promise_index == it->num_promises)
492 return EINA_FALSE;
493
494 if (eina_promise_error_get(it->promises[it->promise_index]))
495 {
496 return EINA_FALSE;
497 }
498 else
499 {
500 *data = eina_promise_value_get(it->promises[it->promise_index++]);
501 return EINA_TRUE;
502 }
503}
504
505static void **
506_eina_promise_iterator_get_container(_Eina_Promise_Success_Iterator *it)
507{
508 return (void**)it->promises;
509}
510
511static void
512_eina_promise_iterator_free(_Eina_Promise_Success_Iterator *it EINA_UNUSED)
513{
514}
515
516static void
517_eina_promise_iterator_setup(_Eina_Promise_Iterator* it, Eina_Array* promises_array)
518{
519 Eina_Promise** promises;
520
521 it->success_iterator = &it->data.success_iterator_impl;
522 it->data.num_promises = eina_array_count_get(promises_array);
523 it->data.promise_index = 0;
524 it->data.promises_finished = 0;
525 promises = (Eina_Promise**)promises_array->data;
526
527 memcpy(&it->data.promises[0], promises, it->data.num_promises*sizeof(Eina_Promise*));
528
529 EINA_MAGIC_SET(&it->data.success_iterator_impl, EINA_MAGIC_ITERATOR);
530
531 it->data.success_iterator_impl.version = EINA_ITERATOR_VERSION;
532 it->data.success_iterator_impl.next = FUNC_ITERATOR_NEXT(_eina_promise_iterator_next);
533 it->data.success_iterator_impl.get_container = FUNC_ITERATOR_GET_CONTAINER(
534 _eina_promise_iterator_get_container);
535 it->data.success_iterator_impl.free = FUNC_ITERATOR_FREE(_eina_promise_iterator_free);
536}
537
538// API functions
539EAPI void
540eina_promise_then(Eina_Promise* promise, Eina_Promise_Cb callback,
541 Eina_Promise_Error_Cb error_cb, void* data)
542{
543 promise->then(promise, callback, error_cb, data);
544}
545
546EAPI void
547eina_promise_owner_value_set(Eina_Promise_Owner* promise, void* value, Eina_Promise_Free_Cb free)
548{
549 promise->value_set(promise, value, free);
550}
551
552EAPI void
553eina_promise_owner_error_set(Eina_Promise_Owner* promise, Eina_Error error)
554{
555 promise->error_set(promise, error);
556}
557
558EAPI void *
559eina_promise_value_get(Eina_Promise const* promise)
560{
561 return promise->value_get(promise);
562}
563
564EAPI Eina_Error
565eina_promise_error_get(Eina_Promise const* promise)
566{
567 return promise->error_get(promise);
568}
569
570EAPI Eina_Bool
571eina_promise_pending_is(Eina_Promise const* promise)
572{
573 return promise->pending_is(promise);
574}
575
576EAPI void
577eina_promise_progress_cb_add(Eina_Promise* promise, Eina_Promise_Progress_Cb callback, void* data)
578{
579 promise->progress_cb_add(promise, callback, data);
580}
581
582EAPI void
583eina_promise_cancel(Eina_Promise* promise)
584{
585 promise->cancel(promise);
586}
587
588EAPI void
589eina_promise_ref(Eina_Promise* promise)
590{
591 promise->ref(promise);
592}
593
594EAPI void
595eina_promise_unref(Eina_Promise* promise)
596{
597 promise->unref(promise);
598}
599
600EAPI void *
601eina_promise_owner_buffer_get(Eina_Promise_Owner* promise)
602{
603 return promise->buffer_get(promise);
604}
605
606EAPI void *
607eina_promise_buffer_get(Eina_Promise* promise)
608{
609 return promise->buffer_get(promise);
610}
611
612EAPI size_t
613eina_promise_value_size_get(Eina_Promise const* promise)
614{
615 return promise->value_size_get(promise);
616}
617
618EAPI Eina_Promise *
619eina_promise_owner_promise_get(Eina_Promise_Owner* promise)
620{
621 return promise->promise_get(promise);
622}
623
624EAPI Eina_Bool
625eina_promise_owner_pending_is(Eina_Promise_Owner const* promise)
626{
627 return promise->pending_is(promise);
628}
629
630EAPI Eina_Bool
631eina_promise_owner_cancelled_is(Eina_Promise_Owner const* promise)
632{
633 return promise->cancelled_is(promise);
634}
635
636EAPI void
637eina_promise_owner_progress(Eina_Promise_Owner const* promise, void* progress)
638{
639 promise->progress(promise, progress);
640}